Skip to content

Configuration

The reference for every option. If you have not set the toolbar up yet, start with just for me; if you are rolling it out, start with for a team or org.

Everything the toolbar does follows from two things, and both come out of your configuration.

The hostname decides the environment and the side. A host listed under authors makes the page an author page and names the environment. A host listed under a site’s public section makes it a live page and names both the environment and the site. A host in neither list gets nothing at all: the toolbar does not render.

The path decides the screen. region-map.yaml matches the path to a sub-context: the page editor, the “view as published” preview, the sites console, an asset in the DAM. Links and messages are grouped by LocationContext/SubContext, so a rule written under author/pageEditor only ever appears in the page editor. The defaults recognise the standard AEM screens, so you only touch region-map.yaml if your author UI has been heavily customised.

authors:
prod: [author.acme.com]
stage: [author-stage.acme.com]
dev: [author-dev.acme.com]
local: [http://localhost:4502]

The keys are yours to name. They become the environment names the badge shows, and the order they appear in decides the default colours.

A host entry carries an origin, not just a hostname.

  • author.acme.com implies https, and matches whatever scheme and port the page is actually on.
  • http://localhost:4502 matches that origin exactly. A full origin is required for http or a non-standard port.
  • localhost:4502 matches nothing. A port with no scheme is ambiguous, so it is rejected when you author it and ignored when it is loaded from a stored configuration.
sites:
acme:
stripPath: /en
public:
prod: [www.acme.com, acme.com]
stage: [stage.acme.com]
local: [http://localhost:3000]

The site key must be the folder name under /content in AEM. It is how the toolbar works out which public domain an author page belongs to.

List every public domain a site answers on. Each one becomes its own View Live row labelled with the host it goes to, so an editor picks the one they meant rather than finding out afterwards.

stripPath is the path segment that exists in the author path and not in the public one.

Author path Public URL stripPath
/content/acme/en/products/kettles www.acme.com/products/kettles /en
/content/acme/us/products/kettles www.acme.com/products/kettles /us
/content/acme/products/kettles www.acme.com/products/kettles Leave it out

Live URLs are built without a .html extension. If your publish tier only answers the .html form, worked example 2 has the one transform that puts it back.

For public URLs that do not match the author path beyond a stripped prefix:

sites:
acme:
stripPath: /en
pathTransforms:
- authorPattern: "^/catalogue"
liveReplacement: "/shop"

Only the first transform whose authorPattern matches is applied. So a site cannot list a rule that rewrites the path alongside the catch-all rule that appends .html: a path the first rule matches never reaches the second, and Edit Page from a live page can then resolve to a node that does not exist. A site that needs both has to express them in a single transform.

A site can also carry transforms for one environment alone, under pathTransformsPerEnvironment. They are tried after the shared pathTransforms, under the same first-match-wins rule.

sites:
acme:
pathTransforms:
- authorPattern: "^/catalogue"
liveReplacement: "/shop"
pathTransformsPerEnvironment:
stage:
- authorPattern: "^/drafts"
liveReplacement: "/preview"

Both directions are built from the one rule, and this is what the return trip does with it. Going out, authorPattern is a real regular expression and liveReplacement is what its match becomes. Coming back, the toolbar turns that around: it matches the live path against the liveReplacement, reads the capture groups back out of it, and writes them into the literal text of the authorPattern. So ^/catalogue/(.+)$ to /shop/$1 maps /catalogue/kettles out to /shop/kettles and back again.

Seven things the return trip will not do, each of which leaves the live path as it is rather than guessing. It will not reverse an authorPattern that does not spell out the text it matched, such as one with alternation or a character class it has to match, or one whose capture the liveReplacement never puts back. It will not reverse a pattern that repeats a part, such as the /+ in ^/blog/+(.+)$ or the group in ^/(x)+y/(.+)$, because nothing in the live path says how many times it matched. It will not reverse a pattern with an optional part in the middle, such as (?:draft-)? before more of the path, because /articles/kettles and /draft-articles/kettles both publish to the same live path and nothing in it says which one the page came from. An optional part at the end of the pattern still reverses when the text it drops starts a new path segment, such as (?:/.*)?, or is a rendering extension, such as (?:\.html)?. The first answers the ancestor page, which every page under the rule publishes to, and the second answers the page itself, because an AEM path carries no extension of its own. An optional part at the end whose text sits inside the last segment’s own name, such as the [0-9]? in ^/x[0-9]?$, is not reversed either, because /x5 would come back as /x, a different page that may well exist and shows nothing to say it is the wrong one. It will not rewrite a live path that only shares a prefix with a mapped one, so ^/catalogue to /shop leaves /shopping-list alone. That prefix check cannot be made when the liveReplacement ends in a capture group, so write a prefix swap as ^/catalogue to /shop rather than as ^/catalogue(.*)$ to /shop$1, which does rewrite /shopping-list to /catalogueping-list. It will not accept a reversal that does not survive the trip back out, so a rule an earlier transform would have matched first is discarded rather than trusted. It will not reverse a rule carrying more than three capture references, because the pattern it would match the live path against is that many unbounded captures side by side and matching one gets expensive on a long path. That cap is read off every rule in the list, because each answer is checked by running the whole list back out again, so one rule with more than three capture groups stops the site’s other rules reversing too. One or two captures is the normal shape; a rule that needs more is better written as several transforms, or as a prefix swap that captures nothing. It will not reverse a rule that repeats a group which can itself repeat, such as the (a+)+ in ^/(a+)+b$, because a path that does not match one gives the regular expression engine so many ways to try that the toolbar would stop for tens of seconds. That rule is skipped on its own rather than costing the list, so the site’s other rules still reverse. It is the only rule on this page that the toolbar skips going out as well: the live URL is built without it, so View Live points somewhere wrong rather than nowhere. Write the repetition once, as ^/(a+)b$, or move it inside a capture the rule does not itself repeat.

Whatever you write, open a live page and press Edit Page before you hand the configuration to anyone.

environments:
prod:
color: "#D32F2F"
stage:
color: "#FF8F00"
dev:
color: "#388E3C"
local:
color: "#1976D2"

Optional. Leave the section out and colours are assigned in the order the environments appear under authors: red, orange, green, blue, purple, gold, teal. Listing production first therefore makes it red for free.

A link, message or condition can use a property of the AEM page itself with {page.<name>}. The properties are read off the page node on the author instance, so a convention your team already keeps in page properties becomes a toolbar row with no AEM code change.

author/pageEditor:
- label: "Event details fragment"
urlTemplate: "{instanceOrigin}/editor.html{page.eventDetailsContentFragmentPath}"
icon: "contentFragment"

The rules worth knowing before you write one:

  • A rule naming a property the page does not carry is not rendered at all, rather than rendered with a gap in it. That is what makes one rule safe to apply site-wide.
  • A property an editor has cleared counts as one the page does not carry.
  • A multi-value property resolves to its first value.
  • Property names carry their prefix, so cq:template is written {page.cq:template}.
  • The token works in a link’s urlTemplate, label and labelActive, in a message’s text, and inside an assertion’s values.
  • Properties are read after the toolbar is on screen, so a rule using one appears a moment after the rest.
  • These rules resolve on author pages only. One written under public/page will never appear.

In the options page, Links & Messages has a picker that lists the properties of the last author page you opened and shows what your label or URL resolves to on it.

Worked example 3 builds a whole configuration out of these, and the advanced recipes in the FAQ answer what usually comes next: limiting a link to one section of the site, combining several checks in one rule, and reading a value off the page into a message.

A link or a message can name the icon its row shows with icon. There are nine, and a key the extension does not recognise falls back to the default.

world, edit, dam, folder, info, properties, alert, contentFragment, link

Leave icon out and the row gets the one for its kind: link for a link, info for a message, alert for an error and dam for a toggle action. A toggle can also name an iconActive for its on state, the way it names a labelActive.

Each of the four files resolves in this order:

  1. A personal override, saved by the user in the YAML editors or built in Links & Messages.
  2. Your hosted package, if one is configured.
  3. The bundled defaults the extension ships with.

So individuals can add their own links and messages on top of a team configuration without breaking it. When a hosted package changes a file a user has overridden, they get a notice with a diff and choose.

A structurally invalid file is rejected outright and the loader falls back to the next tier, so a malformed rule can never reach the detector and match everything. The options page validates as you type and points at the failing line, and the browser console carries the rejection reason. Almost every YAML failure is indentation: spaces, never tabs.