Blog Post

Developer Blog
8 MIN READ

How We Built It: Access Signposting

SuzieH's avatar
SuzieH
Khoros Alumni (Retired)
4 years ago

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 coreNode.id handles the case when the current node in context is the Events category Category Page:

 

coreNode.hasAncestor("events") == true || coreNode.id == "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 coreNode.id == "events" || coreNode.hasAncestor("events") == true || coreNode.id == "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 , border, and box-shadow attributes.

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.

 

.public-logo-link {
    background: white;
    border-radius: 5px;
    border: 1px solid #00004B;
    bottom: 20px;
    box-shadow: 0px 0px 5px #00004B;
    color: #00004B;
    display: inline-block;
    font-size: 28px;
    font-weight: 300;
    left: 20px;
    line-height: 28px;
    padding: 10px;
    position: fixed;
    z-index: 1000;
    @include media(phone-and-down) {
   	 font-size: 20px;
    }
}

 

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 coreNode.id == "{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 coreNode.id == "{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 || coreNode.id == "{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/my.registration.page" 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>

</#if>

<#-- 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} 
</#if>

 

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/${user.id?c}/roles").roles.role as role>
      <#if role.name?? && ((role.name == "{role name")||(role.name == "{role name")||(role.name == "{role name}"))> <#-- add or remove the role.name === as needed
         <#assign user_has_role = true />
      </#if>
   </#list></#if>
<#if user_has_role > 
<#else>
</#if>

<#if user_has_role = true>

{component text goes here}

</#if>

 

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 || coreNode.id == "CommInternalDocs"  || coreNode.hasAncestor("CareInternalDocs") == true || coreNode.id == "CareInternalDocs"  || coreNode.hasAncestor("MktgInternalDocs") == true || coreNode.id == "MktgInternalDocs" >

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


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

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

</#if>

<#assign registered = "" />
<#if coreNode.hasAncestor("Developer") == true || coreNode.id == "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>
</#if>

<#assign archives = "" />
<#if coreNode.hasAncestor("Archive") == true || coreNode.id == "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>
</#if>

 

 

 

 

 

Updated 4 years ago
Version 11.0