Header Content Footer
Discover best selling sofas, Lounge chairs and get an extra 10% off using the code SEDONA10

Client contexts are often used in a website implementation in order to achieve a certain amount of personalization of the contents of a website.

In AEM a client context is provided by an out-of-the-box feature called ContextHub.

This framework enables for multiple functionalities such as targeting, segmentation, geolocalization, cart management, only to cite a few, and can be easily customized to provide more.

What is AEM ContextHub

The ContextHub is a JavaScript framework provided by AEM to manage the client context. This context is provided on the browser through a set of data stored in the first available of the following layers: local storage, session storage, cookie, window.name.

ContextHub comes with its own JQuery version (currently v3.2.1 for AEM 6.5) included in its kernel.

The framework can be configured and extended in AEM author instance while a set of APIs provides meanings for access, manipulation and persistence of data in the client context on the publish instance.

Stores

The ContextHub framework is organized in stores. A store logically includes:

  • An object persisted in the client context containing all data related to the specific logical entity (e.g., user profile, e-commerce cart, browser and device used, …);
  • APIs to manage that object;
  • A storeType: the store ID;
  • An initial configuration in json format.

A store is implemented by a clientlib, whose categories property has the following convention: contexthub.store.<storeType>

A clientlib which can be registered in a ContextHub configuration using its storeType is called a store candidate.
It follows that a ContextHub configuration only contains the subset of all store candidates explicitly associated with it.

A ContextHub configuration can be binded to a whole site or to a specific page. When a page is rendered, the ContextHub with its configuration is loaded by including a JS file.

The kernel of the framework creates a global object ContextHub in the DOM. ContextHub APIs give the possibility to access and use data in this object.
A store object can be retrieved using ContextHub.getStore(“<store name>”) function.

Given a store object, data within the store can be retrieved and updated using storeObject.getItem(“<data path>”) and the storeObject.setItem(“<data path>”, <data value>) functions.

Each store can define event handlers that are triggered when a specific event occurs. For instance: ContextHub.eventing.on(ContextHub.Constants.EVENT_STORE_UPDATED + “:cart”, this.refreshCart)

1.1. – Usage of contextHub data to render cart objects in page

According to the specific implementation of each store and availability in the browser, ContextHub information is persisted in the object ContextHubPersistence in the following positions:

  • localStorage;
  • sessionStorage;
  • cookie;
  • window.name;
  • JavaScript objects.

Custom store candidates

It is possible to create or extend stores, in order to achieve a custom behavior, by creating a new ContextHub clientlib.

The JavaScript code must define a function representing the store candidate which must inheriting from one of these base stores:

  • ContextHub.Store.PersistedStore
  • ContextHub.Store.SessionStore
  • ContextHub.Store.JSONPStore
  • ContextHub.Store.PersistedJSONPStore

All of these extends the base ContextHub.Store.Core store.
Customizations may vary from specifying a different backend servlet responsible to provide data to different events or data schema in the JS clientlib.

The clientlib must register the store using its store type and a priority in case multiple store candidates are registered with the same store type. Below is reported a pseudocode for a store candidate definition:

 (function($, window) {
     var defaultConfig = { ... };
     myStoreCandidate = function(name, config){
        this.config = $.extend(true, {}, defaultConfig, config);
        this.init(name, this.config);
         …
     };
    ContextHub.Utils.inheritance.inherit(myStoreCandidate,ContextHub.Store.PersistedStore);
    ContextHub.Utils.storeCandidates.registerStoreCandidate(myStoreCandidate, '', 0);
 }(ContextHubJQ, this)); 

ContextHub UI Modules

With the ContextHub, AEM provides a toolbar for editors to allow display and manipulation of client context data in order to achieve a reliable preview.
The toolbar is fully customizable and configurable.

1.2 – a ContextHub toolbar example

Content of the toolbar is defined by a UI module, which is a clientlib, whose categories property contains a value having this convention: contexthub.module.<moduleType>

Similarly to stores, only modules registered in the ContextHub configuration related to a site page are available in that page.

Modules use ContextHub APIs to populate the widget in the toolbar and to view and persist manipulated data into the client context.

1.3 – example of the cart UI module

Custom UI Modules

If out-of-the-box UI Modules don’t provide a solution for a specific requirement, a custom one can be created.

Simply define a new clientlib with a category named contexthub.module.<module type>.
Within the JavaScript, define a function that represents the module renderer, inheriting from ContextHub.UI.BaseModuleRenderer.

Eventually add or redefine some functions and manage module configuration extending the default one. Then register the module using its module type.

Below is reported a pseudocode for a module definition:

 (function($, window) {
     myModuleRenderer = function(){};
    ContextHub.Utils.inheritance.inherit(myModuleRenderer, ContextHub.UI.BaseModuleRenderer);
                myModuleRenderer.prototype.defaultConfig = { … };
    myModuleRenderer.prototype. onFullscreenClicked = function(module) {
       var config = $.extend({}, this.defaultConfig, module.config);
        …
     };
    myModuleRenderer.prototype.onListItemClicked = function(module, position, data) {…};
    myModuleRenderer.prototype. getPopoverContent = function(module, popoverVariant) {…};
    ContextHub.UI.ModuleRenderer('', new myModuleRenderer ());
 }(ContextHubJQ, this)); 

How to setup AEM ContextHub

Two steps are required in order to add the ContextHub framework to a project.

Developments

It is necessary to include the ContextHub JavaScript framework in all pages which requires it. By Adobe guidelines the inclusion has to occur in html head tag. Use the following HTL script:

<sly data-sly-resource=”${‘contexthub’ @ resourceType=’granite/contexthub/components/contexthub’}”/>

2.1 – Rendered output sample of context hub framework inclusion

Configurations

Another requirement is to create a ContextHub Configuration whichincludes stores, UI Modes and UI Modules.

ContextHub configurations are located in the repository at:

  • /libs/settings/cloudsettings/legacy: OOTB configuration ready to use;
  • /conf/global/settings/cloudsettings: location used to share configuration among different projects;
  • /conf/<tenant>/settings/cloudsettings: configuration related to a tenant.

The binding between the ContextHub configuration and the site pages is made using a page property.

2.2 – ContextHub binding

The binding is automatically inherited if not specifically set in the leaf pages, it is enough to set it in the homepage to be valid for a whole website.

The contextHub configuration has the following structure:

  1. First level: stores or UI modes;
  2. Second level: UI modules (only with UI modes as parent)
2.3 – ContextHub configuration

Configuration under a specific tenant can be made editorially from the author instance.

Targeted Content

One of the functionalities provided by the ContextHub is the possibility for a personalized user experience.

Users are divided into categories called segments.
A user belongs to a segment if its client context satisfies all traits included in the segment definition.
A user can belong to zero, one or more segments, called resolved segments.
The process regarding creation of segments is called segmentation.

An activity is a marketing initiative regarding a subset of segments. An AEM component in order to be targeted must be associated to an activity. Then a specific content can be binded to a specific segment.

When a user belonging to a segment navigates to a page with a targeted component, the content associated to the user segment is showed.

For targeting, it’s required to add segmentation ContextHub store candidate to the ContextHub configuration. Adding segmentation UI Module provides also to the ContextHub toolbar the specific widget

3.1 – ContextHub UI Module for segmentation

Targeting rendering

When rendering a page containing a targeted component, the default version of the targeted component is in page but initially hidden.

  1. The Segment Engine retrieves all resolved segments but only the one with higher boost is considered;
  2. For a targeted component with multiple activities, only the one with higher priority is considered;
  3. If there is a matching between the considered user segment and a segment related to the considered activity, the content related to that activity segment is retrieved with an ajax call and replaces the default one;
  4. The component is showed,

There is a drawback in targeting contents: the logic forces the component to be shown in a second time creating a delay in showing contents or even a layout shift. This is because the targeted content is injected client-side after the server-side rendering.

Getting the most out of AEM ContextHub

Out of the box, ContextHub offers a large selection of stores enabling for personalization and behaviors.
It useful to understand how such stores can be extended and how the ContextHub framework can be improved towards a better performance.

Customization

When setting up a store for a required functionality of our site, it is possible that a default store is already close to what we might need and that it is necessary to introduce some degree of customization. For instance, because we have a specific integration or require a customized data structure.

When setting up a new store it is possible to work from scratch or to customize one of the out of the box stores by bringing it under our project folder and applying the required changes.

A common change when setting up ContextHub for an ecommerce site is caused by the integration to the ecommerce backend, where we might need to use a different AEM service to provide cart data.

This can be achieved by changing the path variable inside the default config of the service itself, for instance:

var defaultConfig = {
        service: {
            jsonp: false,
            timeout: 10000,
            ttl: 0,
            path: '${resourcePath}.commercecart.json'
        },
        initialValues: {}
    };

This configuration will tell to the store to call a servlet enabled on selector “commercecart” and with a “json” extension.

The servlet will implement and provide all custom behaviors required such as an integration with a specific ecommerce outside of AEM or a different structure of the cart object to return to the client context.

Optimization

AEM ContextHub, enabled by documentation, comes with two limitations in terms of performance:

  • It is included in page in a blocking position in the <head> tag
  • The inclusion comes with a non cachable URL

The Context Hub kernel can have a big impact in terms of network traffic. It is included in each HTML page which requires it and it is a file of around 500 minified lines.
In a website page, it is a good practice to postpone all scripts to the end of page. In this scenario it is possible to place even the ContextHub inclusion to the bottom of the page, before all custom scripts of the site.

When including the framework, the default URL of the ContextHub has the following format:

  • /etc/cloudsettings.kernel.js/conf/{project}/settings/cloudsettings/default/contexthub

This path is not ideal since it is not cachable in an easy way by the dispatcher.
The kernel of the ContextHub is not mutable as a content and hence should be cachable in order to avoid requesting it to publish instances at every occurrence.

In order to obtain a cachable URL it is possible to make the following changes:

  • Override the component /libs/granite/contexthub/components/contexthub/contexthub.jsp in order to provide a different URL in page
  • Create a sling mapping which maps this new URL to the original one

For instance:

the contexthub.jsp file can be copied under /apps/granite/contexthub/components/contexthub and modfied as follows:

String path = contextHub.getKernelCodeURL(slingRequest);
path = "/etc/cloudsettings" + xssAPI.encodeForJSString(CQ_CONTEXT_PATH) + contextHub.getPath(slingRequest, false) + ".kernel.js" ;

A sling mapping can be created under /etc/map with the following regular expression:

jcr:primaryType="sling:Mapping"
sling:internalRedirect="/etc/cloudsettings.kernel.js/conf/$1/settings/cloudsettings/default/contexthub"
sling:match="^[^/]+/[^/]+/etc/cloudsettings/conf/([a-z0-9]+)/settings/cloudsettings/default/contexthub.kernel.js"/>

In order to handle caching even better, it could be a nice addition to provide a hash to the clientlib which changes at every release of the functionality.

Considerations

ContextHub is not meant to be simply an alternative to more complete e customizable personalization frameworks such as Adobe Target.
Its scope is to provide a client context framework, the usage of its contents can vary for a lot of purposes, for instance:

  • Handling cart in page
  • Handling user data in page
  • Provide contents based on geolocalization

One of those purposes, can be to provide meanings for a quick, editorial and out of the box personalization of contents.

It is a powerful framework but requires a bit of caution when used because it can easily expose sensitive user data on the browser session.

In time an upgrade in terms of storaging options, will be necessary. Currently available storage locations are being limited in terms of data that can be saved or even turned off (by policies or user settings).
New ways to handle client context, in particular when using a lot of data, are rising such as IndexedDB, it would be interesting to see them included in future ContextHub releases.

Bibliography

Adobe documentation about developing ContextHub
Adobe documentation about authoring ContextHub
Tutorial about enabling ContextHub in a project
Tutorial about audiences and activities management
Tutorial about segment engine
Mozilla IndexedDB API


Written by Aldo Caruso and Marco Pasini

Leave a Reply