Working with Flags
Flags let you work out once what kind of page a user is looking at, and then reuse that decisionanywhere in your skin. A flag is a named register that holds one of two states:
- True - The flag is set
- False - The flag is not set
A flag you have never set is treated as false, so you only need to set the flags you care about.
Every ThemeBuilder macro can be switched on or off by a flag, and flags set at the threeuser-facing levels (request, session and user) also add flag-FLAGNAME classes to the <body>element so you can drive CSS from them.
See the following for more information:
How to Set a Flag
Use the Set Flag macro to set a flag, and the same macro with state=false to unset it. Forexample, the following markup sets a flag called foo and then unsets it:
Storage Format
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">foo</ac:parameter>
</ac:macro>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">foo</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>Wiki Markup
{set-flag:foo}
{set-flag:foo|state=false}You can set flags in a wiki page or in a theme panel. In most cases, you'll want to conditionally set a flag based on some other criteria. For example:
Storage Format
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="title">Forum</ac:parameter>
<ac:parameter ac:name="recurse">true</ac:parameter>
<ac:rich-text-body>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">in-the-forum</ac:parameter>
</ac:macro>
</ac:rich-text-body>
</ac:macro>Wiki Markup
{panel-show:title=Forum|recurse=true}
{set-flag:in-the-forum}
{panel-show}The markup above would set the in-the-forum flag only when the user is looking at a page called Forum or any of its child pages due to the recurse=true.
The macros most commonly used to set and unset flags in this manner are as follows:
- Panel Show macro
- Panel Hide macro
These show/hide macros will show or hide their contents based on the parameters you set, allowing you to determine when the Set Flag macro is triggered.
How to Unset a Flag
There are two ways to turn a flag off, and the difference matters once you start using more than one storage target.
Set it to false
state=false stores an explicit false against the target:
Storage Format
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">in-the-forum</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>Wiki Markup
{set-flag:in-the-forum|state=false}You can also completely remove a flag by setting the state to remove:
Remove it Completely
state=remove deletes the stored value instead of storing false:
Storage Format
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">in-the-forum</ac:parameter>
<ac:parameter ac:name="state">remove</ac:parameter>
</ac:macro>Wiki Markup
false is not the same as absent. When a flag is read, ThemeBuilder stops at the firsttarget that holds a value — and a stored false counts as a value. So state=false also masksany value held by a longer-lived target, while state=remove lets that longer-lived value showthrough again. See Flag Target Priority.
There is no clear parameter. If you have clear=true in older markup, it is silently ignored andthe flag is set to true — the opposite of what you intended. Replace it with state=false orstate=remove.
What to do with Flags
You can use flags to toggle the rendering of other macros and wiki markup. Using the in-the-forum flag above, we can now show some extra content in a theme sidebar when that flag is set to the following:
Storage Format
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="flag">in-the-forum,foo</ac:parameter>
<ac:rich-text-body>
this stuff will only be shown when either the 'in-the-forum' and/or 'foo' flag has been set
</ac:rich-text-body>
</ac:macro>Wiki Markup
{panel-show:flag=in-the-forum,foo}
this stuff will only be shown when either the 'in-the-forum' and/or 'foo' flag has been set
{panel-show}Or we could show some content when the flag is not set, like the markup as follows:
Storage Format
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="notflag">in-the-forum,foo</ac:parameter>
<ac:rich-text-body>
this stuff will only be shown if neither the 'in-the-forum' and 'foo' flags have been set
(in other words, if the 'in-the-forum' or 'foo' flags are set, ths won't be shown)
</ac:rich-text-body>
</ac:macro>
<ac:macro ac:name="panel-hide">
<ac:parameter ac:name="flag">in-the-forum,foo</ac:parameter>
<ac:rich-text-body>
this is does the same as the panel-show markup shown above, we've changed 'notflag' to 'flag'
so it gets hidden when either of the flags are set
</ac:rich-text-body>
</ac:macro>Wiki Markup
{panel-show:notflag=in-the-forum,foo}
this stuff will only be shown if neither the 'in-the-forum' and 'foo' flags have been set
(in other words, if the 'in-the-forum' or 'foo' flags are set, ths won't be shown)
{panel-show}
{panel-hide:flag=in-the-forum,foo}
this is does the same as the panel-show markup shown above, we've changed 'notflag' to 'flag'
so it gets hidden when either of the flags are set
{panel-hide}Macros that Support Flags
All ThemeBuilder macros support flags.
The flag parameters are as follows:
- flag – defines which flags must be set for the macro to be processed
- notflag – determines which flags must not be set for the macro to be processed.
If you supply both, both conditions must pass.
Two macros honour flag and notflag at render time but do not offer them in the macro browser - Render Storage Format and Panel Debug. Add the parameters in storage format or wikimarkup if you need them there.
Writing flag lists
- Flag names are case sensitive:
showEditandshoweditare two different flags. - Separate names with a comma, optionally followed by spaces:
flag=a,bandflag=a, bboth work. - Do not put a space before a comma.
flag=a , blooks for a flag calleda(with atrailing space), which will never match.
When to Use Flags
Flags become useful in more complex theme customizations because they allow you to separate a lot of the logic that determines what should be displayed to the end-user. We'll cover some use cases below.
Setting defaults
There are many cases where you'll want to set a default state and override it with a known state based on various criteria. If none of the known states are found, you'll be left with the default state.
A simple example is determining whether a user is logged in or not, and whether they are a member of staff:
Storage Format
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">anonymous-user</ac:parameter>
</ac:macro>
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="group">confluence-users</ac:parameter>
<ac:rich-text-body>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">anonymous-user</ac:parameter>
<ac:parameter ac:name="clear">true</ac:parameter>
</ac:macro>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">registered-user</ac:parameter>
</ac:macro>
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="group">staff-group</ac:parameter>
<ac:rich-text-body>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">staff-user</ac:parameter>
</ac:macro>
</ac:rich-text-body>
</ac:macro>
</ac:rich-text-body>
</ac:macro>Wiki Markup
{set-flag:anonymous-user}
{panel-show:group=confluence-users}
{set-flag:anonymous-user|clear=true}
{set-flag:registered-user}
{panel-show:group=staff-group}
{set-flag:staff-user}
{panel-show}
{panel-show}The result is explained below:
- If the user is not logged in, only the
anonymous-userflag will be set (the default state). - If the user is logged in, the
anonymous-userflag is set to false, and theregistered-userflag will be set - If the user is logged in and is also a member of your staff-group, the
staff-userflag will be set in addition to theregistered-userflag.
In the example above, a user could set a staff-user flag from a wiki page. You could override any flags set by users by defining some more defaults at the start of your flag logic. For an example, look at the markup included here:
Storage Format
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">registered-user</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">staff-user</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>Wiki Markup
{set-flag:registered-user|state=false}
{set-flag:staff-user|state=false}Improved Performance
Often the same state is needed in several places in a layout. Although Builder and Confluence cacheheavily, repeatedly evaluating a complex condition costs time and makes your panels hard to read.
A common example is deciding whether to show an edit button and other page-editing links:
- Don't show edit features on top-level pages or the home page
- Only show edit features to logged-in users, regardless of anonymous permissions
- Only show edit features in the Documentation area of a space to members of staff
That's several checks, repeated in several panels — an ideal use case for flags. Building on theflags set above:
Storage Format
<ac:macro ac:name="panel-hide">
<ac:parameter ac:name="page"><at:var at:name="parent," />home</ac:parameter>
<ac:rich-text-body>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">top-level-page</ac:parameter>
</ac:macro>
</ac:rich-text-body>
</ac:macro>
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="flag">registered-user</ac:parameter>
<ac:parameter ac:name="notflag">top-level-page</ac:parameter>
<ac:rich-text-body>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">show-edit</ac:parameter>
</ac:macro>
</ac:rich-text-body>
</ac:macro>
<ac:macro ac:name="panel-show">
<ac:parameter ac:name="page">Documentation</ac:parameter>
<ac:parameter ac:name="recurse">true</ac:parameter>
<ac:parameter ac:name="notflag">staff-user</ac:parameter>
<ac:rich-text-body>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">show-edit</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>
</ac:rich-text-body>
</ac:macro>Wiki Markup
{panel-hide:page=<at:var at:name="parent," />home}
{set-flag:top-level-page}
{panel-hide}
{panel-show:flag=registered-user|notflag=top-level-page}
{set-flag:show-edit}
{panel-show}
{panel-show:page=Documentation|recurse=true|notflag=staff-user}
{set-flag:show-edit|state=false}
{panel-show}flag=show-edit, without repeating theunderlying checks. Where to Define Flags
We recommend the flagLogic panel in the ThemeBuilder Skin Editor, because:
- Decluttering — it keeps conditional markup out of your layout panels
- Maintainability — the logic lives in one place
- Ordering — ThemeBuilder renders the flagLogic panel before the main panel, so every flagyou set there is available to the rest of the skin
Flags set anywhere else — a layout panel, or a wiki page — only affect markup rendered after thepoint at which they are set.
CSS Customization with Flags
When a flag is set, ThemeBuilder adds a flag-FLAGNAME class to the page's <body> element:
<body class="flag-in-the-forum" ...The class is prefixed with flag-, so to change the colour of first-level headings inside theforum you'd put this in the CSS tab:
.flag-in-the-forum h1 {
color: #0f0;
}The classes are added to the <body> element on both standard pages and the login screen.
Only request, session and user flags produce CSS classes
This is the one place where flags do not behave uniformly across targets. Only flags stored against request, session and user appear in the <body> class list. Flags stored against page, space or global work perfectly well in flag and notflag conditions, but theynever emit a class, so CSS written against them will never match.
A flag whose value is false also produces no class.
| Request | Session | User | Page | flag=xmatches? | flag-xclass on body? |
|---|---|---|---|---|---|
| - | - | true | - | yes | yes |
| - | true | - | - | yes | yes |
| - | - | - | true | yes | no |
| false | - | true | - | no | no |
| true | - | - | false | yes | yes |
If you need CSS to react to a page, space or global flag, mirror it into a request flag in your flagLogic panel. Reading a flag looks at every target, so re-setting the same name at request scope is enough:
{panel-show:flag=sitewide-banner}
{set-flag:sitewide-banner}
{panel-show}The condition matches the global flag; the inner Set Flag stores it against the request, which is what produces flag-sitewide-banner on the <body> element.
Flag Targets
Flags may be targeted against different types of scenarios; for instance, a user can set a flag against themselves. This is useful when implementing user preferences, such as whether to show a sidebar or not.
| Target/Type | Level of Persistence | Where it is stored | Permission to set |
|---|---|---|---|
| Request | Not persisted; reset on every page load | Request data | None - always allowed |
| Session | Persisted for the browser session; reset when the user closes and reopens their browser | HTTP session | None - always allowed |
| User | Persisted against the user; available whenever that user is logged in | Personal information content properties | User must be logged in |
| Page | Persisted against the current page; present when that page is viewed by any user who can see it | Content properties on the page or blog post | User must have edit permission on the page or blog post |
| Space | Persisted against the current space; present on every page in the space | Content properties on the space description | User must have space administer permission |
| Global | Persisted everywhere, for everyone | Bandana (Builder storage) | User must be a Confluence administrator |
request is the default when you omit type on the Set Flag macro.
If a user does not have permission to set a flag, the write is silently ignored — no error is shownand no flag is stored.
Flag Target Priority
flag and notflag never take a target. Reading a flag always checks all six targets in this order and uses the first value it finds, whether that value is true or false:
- Request
- Session
- User
- Page
- Space
- Global
Because a stored false ends the search, this ordering is what makes thedefaults pattern work: a shorter-lived target always wins over a longer-livedone, so a skin can override a stored user preference for a single request.
Setting a flag clears shorter-lived copies
Setting a flag does more than write one value. Before storing the new value, ThemeBuilder removesthat flag from the chosen target and every target above it in the priority list:
typeused to set | Targets cleared first |
|---|---|
| request | request |
| session | request, session |
| user | request, session, user |
| page | request, session, user, page |
| space | request, session, user, page, space |
| global | all six |
This is deliberate: without it, a leftover request or session value would out-rank the value youjust stored and your write would appear to do nothing. It does mean that{set-flag:sidebar|type=user|state=true} discards any request- or session-scoped sidebar valuefor the current page load. Targets below the one you write to are left untouched.
Clearing obeys permissions in the same way as setting: targets you are not allowed to write areskipped silently.
Removing a flag
state=remove uses the same cascade. {set-flag:x|state=remove|type=user} removes x from therequest, session and user targets. Using type=global, or omitting type entirely, removes theflag from all six targets — the simplest way to completely reset a flag.
Flags are not a security mechanism
Any user who can edit a page can set request, session and user flags from that page, and users withedit permission on a page can set page flags that then apply to everyone who views it. Neveruse a flag to hide content that a user is not permitted to see — use Confluence permissions andrestrictions for that.
You can neutralise flags set by end users by re-asserting your own defaults at the start of yourflagLogic panel. Because a stored false stops the lookup, state=false at request scope masksany value a user may have stored at any longer-lived target:
Storage Fromat
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">registered-user</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>
<ac:macro ac:name="set-flag">
<ac:parameter ac:name="name">staff-user</ac:parameter>
<ac:parameter ac:name="state">false</ac:parameter>
</ac:macro>Wiki Markup
{set-flag:registered-user|state=false}
{set-flag:staff-user|state=false}Setting Flags Based on User Interaction
Sometimes it's useful to let users set a flag by clicking something — a sidebar toggle, for example. Use the Set Flag Link macro, which renders a link that sets the flag and then returns the user to the page they were on.
Read-only mode
In Confluence Data Center read-only mode, both the Set Flag and Set Flag Link macros render nothing and set no flags, since several targets would otherwise write to the database. Reading is unaffected, so flags already stored against the user, page, space or global targets still apply — but nothing new is set, which means a skin that computes its layout in flagLogic falls back to its unflagged state while read-only mode is active.
Checking which flags are set
Use more than one of these; no single view lists every target.
<body>class list. Request, session and user flags that are set appear asflag-NAME. Astoredfalseproduces no class. Page, space and global flags never appear here.- Panel Debug. Drop
{panel-debug}into a skin panel.Request flags show under Builder Data asadaptavist.builder.flags.NAME. Addsession=truetoinclude session flags. User, page, space and global flags are not in this dump. Panel Debug isnot in the macro browser; see that page for how to insert it. - A temporary Panel Show. The only way to confirm a page, space or global flag from markup:
{panel-show:flag=my-flag}my-flag is set{panel-show}- Login-screen debug comment. Turning on skin debugging, or adding
?builderdebug=trueto alogin URL, writes the rendered flagLogic panel into an HTML comment near the top of the source.This applies to the login decorator only.