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 附件
88238 查看
平均 (1 投票)
满分为 5,平均得分为 1.0。
评论
讨论主题回复 作者 日期
Add request.setAttribute("test","Just a... Fuad Efendi 2008年7月25日 下午12:21
Regarding this, if you want to set request... Fuad Efendi 2008年8月7日 上午6:40
How do I initialize the list of articles so... Thomas Kellerer 2008年10月23日 上午4:15
Hi everyone, I'm a liferay newbie.Starting with... Nguyen Paul 2008年12月3日 上午9:00
I have the same problem - except using the... Brandon Wagner 2008年12月16日 下午3:30
I'm working on liferay 5.1.1 and the code over... mario salvitti 2009年2月20日 上午1:47
Is there anyway to pass velocity variables into... dennis monsewicz 2009年6月1日 上午11:18
Is there any way to retrieve the currently... Thomas Kellerer 2009年2月2日 下午11:58
See... Alex Wallace 2010年3月23日 下午2:32
@language_id@ is not working for me. It just... Alex Galkin 2013年2月14日 上午10:14

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.
在 08-7-25 下午12:21 发帖。
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.
在 08-8-7 上午6:40 发帖。
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.
在 08-10-23 上午4:15 发帖。
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
在 08-12-3 上午9:00 发帖以回复 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?
在 08-12-16 下午3:30 发帖以回复 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.
在 09-2-2 下午11:58 发帖。
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>
在 09-2-20 上午1:47 发帖以回复 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
在 09-6-1 上午11:18 发帖以回复 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
在 10-3-23 下午2:32 发帖以回复 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.
在 13-2-14 上午10:14 发帖。