CMS editor
The settings of the browser editor (`[cms]`) — its path and workflow, the git repository, the sign-in, roles, field hints — and the Worker's secrets.
[cms] adds the browser editor described in Editing in the browser to a build. Put it in the table of the environment that deploys it, [environments.production.cms] (see environments), so that other builds have no editor; the examples below write it at the root, as [cms]. Keys are case-insensitive, like every setting.
[cms]
path = "admin"
workflow = "review"
media = "static/images/uploads"
maxUpload = 10
[cms.git]
host = "github"
repo = "you/site"
branch = "main"
[cms.login]
provider = ["github", "google"]
[cms.roles.writer]
edit = ["content/blog/**"]
[cms.roles.owner]
edit = ["**"]
publish = true
[cms.fields.summary]
label = "Short summary"
help = "Shown in lists and in search results."Settings#
path- The editor’s directory under the site,
adminby default: the editor is/admin/, its API/admin/api/. The editor replaces a page of the site at that path (with a warning). title- The editor’s title. Default: the site’s
title. workflowreview(default): saves go to a draft per page (a branch, with a pull request labelledfugo-cms), and roles withpublishpublish drafts.direct: every save is a commit to the branch.media- A directory for uploads to pages that are not bundles, inside a static directory,
assets/or the content directory (static/images/uploads). Front matter names its files by their URL path for a static directory (/images/uploads/photo.jpg), else by their path below the directory. Default: none (uploads go into page bundles only). maxUpload- The largest file a save may write, in MiB, 1 to 25. Default: 10.
html- Let roles write
.htmland.htmcontent files. They are raw HTML pages, whose scripts run on the editor’s own address (see security notes). Default: false.
Git#
[cms.git] is the repository the editor commits to.
hostgithub, the only one for now.repoowner/name(required).branch- The branch the site is built from, which the editor publishes to. Default:
main. Withfugo server, which answers the editor’s API from the local repository, the editor uses the branch checked out instead (try it locally). dir- The project’s directory in the repository, when it is not the repository’s root (
site). Default: found from the.gitdirectory above the project; without one, the build warns and takes the root.
Login#
[cms.login] says how people sign in (see sign in).
providergithub,google, or both (["github", "google"], in the order of the editor’s buttons): the Worker sends people to sign in with their account, and keeps them signed in for seven days with a cookie it signs. Each provider needs an OAuth app of yours, and the secrets below.Or
cloudflare-access: Cloudflare Access signs people in (with Google, GitHub, Microsoft, a one-time PIN, …) before they reach the editor, and the Worker checks the token it adds to every request; it needsteamandaud. Withoutprovider,teamandaudmeancloudflare-access.team- With
cloudflare-access: the Access team, its name (your-team) or domain (your-team.cloudflareaccess.com). aud- With
cloudflare-access: the audience (AUD) tag of the Access application that protects the editor, or a list of them.
Roles#
[cms.roles.<name>] is what one role may do; the CMS_USERS secret gives people roles. At least one role is required.
edit- Globs of the files the role may create, change and delete, relative to the project directory:
["content/blog/**", "content/**/*.th.md"].*matches any part of a name,**any part of a path,?one character,{a,b}either alternative,[abc]one of the characters. The editor writes only content, data, i18n and the media directory, never the configuration, whatever the globs say; see who may change what. publish- Whether the role may publish drafts (and discard anyone’s). Default: false.
Fields#
The build gives the editor a field for every front matter key the content uses, and for every key inside its tables, so most sites need no settings:
- a label from the key (
image_preview: Image preview); - a widget from the kind of its values (dates, numbers, yes/no, lists, tables) and from its name (an image picker for
image,cover,photo…; a text box fordescription,summary…). A key whose pages hold values of different kinds shows each value as it is; - the values pages share, which the editor suggests as you type (a brand, a type, an author; not the values that name one page, like
titleorslug). Taxonomy keys suggest the terms the site uses; - the keys of a table across all pages: a page whose
nutritiontable lacksfatshows it empty in the table; - whether a key’s value is one for every language: when nine in ten of the pages that have the key in several languages, at least, give it the same value in each (an image, a rating, a date; the order of a table’s keys and empty values do not count), an edit in one language goes to the page’s other language files too (marked all languages).
title,description,summary,keywordsanddraftstay each language’s own; - the value new pages of a section start with: the one every page of the section gives the key (three pages at least, half of the section’s pages at least), such as a
typeor alayout.
[cms.fields.<key>] changes a field (the key matched ignoring case; a key inside a table by its path, "nutrition.fat.total", the keys of a list’s tables below the list’s key), setting by setting: what you leave out stays as the build works it out. A key that no page has yet, with settings, shows as an empty field on the pages of every section (inside a table, in the table). fugo cms fields prints every field as the build gives it to the editor, as these settings: copy the ones you want to change.
[cms.fields.summary]
help = "Shown in lists and in search results." # the label and the text box stay
[cms.fields.flavours]
label = "Flavours"
options = ["sweet", "salty", "spicy"]
multiple = true # a checkbox each; the value is a list
[cms.fields.rating]
min = 0
max = 5
required = true
[cms.fields.nutrition]
collapsed = true
summary = "{calories} kcal, {fat.total} g fat"
[cms.fields."nutrition.fat.total"]
label = "Total fat (g)"
[cms.fields.author]
default = "The kitchen"
[cms.fields.sku]
widget = "hidden"A key with *, ?, […] or {…,…} is a pattern of keys, whose settings go to every key it matches: * is any part of one key, ** any part of the path, across keys ("scores.*" matches scores.taste, not scores.taste.note; "nutrition.**.percentage" matches nutrition.fat.saturated.percentage). Where several match a key, the longer pattern’s settings go over the shorter’s, and the key’s own over them, setting by setting. A pattern names no key of its own: it changes the keys of the content and those other settings name.
[cms.fields."scores.*"] # every score: scores.taste, scores.crunch…
min = 0
max = 5
step = 1
[cms.fields."scores.taste"]
label = "Taste" # and 0 to 5, from the pattern
[cms.fields."nutrition.**.percentage"]
label = "% Daily Value"label- The field’s label. Default: the key, in words.
help- A line of help under the field.
widgettext,textarea,number,boolean,date,select(withoptions),list,image(a choice of the page’s files and the uploads), orhidden(not shown; the value is kept).options- The choices of a
select(a dropdown). Options without awidgetmake a select. multiple- A select of any number of its
options, a checkbox each; the value is a list. A select of a key whose pages hold lists is one already. Needsoptions. default- The key’s value on a new page, over the value its section’s pages share; a page without the key offers it under its empty field (Use). With
widget = "hidden", a value new pages get without showing it. min,max,step- The bounds of a number and the step between numbers. The editor does not save a number out of bounds.
required- The key needs a value: the editor does not save a page without one, on the pages of a section whose pages use the key (or that have it). A key inside a table needs one where the page has the table it is in.
sharedtrue: one value for every language, an edit goes to each language’s file of the page;false: each language keeps its own, where the build would share it.collapsed- A table, or each table of a list of tables, shown closed with a summary of its values.
summary- The summary of a closed table: its values in place of
{key}({fat.total}for a key of a table inside it). Default: its first three values.
Secrets#
The Worker reads these from its secrets (npx wrangler secret put NAME), never from the repository:
CMS_USERS- JSON: emails, or
@domainfor an email domain, to the roles they have:{"ann@gmail.com": ["writer"], "@example.org": ["writer"]}(required). CMS_SESSION_KEY- With
githuborgoogle: a random key of 32 characters or more, which signs the session cookies (openssl rand -base64 32). Changing it signs everyone out. CMS_GITHUB_CLIENT_ID,CMS_GITHUB_CLIENT_SECRET- With
github: the client ID and a client secret of the GitHub OAuth app people sign in with (not the bot’s credential below). CMS_GOOGLE_CLIENT_ID,CMS_GOOGLE_CLIENT_SECRET- With
google: the client ID and the client secret of the Google OAuth client people sign in with. CMS_GITHUB_APP_ID,CMS_GITHUB_APP_KEY- A GitHub App’s ID and private key (PEM), the app installed on the repository with Contents: Read and write, Pull requests: Read and write for the drafts’ pull requests, and Deployments: Read for the drafts’ builds.
CMS_GITHUB_TOKEN- Instead of an app: a fine-grained token for the repository with Contents: Read and write, Pull requests: Read and write and Deployments: Read.
CMS_DEV_USER- For
wrangler devonly (in.dev.vars): the email that requests tolocalhostsign in as, without signing in. Deployed Workers ignore it.