Showing results for 
Show  only  | Search instead for 
Did you mean: 

How We Built It: Access Signposting

Khoros Staff

Atlas users have asked us how we built our customization that displays a fixed indicator (we call it a signpost) highlighting the access level of content on the current page being viewed. You can easily incorporate this customization into your community using the examples and source code in this article. Thanks to @Claudius  and @AndyK  for writing and sharing this customization!

We built a custom component with FreeMarker that determines the right signpost to use based on the node in context.

We use CSS to change the styling, text, and hover text based on the access level.


About the customization

The component logic

First, we mapped out which nodes match which access levels we want to highlight (Public, Private, Internal, Registered, and Archive).

We created a series of "if" statements using the OR comparison operator (||) to evaluate the node in context. If the page being viewed belongs to a node that matches the condition of the comparison logic, then we display the related access signpost. In some cases, we evaluate based on the current node's ancestors (container nodes in the Community Structure like a category or group hub). In other cases, we evaluate by node ID.

For example, we want the Events category and all of its child nodes to be Public. In the snippet below, the comparison using coreNode.hasAncestor handles the case where the node in context is a child node within the Events category. The comparison using handles the case when the current node in context is the Events category Category Page:






coreNode.hasAncestor("events") == true || == "events"






Let's go a step further.

This next snippet shows the logic we use to evaluate whether a node meets the "Public" access level criteria.

This logic displays our signpost on the Events Category Page, all subpages in the Events category, and our Contact Support board. We create and style the signpost using the public-logo-link class.

Note: We simplified the code in this snippet. See the full component source code at the end of this article.






<#assign public = "" />

<#-- Define the Public criteria: The node being viewed must be the "events" node, a child node within the "events" node, or the "contact-support" node. -->

<#if == "events" || coreNode.hasAncestor("events") == true || == "contact-support">

<#-- If the node in context meets the Public criteria, display the Public indicator with a Help icon that reveals a tooltip describing the access level on hover over. -->

<span class="public-logo-link" title="Visible to the public including non-registered users.">Public <span class="lia-img-icon-help lia-fa-icon lia-fa-help lia-fa" aria-label="Help Icon" role="img" id="display"></span></span>







The component styling

For each access level, we created a separate class. Each signpost class uses a different value for the color attribute.

Atlas uses a Responsive-based skin, so we put our custom CSS in _style.scss in Studio. See Skin architecture to learn more about custom SASS and Responsive skins.

Here is our CSS for our Public signpost.

Adding the component to a page

We chose to put our custom signpost component in the Page Wrapper. This ensures that the logic will be evaluated on every page. We add the component to the wrapper code using the component FreeMarker directive.

We put the following in the Page Header section of Studio > Community Style > {skin ID} > Wrapper > Page Header.






<@component id="custom.location-logo" />






Future improvements

Currently, we do not use any caching with this particular component. As a future enhancement, we plan to evaluate component caching and application caching to improve performance.

Make this your own

You can tailor this customization to fit your needs. Use the example code in this section to try this out in your stage environment.

Create your component

In Studio > Components, create a new component.

Use this sample code to get you started. Also, see our examples to display the signpost based on registration status or role.






<#--Create a new component and give it a unique name -- for example, custom.location-logo -->

<#-- Create a variable for the first type of signpost. The variable below is used with our Public signpost. For a different indicator, you might name the variable "private" or "registered" -->

<#assign public = "" />

<#-- Define your criteria for the first kind of signpost.

For each node on which to display the indicator, add a separate instance == "{node-id}". Where {node-id} is the ID of a board, category, or group hub. Separate each case to evaluate with an OR symbol (||).

To display all content within a node (including nested nodes), pass a container node ID (a category or group hub ID) to coreNode.hasAncestor. If you want the signpost to appear on the container node page, also include == "{container-node-id}"

Remove the coreNode.hasAncestor condition if that case doesn't apply to your version of the customization.

You may add as many conditions as needed. -->

<#if coreNode.hasAncestor("{board id}") == true || == "{board id}" || ... >

<#-- The class element must match a related style in your skin SCSS. 

Edit the title element to set the tooltip text. You can also set the text with a text key and the text.format method. If you do not want tooltip text, remove the "text" element. 

Replace Public with whatever text you would like to appear on the signpost or use a text key.

Replace the span element with an href if you would prefer to have the signpost link elsewhere, such as a registration page for content specific to registered users. (E.g., <a href="/t5/" target = "_blank" class="registered-logo-link" title="This content is requires an Atlas login ">Registered Users</a> -->

<span class="public-logo-link" title="This content is publicly available">Public</span>


<#-- To add code for a second indicator, copy the full snippet above, paste it here, and then customize it for your next indicator use case -->






Display the signpost to registered users only

If you would like this to show only to signed-in users, you can wrap the code in the component with this:






<#if user.registered == true> 
    {component code goes here} 






Display the signpost to users with a specific role

If you would like to limit it by role(s), you can wrap the code in the component with this:






<#assign user_has_role = false />
 <#if user.anonymous == false>
   <#list restadmin("/users/id/${}/roles").roles.role as role>
      <#if && (( == "{role name")||( == "{role name")||( == "{role name}"))> <#-- add or remove the === as needed
         <#assign user_has_role = true />
<#if user_has_role > 

<#if user_has_role = true>

{component text goes here}







Add your custom styling

Add the SCSS from the public-logo-link example in The component styling. Create a separate style for each signpost type you want to use. Be sure to reference the correct style in the class element in your component code.

Add the component to your Page Wrapper

Go to Studio > Community Style > {Skin} > Wrapper and place the following in the Page Header section.






<#-- This can go anywhere in the Page Header section -->
<#-- Use the same name as the component you created above. -->

<@component id="custom.location-logo" />






Customization source code






<#assign internal = "" />
<#if coreNode.hasAncestor("CommInternalDocs") == true || == "CommInternalDocs"  || coreNode.hasAncestor("CareInternalDocs") == true || == "CareInternalDocs"  || coreNode.hasAncestor("MktgInternalDocs") == true || == "MktgInternalDocs" >

    <a href="/t5/Internal-Community/ct-p/Employees" class="internal-logo-link">Internal</a>

<#assign private = "" />
<#elseif coreNode.hasAncestor("communitydoc") == true || == "communitydoc" || coreNode.hasAncestor("lithiumjx") == true || == "lithiumjx" || == "relnote" || == "community-product-coaching" || == "Lithium_Ideas" || coreNode.hasAncestor("marketingdocs") == true || == "marketingdocs" || == "marketingreleasenotes" || == "marketing-product-coaching" || == "spredfastblog" || == "marketingideas" || coreNode.hasAncestor("smm") == true || == "smm" || == "smmreleasenotes" || == "care-product-coaching" || == "reachresponse" || == "smm-ideas" || coreNode.hasAncestor("customer-hub") == true || == "customer-hub" || == "Education@tkb" || == "lisupport">

    <span class="private-logo-link" title="Visible to: Customers, Parters, and Khoros staff.">Private <span class="lia-img-icon-help lia-fa-icon lia-fa-help lia-fa" aria-label="Help Icon" role="img" id="display"></span>

<#assign public = "" />
<#if coreNode.hasAncestor("events") == true || == "events" || == "spredfastdiscussions" || == "socialmediablog" || == "jx-blog" || == "onlinecommunitiesblog" || == "technology" || == "productcoachinghome" || == "kudosawards2020" || == "Help@tkb" || == "lithiumblog" || == "Careers-Forum" || == "Help" || == "SupportInformation" || == "contact-support">

    <span class="public-logo-link" title="Visible to the public including non-registered users.">Public <span class="lia-img-icon-help lia-fa-icon lia-fa-help lia-fa" aria-label="Help Icon" role="img" id="display"></span>


<#assign registered = "" />
<#if coreNode.hasAncestor("Developer") == true || == "Developer">

    <span class="private-logo-link" title="Visible to: Registered Users, Customers, Partners, and Khoros staff.">Registered Users <span class="lia-img-icon-help lia-fa-icon lia-fa-help lia-fa" aria-label="Help Icon" role="img" id="display"></span>

<#assign archives = "" />
<#if coreNode.hasAncestor("Archive") == true || == "Archive">

    <span class="archives-logo-link" title="This content is archived">Archive <span class="lia-img-icon-help lia-fa-icon lia-fa-help lia-fa" aria-label="Help Icon" role="img" id="display"></span>







Honored Contributor

Sorry haven’t read the code yet. But reading above sounds like it’s hard coded against the community structure, so creating a new board would work provided the code is aware of its parent? But creating a new category or group would mean updating this code so it’s aware of the new node? Is that right? 

We’ve always wanted custom settings to be available along side the community node itself so in this case you’d put public, registered etc in the custom setting along side the node description and hey presto done! 

Khoros Staff

@cblown You're correct, and I wondered about that myself. @AndyK would you or our Atlas engineers care to comment?

Community Manager
Hey @cblown Yes that is correct, next time I have some time I am going to see if I can improve the set up so it can be something in the admin if possible, maybe using the settings list editor to maintain it. For now just wanted to get something out there and keep up with our recent practice of sharing the code where possible. Cheers, Andy
Trusted Contributor

@AndyK Tell me about this "settings list editor" -- is that a feature I have not discovered in Admin? Like a property key-value thing?

Community Manager

Hi @cjdinger 


Settings list editor can be enabled by Services or Support where they can expose individual settings that can then be managed via the admin for custom components.

Note that extensive custom settings work may require a Services engagement.

These can include things like custom banners if you need to set different ones at different levels, manually curated featured topics etc.