Jahia provides flexible support for personalization of content delivery through the use of Jahia Marketing Factory.  This article will explore how we can take personalization one step further and build a mechanism for altering the display of content based on activities that a user engages in.  In this case we will track the number of page views for a given page and create a Quicklinks component that will display links for all the pages that have exceeded the configured number of user page views.

The solution comprises the following tasks: 1) Create a custom boolean page attribute “quicklinkEnabled” to indicate which pages we are interested in tracking.  2) Create a filter which will execute for each page view and increment a “pageVisited” counter for the current user if quicklinkEnabled is true for the current page.  3) Create a Quicklinks component which will display all the pages whose pageVisited counter is greater than a threshold value stored as an attribute of the Quicklinks display component.

Create a custom page attribute

The steps in this article assume that you have created a module in Jahia Studio.  For the article we will assume the module is called “myModule”.  For step 1, we create our custom page attribute which will be our boolean flag indicating that the current page is to be tracked for hit-counts.  We define our attribute in the module’s definitions.cnd file as follows:

<jdmix = 'http://www.jahia.org/jahiademo/mix/1.0'>

[jdmix:enableQuicklink] mixin
 extends = jnt:page
 - quicklinkEnabled (boolean)

Now we tell Jahia how to display our new property in our module properties file under: /src/main/resources/resources/myModule.properties

jdmix_enableQuicklink.quicklinkEnabled=Enable Tracking

Create a custom page filter

public class QuicklinkFilter extends AbstractFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(QuicklinkFilter.class);
    private JCRTemplate jcrTemplate;
    public String prepare(final RenderContext renderContext,
                          final Resource resource,
                          final RenderChain chain) throws Exception {
        if (renderContext.isLoggedIn()) {
            final JCRNodeWrapper page = resource.getNode();
            if (page.hasProperty("enabled")
                    && page.getProperty("enabled").getValue().getBoolean()) {
                        resource.getNode().getSession().getLocale(), new JCRCallback<Object>() {
                    public Object doInJCR(final JCRSessionWrapper jcrSessionWrapper) throws RepositoryException {
                        final JCRNodeWrapper userFolder = jcrSessionWrapper
                        final JCRNodeWrapper quicklinks;
                        if (userFolder.hasNode("quicklinks")) {
                            quicklinks = userFolder.getNode("quicklinks");
                        } else {
                            quicklinks = userFolder.addNode("quicklinks", "jdnt:quicklinksFolder");
                        final JCRNodeWrapper quicklink;
                        if (quicklinks.hasNode(page.getIdentifier())) {
                            quicklink = quicklinks.getNode(page.getIdentifier());
                        } else {
                            LOGGER.info("Quicklinks not found, create one");
                            quicklink = quicklinks.addNode(page.getIdentifier(), "jdnt:quicklink");
                            quicklink.setProperty("page", resource.getNode());
                        if (quicklink.hasProperty("score")) {
                            final JCRPropertyWrapper score = quicklink.getProperty("score");
                            LOGGER.info("Page score: {}", score.getValue().getLong());
                            score.setValue(score.getValue().getLong() + 1);
                        } else {
                            quicklink.setProperty("score", 1);
                        return null;
        return super.prepare(renderContext, resource, chain);
    public void setJcrTemplate(final JCRTemplate jcrTemplate) {
        this.jcrTemplate = jcrTemplate;

Wire up the Filter with Spring

Wire up the filter by adding the following to the Spring configuration file at /src/main/resources/META-INF/spring/myModule.xml

    <bean id="quicklinkFilter" class="org.jahia.modules.jahiademo.filter.QuicklinkFilter">
        <property name="priority" value="15"/>
        <property name="jcrTemplate" ref="jcrTemplate"/>
        <property name="applyOnConfigurations" value="page" />
        <property name="applyOnNodeTypes" value="jnt:page"/>
        <property name="applyOnTemplateTypes" value="html" />

Create the Quicklinks display component

Our display component will show a list of page links based on the number of times the user has visited the page.  The threshold value is stored as a custom attribute on the Quicklinks component.

Add the threshold custom attribute

The custom attribute is defined by adding the following to definitions.cnd:

[jdmix:jahiademo] > jmix:droppableContent, jmix:editorialContent mixin
[jdnt:quicklinks] > jnt:content, jdmix:jahiademo
- threshold (long)

Add a quicklink definition to hold the page reference and a hit counter

[jdnt:quicklink] > jnt:content
- page (weakreference)
- score (long)

Add a default view for Quicklink


<li><template:module node="${currentNode.properties.page.node}" view="link"/></li>

Add a default view for Quicklinks


<c:set var="threshold">
  <c:out value="${currentNode.properties.threshold.long}" default="3"/>
<jcr:sql var="quicklinks"
         sql="SELECT * FROM [jdnt:quicklink] AS quicklinks WHERE ISDESCENDANTNODE(['${currentUser.localPath}/quicklinks']) AND quicklinks.score >= ${threshold}"/>
<c:forEach var="quicklink" items="${quicklinks.nodes}">
  <template:module node="${quicklink}" view="default"/>


With this approach we can track and persist user activity metrics that could form the implementation basis for many types of personalization or gamification use cases.  With very little coding we created a general purpose activity based personalization component that could easily be extended to implement more complex requirements.  For additional information look for other articles in this Xtivia series highlighting implementation topics for Jahia.

Share This