Liferay is a Gartner Magic Quadrant Leader for the Sixth Year! Find out why

Journal Tokens#

At runtime, several tokens, included in Journal elements (Article, Structure, Template) will be translated to their applicable runtime value at processing time.

Tokens have the following form:

 @token_name@

Here is a complete list of tokens and their runtime values:

Token Value
@cdn_host@themeDisplay.getCDNHost()
@company_id@themeDisplay.getCompanyId()
@group_id@groupId
@cms_url@themeDisplay.getPathContext() + "/cms/servlet"
@image_path@ themeDisplay.getPathImage()
@friendly_url_private_group@themeDisplay.getPathFriendlyURLPrivateGroup()
@friendly_url_private_user@themeDisplay.getPathFriendlyURLPrivateUser()
@friendly_url_public@themeDisplay.getPathFriendlyURLPublic()
@main_path@themeDisplay.getPathMain()
@portal_ctx@themeDisplay.getPathContext()
@portal_url@Http.removeProtocol(themeDisplay.getURLPortal())
@root_path@themeDisplay.getPathContext()
@theme_image_path@themeDisplay.getPathThemeImages()
@language_id@the language_id of the current request
@layout_set_friendly_url@Added rev.32249 5.1.x, 5.2.x+

i.e.

 @main_path@ will be replaced (usually) by /c

The

@layout_set_friendly_url@
token was added to eliminate the need for fairly complex decision of how to render a URL pointing at some layout in the same community|org by friendlyURL of the layout. Simple! Just use
@layout_set_friendly_url@/{layoutFriendlyURL} 
and the urls will work in all cases.

@layout_set_friendly_url@

will behave as:

  1. when there IS a virtual host assigned to the layout set:
    return BLANK
  2. when the layout set does NOT have a virtual host
    return (/web|/group|/user) + @group_friendly_url@
  3. when the current serverName passed for the request does NOT match the virtual host
    return (/web|/group|/user) + @group_friendly_url@

This allows for a JournalArticle to always properly form a proper URL regardless of where it is used; in Staging, Live, or copied to some other Community.

<a href="@layout_set_friendly_url@/home">Home</a>
will work in all scenarios. And will remove the need for complex processing of these cases in templates.

Reserved Elements#

There are several meta elements (called 'reserved elements') which are added to an article's xml before they are processed by the template.

They have the following xpath:

 /root/dynamic-element[@name='reserved-article-????']/dynamic-content

As such they can be retrieved as follows:

From XSL

 <xsl:value-of select="/root/dynamic-element[@name='reserved-article-????']/dynamic-content" />

From Velocity

 $reserved-article-????.getData()


Here is the complete list of 'reserved elements'

 reserved-article-id			13307
 reserved-article-version		1.0
 reserved-article-title			First blog post
 reserved-article-description		This is a very cool Blog post.
 reserved-article-create-date		2007-08-08 19:25:41.0
 reserved-article-modified-date		2007-08-09 16:47:10.0
 reserved-article-display-date		2007-08-08 19:23:00.0
 reserved-article-small-image-url						# added in 4.3.5+
 reserved-article-author-id		2
 reserved-article-author-name		Joe Bloggs
 reserved-article-author-email-address	
 reserved-article-author-comments
 reserved-article-author-organization					# removed in 4.3.5+
 reserved-article-author-location						# removed in 4.3.5+
 reserved-article-author-job-title	Software Engineer

Journal Request Element (4.3.1+)#

Included in the pre-template-processed article content is the following request element, which contains various useful sub-elements. Here is the RAW form it takes once attached to the article's xml.

  <root>
    <request>
      <container-type>portlet</container-type>
      <container-namespace>/</container-namespace>
      <content-type>text/html</content-type>
      <server-name>localhost</server-name>
      <server-port>8080</server-port>
      <secure>false</secure>
      <auth-type/>
      <remote-user>2</remote-user>
      <context-path>/</context-path>
      <locale>en_US</locale>
      <portlet-mode>view</portlet-mode>
      <portlet-session-id>0539B7801C66CE6030F2EF770D949134</portlet-session-id>
      <scheme>http</scheme>
      <window-state>normal</window-state>
      <action>false</action>
      <portlet-namespace>_56_INSTANCE_tJfy_</portlet-namespace>
      <render-url>...</render-url>
      <render-url-exclusive>...</render-url-exclusive>
      <render-url-maximized>...</render-url-maximized>
      <render-url-minimized>...</render-url-minimized>
      <render-url-normal>...</render-url-normal>
      <render-url-pop-up>...</render-url-pop-up>
      <parameters>
        <parameter>
          <name>name</name>
          <value>Ray</value>
        </parameter>
      </parameters>

New in 4.3.5+

        <attributes>
          <attribute>
            <name></name>
            <value></value>
          </attribute>
        </attributes>
        <portlet-session>
          <portlet-attributes>
            <attribute>
              <name></name>
              <value></value>
            </attribute>
          </portlet-attributes>
          <application-attributes>
            <attribute>
              <name></name>
              <value></value>
            </attribute>
          </application-attributes>
        </portlet-session>
    </request>
  </root>

Of notable significance are the following paths:

 /root/request/portlet-namespace
 /root/request/render-url
 /root/request/render-url-exclusive
 /root/request/render-url-maximized
 /root/request/render-url-minimized
 /root/request/render-url-normal
 /root/request/render-url-pop-up

Access to request parameters

 /root/request/parameters

New in 4.3.5+

Access to request attributes

 /root/request/attributes

Access to portlet session attributes (portlet scope)

 /root/request/portlet-session/portlet-attributes

Access to portlet session attributes (application scope)

 /root/request/portlet-session/application-attributes

By accessing the request element it is possible to create portlet urls linking back to the portlet displaying the article. Parameters can be used to alter the backend API calls and/or they can be used for affecting the rendering logic, you can check the Window State, Portlet Mode, Locale, etc.

Accessing these from XSL or VM is simple.

For example, getting the current Window State of the portlet:

From XSL:

  <xsl:variable name="windowState" select="//request/window-state" />

From Velocity:

  #set ($windowState = $request.get('window-state'))

or simply:

  #set ($windowState = $request.window-state)

Accessing a request parameter:

From XSL:

  <xsl:variable name="param" select="//request/parameters/parameter[name/text() = 'param']/value" />

From Velocity:

  #set ($param = $request.get('parameters').get('param'))

or simply:

  #set ($param = $request.parameters.param)

Accessing a request attribute:

From XSL:

  <xsl:variable name="attr" select="//request/attributes/attribute[name/text() = 'attr']/value" />

From Velocity:

  #set ($attr = $request.get('attributes').get('attr'))

or simply:

  #set ($attr = $request.attributes.attr)

Accessing a portlet scoped session attribute:

From XSL:

  <xsl:variable name="pSAttr" select="//request/portlet-session/portlet-attributes/attribute[name/text() = 'pSAttr']/value" />

From Velocity:

  #set ($pSAttr = $request.get('portlet-session').get('portlet-attributes').get('pSAttr'))

or simply:

  #set ($pSAttr = $request.portlet-session.portlet-attributes.pSAttr)

Accessing an application scoped session attribute:

From XSL:

  <xsl:variable name="aSAttr" select="//request/portlet-session/application-attributes/attribute[name/text() = 'aSAttr']/value" />

From Velocity:

  #set ($aSAttr = $request.get('portlet-session').get('application-attributes').get('aSAttr'))

or simply:

  #set ($aSAttr = $request.portlet-session.application-attributes.aSAttr)

Request Handling Example#

Here is a small example demonstrating request handling.

Structure#

  <root>
    <dynamic-element name='text' type='text_box'></dynamic-element>
  </root>

XSL Template#

<xsl:template match="/">

    <xsl:variable name="url" select="//request/render-url" />
    <xsl:variable name="namespace" select="//request/portlet-namespace" />
    <xsl:variable name="paramName" select="//request/parameters/parameter[name/text() = 'name']/value" />
  
    <xsl:variable name="friendlyUrl" select="//request/attributes/attribute[name/text() = 'FRIENDLY_URL']/value" />
  
    <!-- BEGIN OUTPUT -->
  
    <xsl:value-of disable-output-escaping="yes" select="//dynamic-element[@name='text']" />

    <xsl:text>Current Page: </xsl:text>
    
    <xsl:value-of disable-output-escaping="yes" select="$friendlyUrl" />
    
    <xsl:choose>
        <xsl:when test="$paramName != ''">

            <xsl:text>Hello </xsl:text><xsl:value-of select="$paramName" />!
        
            <a href="{$url}">Back</a>


        </xsl:when>
        <xsl:otherwise> 
  
            <form action="{$url}" name="{$namespace}fm">
  
            Please enter your name:
            <input type="text" name="{$namespace}paramName" />
          
            <input type="submit" />
  
            </form>
  
        </xsl:otherwise>
  
    </xsl:choose>
</xsl:template>

Velocity Template#

  #set ($url = $request.get('render-url'))
  #set ($namespace = $request.get('portlet-namespace'))
  #set ($paramName = $request.get('parameters').get('name'))
  
  #set ($friendlyUrl = $request.get('attributes').get('FRIENDLY_URL'))
  
  <!-- BEGIN OUTPUT -->
  
  $text.getData()
  
  Current Page:
  $friendlyUrl
  
  #if ($paramName)
  
    Hello ${paramName}!
    
    <a href="$url">Back</a>
  
  #else
  
    <form action="$url" name="${namespace}frm">
  
      Please enter your name:
      <input type="text" name="${namespace}paramName" />
      
      <input type="submit" />
  
    </form>
  
  #end

Combined with the backend API calls, this is a powerful mechanism for creating complex, dynamic, and customized displays of journal content using one of two powerful templating languages.

Use it for building "Read More..." teaser views, etc.

Backend Journal Service Calls#

The Journal has 4 calls which can be made to it's backend services. These allow a journal template designer to retrieve Structures, Templates, and Articles for use in various ways. These might be internally loaded through xsl:import/xsl:include or xpath:document(URI) methods

Getting a structure:

 @main_path@/journal/get_structure?groupId={GID}&structureId={SID}

Where {GID} is the groupId and {SID} is the structureId of some Structure.

Getting a template:

 @main_path@/journal/get_template?groupId={GID}&templateId={TID}[&transform={true|false}]

Where {GID} is the groupId and {TID} is the templateId and optional 'transform' is true or false. False means the template will not be pre-processed such that tokens will not be translated.

The content type of the returned template will depend on the type of template:

 css = text/css
 vm  = text/plain
 xsl = text/xml

So, clients should play nicely with the types they expect. E.g. If you create a CSS template to be loaded in your theme, the file type will agree with the browser.

Getting an article:

 @main_path@/journal/get_article?groupId={GID}&articleId={AID}

where {GID} is the groupId and {AID} is the articleId. For internationalization, you can add &languageId={LID} where {LID} = @language_id@

Getting a list of articles:

 @main_path@/journal/get_articles?groupId={GID}[&type={TYPE_NAME}]
     [&structureId={SID[,SID]}][&templateId={TID}][&displayDateGT={ISODateFormat}]
     [&displayDateLT={ISODateFormat}][&delta={deltaInt}][&orderBy={display-date}]

Where {GID} is the groupdId, {TYPE_NAME} is the type as defined in (portal.properties)journal.article.types, {SID[,SID]} is comma delimited list of structureIds, {TID} is the templateId, {ISODateFormat} for displayDateGT|displayDateLT is an ISO date format signifying a boundary for the display date of the returned articles, {deltaInt} is the max number of articles to return, and {display-date} for orderBy means that the articles should be ordered by displayDate as opposed to modifiedDate (the default).

JournalContentFriendlyURLMapper#

Quite often you want to provide access to Journal Content dynamically without having to publish each and every article on its own page. Imagine a News service which generates new content frequently. It would be very cumbersome to have to create a new page for every article. To this end, there is a very handy mechanism which allows us to strategically and dynamically position content on page. This is done using Liferay's JournalContentFriendlyURLMapper feature.

This feature is a simple URL handling mechanism built into the Journal Content portlet, but you don't have to directly use the Journal Content portlet in order to leverage some of it's features (but you can to utilize the more advanced targetting feature).

Suppose you have a template which lists the 10 most recent articles (probably of some type and based on some structure). The list may contain the title, date, description, author and probably a link to the full article. A simple way to create this link is to create it based on a friendly layout path along with some path details about the article in question.

For example (using Velocity):

#set ($targetPortletID = "56")
#foreach ($article in $articles)
    #set ($articleUrl = "/web/guest/news/-/journal_content/" + $targetPortletID + "/" + $article.groupId +  "/" + $article.articleId)
    <h3>$article.title</h3>
    <span>$article.userName</span>
    <p>$article.description</p>
    <a href="${articleUrl}">Read More...</a>
#end

The links above will cause the articles in question to dynamically open up on the layout "/web/guest/news" maximized in a Journal Content portlet, even if the portlet does not exist on this layout.

This might be what you want, it might not. Suppose though that you would prefer for the article to appear in a specific location on some specified layout, so that can surround it by ads and other related content. You could customize a page with all the ads and related portlets you want, and in the position where you want to target the full article, place an empty Journal Content portlet. Once the Journal Content portlet is positioned, click the "Configuration" icon and on the "Setup" tab locate the "Portlet ID" and take note of it.

Once you have to Portlet ID of the target portlet noted, go back to your template and in place of the "56" for the

$targetPortletID
set it to the Portlet ID you noted.

e.g. Suppose that the portlet ID of the positioned Journal Content portlet was "56_INSTANCE_56Ht".

The resulting code would be:

#set ($targetPortletID = "56_INSTANCE_56Ht")
#foreach ($article in $articles)
    #set ($articleUrl = "/web/guest/news/-/journal_content/" + targetPortletID + "/" + $article.groupId +  "/" + $article.articleId)
    <h3>$article.title</h3>
    <span>$article.userName</span>
    <p>$article.description</p>
    <a href="${articleUrl}">Read More...</a>
#end

The syntax for the

JournalContentFriendlyURL
is as follows:

 <layout_friendly_url>/-/journal_content/{PID}/{GID}/{AID}[/{TID}]

Where {PID} is the Portlet ID ("56" will cause the article to be opened in a maximized Journal Content portlet), {GID} is the groupId of the article you want to display, {AID} is the articleId of the article you want to display, and optionally {TID} is the templateId of a template to be used for rendering the article (the template must be associated with the structure of the article, otherwise it will be ignored). Omitting {TID} will simply use the default template associated with the article.

0 Attachments
109307 Views
Average (1 Vote)
The average rating is 1.0 stars out of 5.
Comments
Threaded Replies Author Date
Add request.setAttribute("test","Just a... Fuad Efendi July 25, 2008 12:21 PM
Regarding this, if you want to set request... Fuad Efendi August 7, 2008 6:40 AM
How do I initialize the list of articles so... Thomas Kellerer October 23, 2008 4:15 AM
Hi everyone, I'm a liferay newbie.Starting with... Nguyen Paul December 3, 2008 9:00 AM
I have the same problem - except using the... Brandon Wagner December 16, 2008 3:30 PM
I'm working on liferay 5.1.1 and the code over... mario salvitti February 20, 2009 1:47 AM
Is there anyway to pass velocity variables into... dennis monsewicz June 1, 2009 11:18 AM
Is there any way to retrieve the currently... Thomas Kellerer February 2, 2009 11:58 PM
See... Alex Wallace March 23, 2010 2:32 PM
@language_id@ is not working for me. It just... Alex Galkin February 14, 2013 10:14 AM

Add request.setAttribute("test","Just a Test!"); to Article Content view.jsp
Modify Velocity template:
#set ($test = $request.get('attributes').get('test'))
<h1>$test</h1>
Now, we can see "Just a Test!" for each and any article. However, if we modify JSP request.setAttribute("test","N/A"); we will still see "Just a Test!" for each and any article. Caching! But it even helps because I only need to pass (static) URLs to Journal.

P.S.
Liferay 5 has "Cacheable" checkbox for Journal Templates.
Posted on 7/25/08 12:21 PM.
Regarding this, if you want to set request attribute from the same Journal Content Portlet instance:

#set ($friendlyUrl = $request.get('attributes').get('FRIENDLY_URL'))

- it works only if you modify ViewAction of Journal Content Portlet; ViewAction creates instance of JournalArticleDisplay, and request.setAttribute should be called before that.
Posted on 8/7/08 6:40 AM.
How do I initialize the list of articles so that
#for ($article in $articles)
actually iterates over someting?
I have pasted the above for loop into a VM template, but it does not display anything.

I also cannot get the link to the article to work. I tried this within a VM template that displays the abstract of the article to link to the real article, but the portlet display does not change when I do so.
The link "code" looks like this:

<a href="@friendly_url_public@/-/journal/56_INSTANCE_XUlI/@group_id@/$reserved-arti­cle-id.getData()">Read more</a>

A request is sent, but the portlet display does not change. The portlet ID is the one that is displaying the abstract. I also tried to append the template ID of a different template to the URL, but still no luck.
Posted on 10/23/08 4:15 AM.
Hi everyone,
I'm a liferay newbie.Starting with liferay 5.1.2
I create a structure
<root>
<dynamic-element name='text' type='text_box'></dynamic-element>
</root>
and a template for that structure

<xsl:template match="/">

<xsl:variable name="url" select="//request/render-url" />
<xsl:variable name="namespace" select="//request/portlet-namespace" />
<xsl:variable name="paramName" select="//request/parameters/parameter[name/text() = 'name']/value" />

<xsl:variable name="friendlyUrl" select="//request/attributes/attribute[name/text() = 'FRIENDLY_URL']/value" />

<!-- BEGIN OUTPUT -->

<xsl:value-of disable-output-escaping="yes" select="//dynamic-element[@name='text']" />

<xsl:text>Current Page: </xsl:text>

<xsl:value-of disable-output-escaping="yes" select="$friendlyUrl" />

<xsl:choose>
<xsl:when test="$paramName != ''">

<xsl:text>Hello </xsl:text><xsl:value-of select="$paramName" />!

<a href="{$url}">Back</a>


</xsl:when>
<xsl:otherwise>

<form action="{$url}" name="{$namespace}fm">

Please enter your name:
<input type="text" name="{$namespace}paramName" />

<input type="submit" />

</form>

</xsl:otherwise>

</xsl:choose>
</xsl:template>
Exactly as the example. As soon as I submit a text for example, I dont see my "Hello" word displayed. It means that the variable ${namespace}paramName is not recognised in the new page.
Is there anything missing? Do I have to modify liferay configuration files?
Thank you in advance
Posted on 12/3/08 9:00 AM in reply to Thomas Kellerer.
I have the same problem - except using the Velocity template. Has something changed since this Wiki was completed? I'm trying to pass variables between two Journal Content portlets, which should be fairly easy. And I'm having no luck in the forums and this example doesn't seem to work anymore.

Any merciful experts out there?
Posted on 12/16/08 3:30 PM in reply to Nguyen Paul.
Is there any way to retrieve the currently logged in user in a Journal Template?

I tried (after looking at JournalVmUtil and VelocityVariables)

$user
$user.fullName
$userId
$permissionChecker.userId
$permission­Checker.fullName
$request.get('userId')

None of them is working. The only user id I can get hold of, is the one of the articles/templates author which is not really interesting.

There must be a way to find out if the template is shown to an authenticated user or guest?

I'm a bit frustrated right now due to the lack of documentation.
Posted on 2/2/09 11:58 PM.
I'm working on liferay 5.1.1 and the code over not working, but now I have a partial solution, the problem is in the "namespace" invoke, try this to take inspiration:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:variable name="renderUrl" select="//request/render-url" />
<xsl:variable name="url" select="//request/render-url" />
<xsl:variable name="namespace" select="//request/portlet-namespace" />

<xsl:variable name="cmd" select="//request/parameters/parameter[name/text() = 'cmd']/value" />


<!-- BEGIN OUTPUT -->

<xsl:value-of disable-output-escaping="yes" select="//dynamic-element[@name='text']" />


<xsl:text>CMD:</xsl:text><xsl:value-of select="$cmd" />
<br></br>

<input type="submit" value="TEST" onClick="self.location ='{$renderUrl}&amp;{$namespace}cmd=test'" />
</xsl:template>

</xsl:stylesheet>
Posted on 2/20/09 1:47 AM in reply to Brandon Wagner.
Is there anyway to pass velocity variables into a PHP portlet? Or any language for that matter? I am trying to pass the friendlyURL into my PHP portlet
Posted on 6/1/09 11:18 AM in reply to mario salvitti.
See http://www.liferay.com/community/wiki/-/wiki/Main/How%20To%20Access%20Objects%20­From%20A%20Velocity%20Template ...

It explans how:

$request.attributes.USER_ID
Posted on 3/23/10 2:32 PM in reply to Thomas Kellerer.
@language_id@ is not working for me. It just returns @language_id@ instead of a value. Issue is related to using it inside Web Content. Other variables are parsed Ok.
Posted on 2/14/13 10:14 AM.