« Back

Accessing a Web Content Structure from Application Display Template

Company Blogs February 14, 2014 By Paulo Fernandes

In my experience working with Liferay projects, the most used component is Web Content, not only the Basic Web Content. A lot of times the customer needs to create a structure and a template to achieve his goal. In previous Liferay Portal version (aka 6.1) we need to create a hook to create new Asset Display View, but with Liferay Portal 6.2, we can create a simple Application Display Template (ADT) for Asset Publisher and everything is working in a great way and easily.

For this article I created a simple structure with the following fields: Title, SubTitle and Content.

Let's create the ADT to show the Title, SubTitle and a Link to read the full structure.

 

Solution

The following code access the Web Content Structure in two different ways. Choose which is better to you.

#if (!$entries.isEmpty())

    #foreach ($entry in $entries)
        #set($renderer = $entry.getAssetRenderer() )
        #set($className = $renderer.getClassName() )

        #if( $className == "com.liferay.portlet.journal.model.JournalArticle" )
            #set( $journalArticle = $renderer.getArticle() )
            #set( $document = $saxReaderUtil.read($journalArticle.getContent()) )
            #set( $rootElement = $document.getRootElement() )


            ## first alternative            
            #foreach( $dynamicElement in $rootElement.elements() )
                #if( "subTitle" == $dynamicElement.attributeValue("name") )
                    #set( $subTitle1 = $dynamicElement.element("dynamic-content").getText() )
                    first alternative -> $subTitle1 <br />
                #end
            #end


            ## second alternative
            #set( $xPathSelector = $saxReaderUtil.createXPath("dynamic-element[@name='subTitle']") )
            #set( $subTitle2 = $xPathSelector.selectSingleNode($rootElement).getStringValue() ) 
            second alternative -> $subTitle2 <br />
        #end
    #end
#end

Choose which one is better to you. A improve that you could do for this code is check, if the WebContent (JournalArticle) is the same that the Structure that you are accessing. I didn't to that because I have only one Strucutre.

Let's see the final code.

#if (!$entries.isEmpty())
    <div class="news">
    #foreach ($entry in $entries)
        #set($renderer = $entry.getAssetRenderer() )
        #set($className = $renderer.getClassName() )
        #if( $className == "com.liferay.portlet.journal.model.JournalArticle" )
            #set( $journalArticle = $renderer.getArticle() )
            #set( $document = $saxReaderUtil.read($journalArticle.getContent()) )
            #set( $rootElement = $document.getRootElement() )
            #set( $xPathSelector = $saxReaderUtil.createXPath("dynamic-element[@name='subTitle']") )
            #set( $subTitle = $xPathSelector.selectSingleNode($rootElement).getStringValue() )
            #set( $link = $renderer.getURLViewInContext($renderRequest, $renderResponse, '') )
            
            <div class="new">
                <h1 class="title">$entry.getTitle($locale)</h1>
                <h3 class="sub-title">$subTitle</h3>
                <p class="read-more">
                    <a href="$link">Read More</a>
                </p>
            </div>
        #end
    #end
    </div>
#end

 

I hope that this code can help you more than is helping me. If you have any trouble, please post in our forum.

 

More information about Application Display Template

https://www.liferay.com/pt/web/eduardo.garcia/blog/-/blogs/new-ways-of-customization-with-application-display-templates-part-i-

https://www.liferay.com/pt/web/eduardo.garcia/blog/-/blogs/new-ways-of-customization-with-application-display-templates-part-ii-

 
See you soon.
Threaded Replies Author Date
Weel done, useful article. Daniele Baggio February 14, 2014 8:24 AM
Nice Paulo, certainly very useful Anderson Perrelli February 14, 2014 10:37 AM
Nice job! :) Antônio Junior February 25, 2014 6:40 AM
What is the liferay capability of getting... Hari P Sharma February 26, 2014 1:19 AM
We have a portlet who do that or you can use... Paulo Fernandes February 27, 2014 4:16 AM
That's exactly what i've been trying to do on... Luca Lauretta March 6, 2014 3:27 PM
Thanks for the great post. It is very helpful.... c s March 14, 2014 4:28 PM
Thank you. It is very usefull. ji svob May 4, 2014 8:50 AM
Hello, very useful documentation.Though, I'm... Andra Stanciu May 8, 2014 12:42 AM
Thanks for the article. Please you mentioned... Matthew Edor July 9, 2014 9:28 AM
Nice article! Thanks! Sergio Romero July 18, 2014 3:48 AM
dont rules :( I need extract values from the... Sergio Romero July 21, 2014 3:05 AM
Hi Sergio, Have you solved the problem with... Virginia Calderón October 6, 2014 1:26 AM
How would you go about getting the full content... Konstantin Fedorov July 21, 2014 2:04 PM
Thanks, great article. :) The only thing I... Miroslav Bimbo July 23, 2014 3:18 AM
Thanks a lot... this was very useful for me...... Shashikant Sharma September 9, 2015 2:52 AM
Great post and very useful, but..I have 2... Maarten J August 1, 2014 4:35 AM
Hi, #foreach($dynamicElement in... Virginia Calderón October 6, 2014 3:51 AM
To solve the problem with the redirection that... Virginia Calderón October 6, 2014 5:53 AM
Great, Thank You I have a question : - How can... Naoufel EH March 13, 2015 6:41 AM
Hi all. If you want to access to full content,... Víctor Ponz March 26, 2015 11:48 PM
Hi Víctor. I tried use your solution to access... Wesley Rocha July 20, 2015 11:57 AM
Please specify a little, which is your ... Víctor Ponz July 20, 2015 10:26 PM
Great article. I was using freemarker ADT and... Michael Green August 28, 2015 9:38 PM

Weel done, useful article.
Posted on 2/14/14 8:24 AM.
Nice Paulo, certainly very useful
Posted on 2/14/14 10:37 AM.
Posted on 2/25/14 6:40 AM.
Hari P Sharma
What is the liferay capability of getting recently modified documents and recently accessed documents ?
Posted on 2/26/14 1:19 AM.
We have a portlet who do that or you can use the asset publisher and order that documents from modified date.
Posted on 2/27/14 4:16 AM in reply to Hari P Sharma.
That's exactly what i've been trying to do on my own months ago without success.
I'm very pleased to see this solution documented.

Thanks Paulo!
Posted on 3/6/14 3:27 PM.
Thanks for the great post. It is very helpful. My question is, if the structure is really complicated, won't parsing the XML have performance issues? Is this the best way to accomplish getting attribute values for elements in the structure?
Posted on 3/14/14 4:28 PM.
Thank you.
It is very usefull.
Posted on 5/4/14 8:50 AM.
Hello, very useful documentation.Though, I'm trying to get the Read More behaviour to work. From your example it's not working.Can you help ? Regards
Posted on 5/8/14 12:42 AM.
Thanks for the article. Please you mentioned choosing a JournalArticle based on a specific structure, how do you go about doing that?
Thanks!
Posted on 7/9/14 9:28 AM.
Nice article! Thanks!
Posted on 7/18/14 3:48 AM.
dont rules emoticon

I need extract values from the Structure journalArticle.
this sentence
#set( $document = $saxReaderUtil.read($journalArticle.getContent()) )
Don't works.

$document is null. but $journalArticle.getContent(), it's not null. whats the problem?
Posted on 7/21/14 3:05 AM in reply to Sergio Romero Zayas.
How would you go about getting the full content of a journal article? Like in "full content" display template.
Posted on 7/21/14 2:04 PM.
Thanks, great article. emoticon
The only thing I missed there is proper way of creating links.
Here is the code I found on liferay pages, hopefully useful for someone on this place:
#set( $link = $assetPublisherHelper.getAssetViewURL($renderRequest, $renderResponse, $curEntry) )
#if ("$assetLinkBehavior" != "showFullContent")
#set( $link = $renderer.getURLViewInContext($renderRequest, $renderResponse, $link) )
#end
Posted on 7/23/14 3:18 AM.
Great post and very useful, but..I have 2 questions.

If you added the value for 2 different languages it's always getting the value for 1 language. How to make this language dependent?

How to add the label of the field (in the right language)?
Posted on 8/1/14 4:35 AM.
Hi Sergio,

Have you solved the problem with the getContent method?

Thanks,
Posted on 10/6/14 1:26 AM in reply to Sergio Romero Zayas.
Hi,

#foreach($dynamicElement in $rootElement.elements())
#if($validator.equals($fieldName, $dynamicElement.attributeValue("name")))
#foreach($dynamicContent in $dynamicElement.elements())
#if($dynamicContent.attributeValue("language-id­") ==$locale)
<a href="$viewURL">
#set($contentText = $dynamicContent.getText())
<p>$contentText</p>
</a>
#end <!-- if language -->
#end <!-- forEach dynamicContent -->
#end <!-- if fieldName -->
#end <!-- forEach dynamicContent -->

I hope you find it useful,
Posted on 10/6/14 3:51 AM in reply to Maarten J.
To solve the problem with the redirection that occurs in version 6.2 (LPS-46830) you should replace the redirect parameter with the variable $currentURL. For example:

## solution to the incidence of LPS-46830 backurl
# set ($ replaceURL = $ urlViewContext.substring ($ urlViewContext.indexOf ("redirect ="), $ urlViewContext.length ()))
# set ($ newUrl = "redirect =" + $ currentURL)
# set ($ urlViewContext = $ urlViewContext.replace ($ replaceURL, newUrl $))
## solution to the incidence of LPS-46830 backurl
Posted on 10/6/14 5:53 AM.
Great, Thank You
I have a question :
- How can We access a field of type "Link to Page".
$dynamicElement.element("dynamic-content").getText() does"nt work and it display the reference to element not the value

Thanks
Posted on 3/13/15 6:41 AM.
Hi all.
If you want to access to full content, try this
<#assign assetRenderer = entry.getAssetRenderer() />
<#assign docXml = saxReaderUtil.read(assetRenderer.getArticle().getContent()) />
<#assign rootElement = docXml.getRootElement() />
<#assign availableLocales = rootElement.attribute("available-locales").getText() />
<#assign defaultLocale = rootElement.attribute("default-locale").getText() />
<#if (availableLocales?contains(locale)) >
<#assign fulllContent = docXml.valueOf("//static-content[@language-id='" + locale + "']/text()") />
<#else>
<#assign fulllContent = docXml.valueOf("//static-content[@language-id='" + defaultLocale + "']/text()") />
</#if>

Hope it helps
Posted on 3/26/15 11:48 PM.
Hi Víctor.
I tried use your solution to access "Full Content", but don't work for me. Would you help me solve my problem?

I tried create an ADT


<#assign liferay_ui = taglibLiferayHash["/WEB-INF/tld/liferay-ui.tld"] />

<div id="lista-sanfona" class="lista-sanfona">

<div class="lista-sanfona-inner">
<#list entries as entry>

<#assign assetRenderer = entry.getAssetRenderer() />

<#assign docXml = saxReaderUtil.read(assetRenderer.getArticle().getContent()) />

<#assign rootElement = docXml.getRootElement() />

<#assign availableLocales = rootElement.attribute("available-locales").getText() />

<#assign defaultLocale = rootElement.attribute("default-locale").getText() />


<#assign entryTitle = htmlUtil.escape(assetRenderer.getTitle(locale)) />

<#assign viewURL = assetPublisherHelper.getAssetViewURL(renderRequest, renderResponse, entry) />

<#assign dateFormat = "dd/MM/yyyy" />

<#if assetLinkBehavior != "showFullContent">
<#assign viewURL = assetRenderer.getURLViewInContext(renderRequest, renderResponse, viewURL) />
</#if>

<#if entry_index % 2 == 0>
<#assign classe_alt = "even" />
<#else>
<#assign classe_alt = "odd" />
</#if>

<div class="sanfona-item item-${entry_index} ${classe_alt}">

<h4 class="header toggler-header-collapsed">
<span class="titulo">${entryTitle}</span>
</h4>
<div class="content toggler-content-collapsed">
<#if (availableLocales?contains(locale)) >
<#assign fullContent = docXml.valueOf("//static-content[@language-id='" + locale + "']/text()") />
${fullContent}
<#else>
­<#assign fullContent = docXml.valueOf("//static-content[@language-id='" + defaultLocale + "']/text()") />
${fullContent}
</#if>
</div>­

<span class="lfr-meta-actions asset-actions">
<@getEditIcon />
</span>
<div class="clearfix"></div>
</div>
</#list>
</div>
</div>
<#macro getEditIcon>
<#if assetRenderer.hasEditPermission(themeDisplay.getPermissionChecker())>
<#assign­ redirectURL = renderResponse.createRenderURL() />

${redirectURL.setParameter("struts_action", "/asset_publisher/add_asset_redirect")}
${redirectURL.setWindowState("pop_up")­}

<#assign editPortletURL = assetRenderer.getURLEdit(renderRequest, renderResponse, windowStateFactory.getWindowState("pop_up"), redirectURL)!"" />

<#if validator.isNotNull(editPortletURL)>
<#assign title = languageUtil.format(locale, "edit-x", entryTitle) />

<@liferay_ui["icon"]
image="edit"
message=title
­ url="javascript:Liferay.Util.openWindow({dialog: {width: 960}, id:'" + renderResponse.getNamespace() + "editAsset', title: '" + title + "', uri:'" + htmlUtil.escapeURL(editPortletURL.toString()) + "'});"
/>
</#if>
</#if>
</#macro>
Posted on 7/20/15 11:57 AM in reply to Víctor Ponz.
Please specify a little, which is your problem/error?
Posted on 7/20/15 10:26 PM in reply to Wesley Rocha.
Great article.

I was using freemarker ADT and converting the code listed in the article from the velocity template. I started to get errors:

Expression dynamicElement.attributeValue("name") is undefined .....

The same code from the article cut and pasted into a template on the same site with the same page with the same content works without error. The reason I did this is I was getting other errors from other example code in freemarker which were producing similar errors.

I am using liferay 6.2 CE.

The code I have for freemarker is as follows: (I have extended it to handle the repeat values in my structure).
The extended version in velocity works as required. For now I am going to just convert my full templates to velocity to get around this problem.

Is there something I am missing on the code below?

<#if entries?has_content>

<#list entries as entry>
<#assign renderer = entry.getAssetRenderer()>
<#assign className = renderer.getClassName() >

<#if className == "com.liferay.portlet.journal.model.JournalArticle" >
<#assign journalArticle = renderer.getArticle() >
<#assign document = saxReaderUtil.read(journalArticle.getContent()) >
<#assign rootElement = document.getRootElement() >



<#list rootElement.elements() as dynamicElement >
<#if "rawLinks" == dynamicElement.attributeValue("name") >
<#assign subTitle1 = dynamicElement.element("dynamic-content").getText() >
first alternative -> ${subTitle1} <br />
</#if>
</#list>



<#assign xPathSelector = saxReaderUtil.createXPath("dynamic-element[@name='rawLinks']") >
<#list xPathSelector.selectNodes(rootElement) as subTitle2>
second alternative -> ${subTitle2.getStringValue()} <br />
</#list>
</#if>
</#list>
</#if>
Posted on 8/28/15 9:38 PM.
Thanks a lot... this was very useful for me...
Just one change instead of curEntry, it worked with $entry
(as used in above main code)
Posted on 9/9/15 2:52 AM in reply to Miroslav Bimbo.