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:
- Define a service with a method that fetches and returns AssetEntry references matching the given structure.
- 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
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!
