Showing Popular Items

An ADT consuming a CmsAssistant service

There was recently a requirement to display a bunch of web content items and then provide a category filter. The most convenient way to do this is as follows:

  • Use an instance of the Asset Publisher portlet and configure it to display the web content items of a specified structure
  • Use an instance of the Categories Navigation portlet and configure it to show the categories of a specific vocabulary

This worked perfectly well.

And then there was an additional requirement:

  • Show the top 3 most popular items. This requirement meant that we would show the 3 web content items that had the highest view counts.

Here’s how I solved it in a couple hours:

  1. Define a service with a method that fetches and returns AssetEntry references matching the given structure.
  2. Use the freemarker builtins to get the most popular items from that list.

Here’s my service method implementation:

public List<AssetEntry> getAssetEntriesByGroupIdAndStructureName(Long groupId, String structureName) throws PortalException {
    List<AssetEntry> assetEntries = new ArrayList<AssetEntry>();
    try {
      AssetEntryQuery assetEntryQuery = new AssetEntryQuery();
      assetEntryQuery.setClassName(JournalArticle.class.getName());
      List<DDMStructure> structures = DDMStructureLocalServiceUtil.getStructures(groupId);
      //find the FAQ structure
      long[] structureIds = new long[1];
      for (DDMStructure structure : structures) {
        if (structure.getName(Locale.getDefault()).equalsIgnoreCase(structureName)) {
          structureIds[0] = structure.getStructureId();
          break;
        }
      }
      if (structureIds[0] == 0) {
        throw new RuntimeException("Invalid structure name");
      } else {
        assetEntryQuery.setClassTypeIds(structureIds);
        assetEntries = AssetEntryServiceUtil.getEntries(assetEntryQuery); 
      }     
    } catch (Exception exc) {
      exc.printStackTrace();
    } 
    return assetEntries;
  }

Full impl class definition here

Entire service module here.

And here’s my freemarker snippet.

<#assign cmsAssistant = serviceLocator.findService('com.creatosix.cms.assistant.CmsAssistant')>
<#assign myEntries = cmsAssistant.getAssetEntriesByGroupIdAndStructureName(groupId, "FAQ")>
...
...
...
<h1>Most Popular Items</h1>
<br>
<#if myEntries?has_content>
    <#list myEntries?sort_by("viewCount")?reverse as curEntry>
        <#if curEntry?counter == 4>
            <#break>
        </#if>
        <#assign assetRenderer = curFaq.getAssetRenderer() >
            <#assign journalArticle = assetRenderer.getArticle()>
          <#assign document = saxReaderUtil.read(journalArticle.getContentByLocale(locale.toString()))>

          <#-- Fetch the field named txtSomething -->
          <#assign node = document.selectSingleNode("/root/dynamic-element[@name='txtSomething']/dynamic-content")> 
          <#assign something = node.getText() >
        
          <#-- Fetch the field named txtIdentifier -->
          <#assign node = document.selectSingleNode("/root/dynamic-element[@name='txtIdentifier']/dynamic-content")> 
          <#assign itemId = node.getText() >

        <a href="/web/guest/home#${itemId}">
          ${something} (${curEntry.viewCount})
      </a> <br>
    </#list>
</#if>
...
...
...

Note that I only use two fields from my matching web content items – txtSomething and txtIdentifier. The idea is to show hyperlinks to a list of these items that may be displayed further down in the template.

Enjoy!

Published by Javeed

Java/JEE solution provider. Liferay Portal enthusiast. I am a Software Engineer with a focus on highly maintainable solutions that lie at the sweet spot intersection of modularity, extensibility and development team productivity. I enjoy experimenting with innovative documentation ideas.

Leave a comment