<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/">  <title>Ray Augé</title>  <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/rss" />  <subtitle>Ray Augé</subtitle>  <entry>    <title>Portal Hook Plugins</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/portal-hook-plugins" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-10-07T17:27:28Z</updated>    <published>2008-10-07T17:27:28Z</published>    <summary type="html">&lt;div&gt;&lt;p&gt;Lately, Liferay has been following a trend toward externalizing extensibility features as much as possible. The traditional EXT model, while powerful, often proves too complex for many situations, or too intrusive to the product to retrain a high level of maintainability and a comfortable upgrade path because by it's very nature it inadvertently promotes bad programming practices which can easily introduce difficult migration issues.&lt;/p&gt; &lt;p&gt;There are still use cases for using the EXT extension model (some less component friendly app servers for example), but the plan is to minimize the need and outright eliminate it wherever possible.&lt;/p&gt; &lt;p&gt;That in mind, the external extensibility features of Liferay have been very un-trendily dubbed &amp;quot;plugins&amp;quot;. Liferay supports 5 different types of plugins out-of-the-box. They are:&lt;/p&gt; &lt;ul&gt;     &lt;li&gt;Portlets&lt;/li&gt;     &lt;li&gt;Themes&lt;/li&gt;     &lt;li&gt;Layout Templates&lt;/li&gt;     &lt;li&gt;Webs&lt;/li&gt;     &lt;li&gt;&lt;span class="b em"&gt;Hooks&lt;/span&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Today I'd like to focus on the newest addition, &lt;span class="b em"&gt;&amp;quot;Hooks&amp;quot;&lt;/span&gt;. Hooks have been Brian Chan's own personal pet project for the last several months. As the name implies they allow &amp;quot;hooking&amp;quot; into Liferay. Specifically they allow you to hook into the eventing system, model listeners, jsps and portal properties. We'll begin by creating a bare hook config file where we will define some hooks as we review the different types&lt;/p&gt; &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;!DOCTYPE hook PUBLIC &amp;quot;-//Liferay//DTD Hook 5.1.0//EN&amp;quot; &amp;quot;http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd&amp;quot;&amp;gt;&amp;lt;hook&amp;gt;&amp;lt;/hook&amp;gt;&lt;/pre&gt; &lt;h3&gt;Event Handlers&lt;/h3&gt; &lt;p&gt;Liferay has a few event handler connection points throughout its lifecycle, designed to allow developers to conveniently hook-in custom logic. The available events are:&lt;/p&gt; &lt;ul&gt;     &lt;li&gt;Application Startup Events (&lt;span class="tt"&gt;application.startup.events&lt;/span&gt;)&lt;/li&gt;     &lt;li&gt;Login Events (&lt;span class="tt"&gt;login.events.pre&lt;/span&gt;, &lt;span class="tt"&gt;login.events.post&lt;/span&gt;)&lt;/li&gt;     &lt;li&gt;Service Events (&lt;span class="tt"&gt;servlet.service.events.pre&lt;/span&gt;, &lt;span class="tt"&gt;servlet.service.events.post&lt;/span&gt;)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Generally speaking your event implementations should extend &lt;span class="tt"&gt;com.liferay.portal.kernel.events.Action&lt;/span&gt;.&lt;/p&gt; &lt;p&gt;For example, presuming we have a custom event handler which should fire when the portal is starting to process any request called &lt;span class="tt"&gt;me.auge.ray.ServicePreAction&lt;/span&gt;, this class placed in the plugin work dir of &lt;span class="tt"&gt;&amp;lt;sdk_root&amp;gt;/hooks/rays-hook/docroot/WEB-INF/src/me/auge/ray/ServicePreAction.java&lt;/span&gt;, I would place the following element in the config file:&lt;/p&gt; &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;!DOCTYPE hook PUBLIC &amp;quot;-//Liferay//DTD Hook 5.1.0//EN&amp;quot; &amp;quot;http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd&amp;quot;&amp;gt;&amp;lt;hook&amp;gt;&lt;span class="new"&gt;	&amp;lt;event&amp;gt;&lt;br /&gt;		&amp;lt;event-class&amp;gt;me.auge.ray.ServicePreAction&amp;lt;/event-class&amp;gt;&lt;br /&gt;		&amp;lt;event-type&amp;gt;servlet.service.events.pre&amp;lt;/event-type&amp;gt;&lt;br /&gt;	&amp;lt;/event&amp;gt;&lt;/span&gt;&amp;lt;/hook&amp;gt;&lt;/pre&gt; &lt;p&gt;A couple points to make about events are that an &lt;span class="tt"&gt;application.startup.events&lt;/span&gt; can only extend &lt;span class="tt"&gt;com.liferay.portal.kernel.events.SimpleAction&lt;/span&gt; as that one expects an array of companyIds for which it should be invoked. The second is that you can define as many implementations as you like for each event type. Simply repeat the event element for each one.&lt;/p&gt; &lt;h3&gt;Model Listeners&lt;/h3&gt; &lt;p&gt;Model listeners are similar in behavior to portal event handlers except that these handle events with respect to models built using ServiceBuilder. These listeners implement the &lt;span class="tt"&gt;com.liferay.portal.model.ModelListener&lt;/span&gt; interface.&lt;/p&gt; &lt;p&gt;If you wanted to listen for new Blog posts, you might have a class called &lt;span class="tt"&gt;me.auge.ray.NewBlogEntryListener&lt;/span&gt; which looked like this:&lt;/p&gt; &lt;pre&gt;package me.auge.ray;import com.liferay.portal.ModelListenerException;import com.liferay.portal.model.BaseModel;import com.liferay.portal.model.ModelListener;import com.liferay.portlet.blogs.model.BlogsEntry;public class NewBlogEntryListener implements ModelListener {&lt;span class="new"&gt;	public void onAfterCreate(BaseModel arg0) throws ModelListenerException {&lt;br /&gt;		BlogsEntry entry = (BlogsEntry)arg0;&lt;br /&gt;		System.out.println(&amp;quot;Woohoo! We got an new one called: &amp;quot; + entry.getTitle());&lt;br /&gt;	}&lt;/span&gt;public void onAfterRemove(BaseModel arg0) throws ModelListenerException {}public void onAfterUpdate(BaseModel arg0) throws ModelListenerException {}public void onBeforeCreate(BaseModel arg0) throws ModelListenerException {}public void onBeforeRemove(BaseModel arg0) throws ModelListenerException {}public void onBeforeUpdate(BaseModel arg0) throws ModelListenerException {}}&lt;/pre&gt; &lt;p&gt;You'd configure it like so:&lt;/p&gt; &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;!DOCTYPE hook PUBLIC &amp;quot;-//Liferay//DTD Hook 5.1.0//EN&amp;quot; &amp;quot;http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd&amp;quot;&amp;gt;&amp;lt;hook&amp;gt;&amp;lt;event&amp;gt;&amp;lt;event-class&amp;gt;me.auge.ray.ServicePreAction&amp;lt;/event-class&amp;gt;&amp;lt;event-type&amp;gt;servlet.service.events.pre&amp;lt;/event-type&amp;gt;&amp;lt;/event&amp;gt;&lt;span class="new"&gt;	&amp;lt;model-listener&amp;gt;&lt;br /&gt;		&amp;lt;model-listener-class&amp;gt;me.auge.ray.NewBlogEntryListener&amp;lt;/model-listener-class&amp;gt;&lt;br /&gt;		&amp;lt;model-name&amp;gt;com.liferay.portlet.blogs.model.BlogsEntry&amp;lt;/model-name&amp;gt;&lt;br /&gt;	&amp;lt;/model-listener&amp;gt;&lt;/span&gt;&amp;lt;/hook&amp;gt;&lt;/pre&gt; &lt;p&gt;As with events, add as many as you like even per model.&lt;/p&gt; &lt;h3&gt;JSPs&lt;/h3&gt; &lt;p&gt;One of the biggest aspects of implementing the portal is of course customization of the user experience. This largely involves modifying portal jsps. The problem is of course migration from version to version where you may end up with code squew, code management, version management, and many other issues. JSP hooks are designed to aleviate some of those issues by providing a way for SI's to easily modify jsps without having to alter the core. Simply specify a folder in the hook plugin from which to obtain jsp files and the portal will automatically use those in place of existing ones in the portal. This works for any jsps in the portal, portlets, servlets, and tags. All you need to do is make sure that you follow the same folder structure off your specified folder.&lt;/p&gt; &lt;p&gt;For example if you specify the folder &lt;span class="tt"&gt;/WEB-INF/jsps&lt;/span&gt;, the changing the view for the blogs portlet would require a file in &lt;span class="tt"&gt;/WEB-INF/jsps/html/portlet/blogs/view.jsp&lt;/span&gt;. Configuration would look like this:&lt;/p&gt; &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;!DOCTYPE hook PUBLIC &amp;quot;-//Liferay//DTD Hook 5.1.0//EN&amp;quot; &amp;quot;http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd&amp;quot;&amp;gt;&amp;lt;hook&amp;gt;&amp;lt;event&amp;gt;&amp;lt;event-class&amp;gt;me.auge.ray.ServicePreAction&amp;lt;/event-class&amp;gt;&amp;lt;event-type&amp;gt;servlet.service.events.pre&amp;lt;/event-type&amp;gt;&amp;lt;/event&amp;gt;&amp;lt;model-listener&amp;gt;&amp;lt;model-listener-class&amp;gt;me.auge.ray.NewBlogEntryListener&amp;lt;/model-listener-class&amp;gt;&amp;lt;model-name&amp;gt;com.liferay.portlet.blogs.model.BlogsEntry&amp;lt;/model-name&amp;gt;&amp;lt;/model-listener&amp;gt;&lt;span class="new"&gt;	&amp;lt;custom-jsp-dir&amp;gt;/WEB-INF/jsps&amp;lt;/custom-jsp-dir&amp;gt;&lt;/span&gt;&amp;lt;/hook&amp;gt;&lt;/pre&gt; &lt;h3&gt;Portal Properties&lt;/h3&gt; &lt;p&gt;We can alter the portal's configuration properties by specifying an override file. The properties in this file will immediately take effect when deployed thus allowing runtime re-configuration of the portal.&lt;/p&gt; &lt;p&gt;If you had a file &lt;span class="tt"&gt;/WEB-INF/src/portal.properties&lt;/span&gt;, the configuration would look like:&lt;/p&gt; &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;!DOCTYPE hook PUBLIC &amp;quot;-//Liferay//DTD Hook 5.1.0//EN&amp;quot; &amp;quot;http://www.liferay.com/dtd/liferay-hook_5_1_0.dtd&amp;quot;&amp;gt;&amp;lt;hook&amp;gt;&amp;lt;event&amp;gt;&amp;lt;event-class&amp;gt;me.auge.ray.ServicePreAction&amp;lt;/event-class&amp;gt;&amp;lt;event-type&amp;gt;servlet.service.events.pre&amp;lt;/event-type&amp;gt;&amp;lt;/event&amp;gt;&amp;lt;model-listener&amp;gt;&amp;lt;model-listener-class&amp;gt;me.auge.ray.NewBlogEntryListener&amp;lt;/model-listener-class&amp;gt;&amp;lt;model-name&amp;gt;com.liferay.portlet.blogs.model.BlogsEntry&amp;lt;/model-name&amp;gt;&amp;lt;/model-listener&amp;gt;&lt;span class="new"&gt;	&amp;lt;portal-properties&amp;gt;portal.properties&amp;lt;/portal-properties&amp;gt;&lt;/span&gt;&amp;lt;custom-jsp-dir&amp;gt;/WEB-INF/jsps&amp;lt;/custom-jsp-dir&amp;gt;&amp;lt;/hook&amp;gt;&lt;/pre&gt; &lt;p&gt;Finally, all of the above hooks will immediately revert their targetted functionality as soon as they are undeployed from the portal. Also, each type of hook can easily be disabled via portal.properties (Note that if properties hook is disabled, a hook cannot be used to re-enable it). Hooks can be built, packaged, and deployed, like other plugins, using the Liferay plugins SDK.&lt;/p&gt; &lt;style type="text/css"&gt;.post-body {}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 300px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-10-07T17:27:28Z</dc:date>  </entry>  <entry>    <title>The New Liferay Permission Algorithm (a.k.a. 5, a.k.a RBAC)</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/1339340" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-09-23T19:36:16Z</updated>    <published>2008-09-23T19:36:16Z</published>    <summary type="html">&lt;p&gt;Well, it's been almost 2 months since we introduced a new permission checking algorithm into the portal. The key features of the new algorithm are:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;increased speed of evaluation&lt;/li&gt;&lt;li&gt;simpler usage&lt;/li&gt;&lt;li&gt;increased performance&lt;/li&gt;&lt;li&gt;increased speed of evaluation&lt;/li&gt;&lt;li&gt;and lastly increased speed of evaluation and increased performance&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;So, how did we acheive this and what did we have to sacrific to get it?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;With the old default algorithm (a.k.a. 2) we had all kinds of objects to which we could assign permissions; users, groups, roles, orgs, user groups. While this sounds great, it really isn't for several reasons.&lt;/p&gt;&lt;p&gt;1) Having this many objects to which we can assign permissions means that evaluating whether a user has a particular permission on some entity incurs a check on all those objects. Not to mention the fact that some of those objects support inheritence. This lead to some very complex and expensive JOIN queries.&lt;/p&gt;&lt;p&gt;2) The simple fact that EVERY User could have permissions on a given entity meant that we had to define defaults on these entities. This would lead to situations where a user would visit a portal page which contained entities they had never before encountered and suddenly there would be a tone of DB interactions to create these default permissions for these new objects. Imagine a Message Board page with 20 posts happening over night, morning comes and traffic increases to say 100 users (user who had not encountered the 20 posts before) per minute. That is &lt;code&gt;100 * 20 / minute&lt;/code&gt; &lt;b&gt;new&lt;/b&gt; objects being created. This lead to hundreds of DB interactions per second on some highly dynamic sites.&lt;/p&gt;&lt;p&gt;3) Managing permissions on so many different objects was difficult at best, and utterly confusing a worst.&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;span style="font-size: small;"&gt;The solution&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;What we did was implement a system based on the &lt;a href="http://en.wikipedia.org/wiki/Role-based_access_control"&gt;Roles Based Access Control (RBAC)&lt;/a&gt; paradigm. We had the foundation for such a system in place, we simply had to reduce the number of objects to which we could assign permissions to only one; Role. This allowed us to perform shorter, faster queries at evaluation time with many fewer JOINS. Also, since we eliminated the assignment of Permissions to User objects we no longer had to create defaults for Users encountering new entities. This increased the concurrent load the portal could handle by a huge factor.&lt;/p&gt;&lt;p&gt;For a short while after the initial system was in place we realized that we had overlooked one key issue, &amp;quot;ownership&amp;quot;.&lt;/p&gt;&lt;p&gt;Because User is not assigned permissions, how can we define the permissions granted to the original creator? Well, it took a while to discover a flexible enough solution that would not lead us back down a patch which would cause us to lose our recent performance increase. We definitely did not want to go back to one to one association of permissions to Users.&lt;/p&gt;&lt;p&gt;The solution came in the form of an &amp;quot;implied Role&amp;quot;. The &amp;quot;implied&amp;quot; meaning that this Role, though it is a Role like any other, can't be assigned to anything, can't be assigned too, rather it is the result of &amp;quot;state&amp;quot;, when a new object is created the default permissions normally associated with the User object are associated with the implied &amp;quot;Owner&amp;quot; Role. Then, on objects which are &amp;quot;owned&amp;quot; (meaning they have a userId field, like a Message Board Message, Blog Entry, Bookmarks Entry, Journal Article, etc.) we first check if the current User is the owner of the object. If so, that user inherites the &amp;quot;Owner&amp;quot; Role. Now, since the &amp;quot;Owner&amp;quot; role is actually a real role, an administrator can manage permissions associated with the Owner Role for any object, by the normal means.&lt;/p&gt;&lt;p&gt;Other cases where we want to customize permissions specifically for a give user or set of users, or even with User Groups or Orgs, we can do this through Roles and then assign those. We lose non of the capability we had before, we increased the performance of the portal significantly, and also made permissions management far easer.&lt;/p&gt;&lt;p&gt;Also, since we only have one type of object on which we can assign permissions, it's easier to map onto external autorization systems, because most of those are already RBAC based, like LDAP.&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-09-23T19:36:16Z</dc:date>  </entry>  <entry>    <title>Calling Liferay Services from XSL Journal Templates</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/calling-liferay-services-from-xsl-journal-templates" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-09-23T19:32:13Z</updated>    <published>2008-09-23T19:32:13Z</published>    <summary type="html">&lt;div&gt;&lt;p&gt;Recently I was again asked how to call a more complex Liferay service using XSL.&lt;/p&gt;&lt;p&gt;Here is an example of getting CalEvents from XSL and printing the list. The first thing you might find is that the iteration is rather strange. That's because XSL has no notion of &lt;span class="tt"&gt;arrays&lt;/span&gt; or &lt;span class="tt"&gt;lists&lt;/span&gt;, other than nodelists that is. So we have to get around that by creating a template construct to represent our loop logic.&lt;/p&gt;&lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&amp;lt;xsl:stylesheet version=&amp;quot;1.0&amp;quot; xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot; xmlns:getterUtil=&amp;quot;xalan://com.liferay.portal.kernel.util.GetterUtil&amp;quot;xmlns:java=&amp;quot;http://xml.apache.org/xalan/java&amp;quot;xmlns:calEventLocalServiceUtil=&amp;quot;xalan://com.liferay.portlet.calendar.service.CalEventLocalServiceUtil&amp;quot;xmlns:calFactoryUtil=&amp;quot;xalan://com.liferay.portal.kernel.util.CalendarFactoryUtil&amp;quot;xmlns:userLocalServiceUtil=&amp;quot;xalan://com.liferay.portal.service.UserLocalServiceUtil&amp;quot;exclude-result-prefixes=&amp;quot;java&amp;quot; extension-element-prefixes=&amp;quot;getterUtil userLocalServiceUtil&amp;quot;&amp;gt;&amp;lt;xsl:output method=&amp;quot;html&amp;quot; omit-xml-declaration=&amp;quot;yes&amp;quot;/&amp;gt;&amp;lt;xsl:param name=&amp;quot;groupId&amp;quot; /&amp;gt;&amp;lt;xsl:param name=&amp;quot;locale&amp;quot; /&amp;gt;&amp;lt;xsl:template match=&amp;quot;/&amp;quot;&amp;gt;&amp;lt;xsl:variable name=&amp;quot;remote-user&amp;quot; select=&amp;quot;/root/request/remote-user&amp;quot; /&amp;gt;&amp;lt;xsl:text&amp;gt;Remote User: &amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;$remote-user&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;separator&amp;quot;&amp;gt;&amp;lt;!--//--&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;xsl:choose&amp;gt;&amp;lt;xsl:when test=&amp;quot;$remote-user != ''&amp;quot;&amp;gt;&amp;lt;xsl:variable name=&amp;quot;user&amp;quot; select=&amp;quot;userLocalServiceUtil:getUserById(getterUtil:getLong($remote-user))&amp;quot; /&amp;gt;&amp;lt;xsl:variable name=&amp;quot;timeZone&amp;quot; select=&amp;quot;java:getTimeZone($user)&amp;quot; /&amp;gt;&amp;lt;xsl:variable name=&amp;quot;calendar&amp;quot; select=&amp;quot;calFactoryUtil:getCalendar($timeZone, $locale)&amp;quot; /&amp;gt;&amp;lt;xsl:variable name=&amp;quot;events&amp;quot; select=&amp;quot;calEventLocalServiceUtil:getEvents($groupId, $calendar)&amp;quot; /&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;java:size($events)&amp;quot; /&amp;gt;&amp;lt;xsl:text&amp;gt; events were found.&amp;lt;/xsl:text&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;xsl:call-template name=&amp;quot;for.loop&amp;quot;&amp;gt;&amp;lt;xsl:with-param name=&amp;quot;i&amp;quot; select=&amp;quot;'0'&amp;quot; /&amp;gt;&amp;lt;xsl:with-param name=&amp;quot;count&amp;quot; select=&amp;quot;java:size($events)&amp;quot; /&amp;gt;&amp;lt;xsl:with-param name=&amp;quot;list&amp;quot; select=&amp;quot;$events&amp;quot; /&amp;gt;&amp;lt;/xsl:call-template&amp;gt;&amp;lt;/xsl:when&amp;gt;&amp;lt;xsl:otherwise&amp;gt;&amp;lt;xsl:text&amp;gt;Hello! Please log in.&amp;lt;/xsl:text&amp;gt;&amp;lt;/xsl:otherwise&amp;gt;&amp;lt;/xsl:choose&amp;gt;&amp;lt;/xsl:template&amp;gt;&amp;lt;!-- This is what you customize to tailor your output per item. --&amp;gt;&amp;lt;xsl:template name=&amp;quot;do.item&amp;quot;&amp;gt;&amp;lt;xsl:param name=&amp;quot;i&amp;quot; /&amp;gt;&amp;lt;!-- 'item' is an object in the list --&amp;gt;&amp;lt;xsl:param name=&amp;quot;item&amp;quot; /&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;java:getTitle($item)&amp;quot; /&amp;gt;&amp;lt;br/&amp;gt; 	 &amp;lt;/xsl:template&amp;gt;&amp;lt;!-- Don't touch bellow code. --&amp;gt;&amp;lt;xsl:template name=&amp;quot;for.loop&amp;quot;&amp;gt;&amp;lt;xsl:param name=&amp;quot;i&amp;quot; /&amp;gt;&amp;lt;xsl:param name=&amp;quot;count&amp;quot; /&amp;gt;&amp;lt;xsl:param name=&amp;quot;list&amp;quot; /&amp;gt;&amp;lt;xsl:if test=&amp;quot;$i &amp;amp;lt; $count&amp;quot;&amp;gt;  	 	 	&amp;lt;xsl:call-template name=&amp;quot;do.item&amp;quot;&amp;gt; 	 	 	 	&amp;lt;xsl:with-param name=&amp;quot;i&amp;quot; select=&amp;quot;$i&amp;quot; /&amp;gt; 	 	 	 	&amp;lt;xsl:with-param name=&amp;quot;item&amp;quot; select=&amp;quot;java:get($list, $i)&amp;quot; /&amp;gt; 	 	 	&amp;lt;/xsl:call-template&amp;gt;&amp;lt;/xsl:if&amp;gt;  		&amp;lt;xsl:if test=&amp;quot;$i &amp;amp;lt; $count&amp;quot;&amp;gt;  	 	 	&amp;lt;xsl:call-template name=&amp;quot;for.loop&amp;quot;&amp;gt; 	 	 	 	&amp;lt;xsl:with-param name=&amp;quot;i&amp;quot; select=&amp;quot;$i + 1&amp;quot; /&amp;gt;  	 	 	 	&amp;lt;xsl:with-param name=&amp;quot;count&amp;quot; select=&amp;quot;$count&amp;quot; /&amp;gt;  	 	 	 	&amp;lt;xsl:with-param name=&amp;quot;list&amp;quot; select=&amp;quot;$list&amp;quot; /&amp;gt; 	 	 	&amp;lt;/xsl:call-template&amp;gt; 	 	 &amp;lt;/xsl:if&amp;gt; 	 &amp;lt;/xsl:template&amp;gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/pre&gt;&lt;p&gt;There is no need to change the &lt;span class="tt b"&gt;for.loop&lt;/span&gt; template. Only change the &lt;span class="tt b"&gt;do.item&lt;/span&gt; template to operate on your list item. The &lt;span class="tt b"&gt;for.loop&lt;/span&gt; can be re-used and in fact could be placed in a utility template and included rather than embedded.&lt;/p&gt;&lt;p&gt;Another thing you might ask is where did &lt;span class="tt b"&gt;groupId&lt;/span&gt; and &lt;span class="tt b"&gt;locale&lt;/span&gt; parameters come from. Well, those are two params automatically included in all our Journal XSL templates. You can just use them.&lt;/p&gt;&lt;style type="text/css"&gt;.post-body {}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 300px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-09-23T19:32:13Z</dc:date>  </entry>  <entry>    <title>Journal VM Template meets SAXReaderUtil</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/journal-vm-template-meets-saxreaderutil" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-09-12T05:36:36Z</updated>    <published>2008-09-12T05:36:36Z</published>    <summary type="html">&lt;div&gt;&lt;p&gt;Recently my friend &lt;span class="b tt"&gt;Journal VM Template&lt;/span&gt; was feeling a little down in the dumps because of his lack of ability when it comes to handling XML content. He was feeling inferior to &lt;span class="tt"&gt;Journal XSL Template&lt;/span&gt; because of this one's superlative ability in dealing with any type of XML content, local and remote.&lt;/p&gt;  &lt;p&gt;Not liking to see my friend down in the dumps, because he really has many other likeable features, I decided that it was time for an intervention. I got together with a new friend I'd recently started working with, &lt;span class="b tt"&gt;SAXReaderUtil&lt;/span&gt;. &lt;span class="tt"&gt;SAXReaderUtil&lt;/span&gt; is a new and upcomming character around the Liferay codebase, and has tremendous skill when if comes to dealing with XML content.&lt;/p&gt;  &lt;p&gt;He can easily get XML from Strings, Files, java.net.URLs, java.io.Readers, InputStreams, and most recently from a plain old String URLs. Not only that, but he can get you Documents with one line, can handle XPATH, do Node sorting, etc.. all in very little code.&lt;/p&gt;  &lt;p&gt;Not only that, but all his buddies, Element, Node, and Branch (because he's a relative of dom4j) are all equally great to work with and very skilled.&lt;/p&gt;  &lt;p&gt;Well, I'll show some code in a minute, but what I really wanted to tell you was that, I introduced &lt;span class="tt"&gt;SAXReaderUtil&lt;/span&gt; to &lt;span class="b tt"&gt;Journal VM Template&lt;/span&gt; and they are getting along very well. So well, in fact, that I'm itching to show you some of the cool stuff these two can do now that they are working together.&lt;/p&gt;  &lt;p&gt;Ok, so let's make a new Document from scratch:&lt;/p&gt;  &lt;pre&gt;#set ($document = $saxReaderUtil.read(&amp;quot;&amp;lt;friends/&amp;gt;&amp;quot;))#set ($root = $document.getRootElement())#set ($friends = [&amp;quot;Journal VM Template&amp;quot;, &amp;quot;SAXReaderUtil&amp;quot;, &amp;quot;Document&amp;quot;, &amp;quot;Element&amp;quot;, &amp;quot;Branch&amp;quot;, &amp;quot;Node&amp;quot;])#foreach ($friend in $friends)  #set ($friendEl = $root.addElement(&amp;quot;friend&amp;quot;))  #set ($friendEl = $friendEl.addText($friend))#end&amp;lt;pre&amp;gt;$htmlUtil.escape($document.asXML())&amp;lt;/pre&amp;gt;&lt;/pre&gt;  &lt;p&gt;The output should look something like this (but not pretty printed, I did that):&lt;/p&gt;  &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;friends&amp;gt;  &amp;lt;friend&amp;gt;Journal VM Template&amp;lt;/friend&amp;gt;  &amp;lt;friend&amp;gt;SAXReaderUtil&amp;lt;/friend&amp;gt;  &amp;lt;friend&amp;gt;Document&amp;lt;/friend&amp;gt;  &amp;lt;friend&amp;gt;Element&amp;lt;/friend&amp;gt;  &amp;lt;friend&amp;gt;Branch&amp;lt;/friend&amp;gt;  &amp;lt;friend&amp;gt;Node&amp;lt;/friend&amp;gt;&amp;lt;/friends&amp;gt;&lt;/pre&gt;  &lt;p&gt;Cool eh?&lt;/p&gt;  &lt;p&gt;Ok, now suppose we mocked up a whole doc full of content and we want to provide it to some external client, an RSS reader, as a response to an AJAX call, etc.&lt;/p&gt;  &lt;p&gt;First off, make sure that the article using the template is on a public page (so it's not gonna give you permission problems to start with). Next, lets add a little bit more code to make this content available as a regular old feed.&lt;/p&gt;  &lt;pre&gt;#set ($document = $saxReaderUtil.read(&amp;quot;&amp;lt;friends/&amp;gt;&amp;quot;))#set ($root = $document.getRootElement())#set ($friends = [&amp;quot;Journal VM Template&amp;quot;, &amp;quot;SAXReaderUtil&amp;quot;, &amp;quot;Document&amp;quot;, &amp;quot;Element&amp;quot;, &amp;quot;Branch&amp;quot;, &amp;quot;Node&amp;quot;])#foreach ($friend in $friends)  #set ($friendEl = $root.addElement(&amp;quot;friend&amp;quot;))  #set ($friendEl = $friendEl.addText($friend))#end&lt;span class="new"&gt;#if ($request.window-state != &amp;quot;exclusive&amp;quot;)&lt;/span&gt;  &amp;lt;pre&amp;gt;$htmlUtil.escape($document.asXML())&amp;lt;/pre&amp;gt;  &lt;span class="new"&gt;&amp;lt;a href=&amp;quot;${request.render-url-exclusive}&amp;quot;&amp;gt;FEED ME&amp;lt;/a&amp;gt;&lt;br /&gt;#else&lt;br /&gt;$document.asXML()&lt;br /&gt;#end&lt;/span&gt;&lt;/pre&gt;  &lt;p&gt;Now, click the &amp;quot;FEED ME&amp;quot; link and see what you get... (view source...)&lt;/p&gt;  &lt;img alt="" src="http://cdn.www.liferay.com/image/image_gallery?uuid=3ee16502-969e-451a-aef6-97ff77c3e22e&amp;amp;groupId=10529&amp;amp;t=1221190553894" /&gt;  &lt;p&gt;That's right.. Whoop! Whoop!! Who's your Daddy???&lt;/p&gt;  &lt;p&gt;That effectively turns your regular old templates into a relatively simple service end point. Tie that together with the ability to call Liferay's underlying services and you can publish any kind of feed you like... could even provide reports for some remote system which process some of your data blah blah... you get the idea.&lt;/p&gt;  &lt;p&gt;But what if you want the template to be a consumer of some data in XML and produce a view of that data. A common scenario is a custom Journal Article list. Let's write some code to do that.&lt;/p&gt;  &lt;pre&gt;#set ($document = $saxReaderUtil.readURL(&amp;quot;http://localhost:8080/lportal/c/journal/get_articles?groupId=14&amp;amp;delta=1&amp;quot;))&amp;lt;textarea style=&amp;quot;height: 800px; width: 500px;&amp;quot;&amp;gt;$document.asXML()&amp;lt;/textarea&amp;gt;&lt;/pre&gt;  &lt;p&gt;So, I'm making an API call to the backend Journal API to get the most recent article (&lt;span class="tt"&gt;delta=1&lt;/span&gt;) updated in the community with &lt;span class="tt"&gt;groupId=14&lt;/span&gt;. So first off, it's worth noting that it was easy to make the call, we just used the &lt;span class="tt"&gt;readURL&lt;/span&gt; method. Next, we're just dumping the contents into a textarea. This is the first step to see if we have good data to work with. It should look something like this.&lt;/p&gt;  &lt;img alt="" src="http://cdn.www.liferay.com/image/image_gallery?uuid=e156d7d0-33fa-46bb-bdd2-0d87a769d310&amp;amp;groupId=10529&amp;amp;t=1221193657233" /&gt;  &lt;p&gt;Ok, so we were talking about our new friend SAXReaderUtil. It seems he's doing a fine job so far. Let's see what other tricks him and his buddies provide.&lt;/p&gt;  &lt;p&gt;Let's get the list of articles using XPATH (of course we have one... but you'd likely have more right?).&lt;/p&gt;  &lt;pre&gt;#set ($document = $saxReaderUtil.readURL(&amp;quot;http://localhost:8080/lportal/c/journal/get_articles?groupId=14&amp;amp;delta=1&amp;quot;))#set ($root = $document.getRootElement())#set ($articles = $root.selectNodes(&amp;quot;/result-set/result/root&amp;quot;))&amp;lt;textarea style=&amp;quot;height: 800px; width: 500px;&amp;quot;&amp;gt;$articles.get(0).asXML()&amp;lt;/textarea&amp;gt;&lt;/pre&gt;  &lt;img alt="" src="http://cdn.www.liferay.com/image/image_gallery?uuid=42df3716-312e-46f1-9048-8390eaf0b3f3&amp;amp;groupId=10529&amp;amp;t=1221194748264" /&gt;  &lt;p&gt;Now we know our list is right, we can iterate through each article and get some details about each one.&lt;/p&gt;  &lt;pre&gt;#set ($document = $saxReaderUtil.readURL(&amp;quot;http://localhost:8080/lportal/c/journal/get_articles?groupId=14&amp;amp;delta=1&amp;quot;))#set ($root = $document.getRootElement())#set ($articles = $root.selectNodes(&amp;quot;/result-set/result/root&amp;quot;))&lt;span class="new"&gt;&amp;lt;ul&amp;gt;&lt;br /&gt;#foreach ($article IN $articles)&lt;br /&gt;  #set ($articleId = $article.selectSingleNode(&amp;quot;dynamic-element[@name='reserved-article-id']/dynamic-content&amp;quot;))&lt;br /&gt;  #set ($articleTitle = $article.selectSingleNode(&amp;quot;dynamic-element[@name='reserved-article-title']/dynamic-content&amp;quot;))&lt;br /&gt;  #set ($articleModifiedDate = $article.selectSingleNode(&amp;quot;dynamic-element[@name='reserved-article-modified-date']/dynamic-content&amp;quot;))&lt;br /&gt;  #set ($articleAuthorName = $article.selectSingleNode(&amp;quot;dynamic-element[@name='reserved-article-author-name']/dynamic-content&amp;quot;))&lt;br /&gt;  &amp;lt;li&amp;gt;&lt;br /&gt;    &amp;lt;a href=&amp;quot;${request.render-url-maximized}${request.portlet-namespace}articleId=${articleId.text}&amp;quot;&amp;gt;${articleTitle.text}&amp;lt;/a&amp;gt;&lt;br /&gt;    &amp;lt;br/&amp;gt;&lt;br /&gt;    &amp;lt;span style=&amp;quot;font-size: smaller;&amp;quot;&amp;gt;${articleAuthorName.text}, ${articleModifiedDate.text}&amp;lt;/span&amp;gt;&lt;br /&gt;#end&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;/pre&gt;  &lt;p&gt;What we get is this:&lt;/p&gt;  &lt;img alt="" src="http://cdn.www.liferay.com/image/image_gallery?uuid=222fb8e1-f0fe-4e46-bf38-b9c522df227e&amp;amp;groupId=10529&amp;amp;t=1221195850133" /&gt;  &lt;p&gt;Ok, we're only showing one... how does it look with more. All we do is modify the API query by setting the &lt;span class="tt"&gt;delta&lt;/span&gt; parameter to something higher. Let's pick 4.&lt;/p&gt;  &lt;img alt="" src="http://cdn.www.liferay.com/image/image_gallery?uuid=567dfe58-d755-44b9-9674-2a5dcc5624f2&amp;amp;groupId=10529&amp;amp;t=1221196584489" /&gt;  &lt;p&gt;Now notice that the URL's actually work, placing the given article in Maximized mode. You can choose to not go maximized if you like (use &lt;span class="tt"&gt;${request.render-url}&lt;/span&gt; instead of &lt;span class="tt"&gt;${request.render-url-maximized}&lt;/span&gt;. And if you use a friendly URL you can target another portlet on the page completely, using the traditional &amp;quot;narrow-side-nav-links vs. wide-side-view-port&amp;quot; model.&lt;/p&gt;  &lt;p&gt;Anyway, It's pretty impressive what you can accomplish when friends work together. I'd suggest visiting with &lt;span class="b tt"&gt;Journal VM Template&lt;/span&gt; and &lt;span class="b tt"&gt;SAXReaderUtil &amp;amp; Co.&lt;/span&gt; as soon as you can take Liferay 5.1.2 for a spin (5.1.x until the official release). All my friends are enjoying their new acquaintances. I hope you enjoy them too.&lt;/p&gt;  &lt;style type="text/css"&gt;.post-body {}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 300px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-09-12T05:36:36Z</dc:date>  </entry>  <entry>    <title>Calling Java (tools|services) from XSL Journal Templates</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/1266259" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-09-04T02:28:31Z</updated>    <published>2008-09-04T02:28:31Z</published>    <summary type="html">&lt;div&gt;&lt;p&gt;Recently a coleague asked if it was possible to call java from XSL.&lt;/p&gt;  &lt;p&gt;It is possible, and here is the example I provided.&lt;/p&gt;  &lt;pre&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&amp;lt;xsl:stylesheet version=&amp;quot;1.0&amp;quot; xmlns:xsl=&amp;quot;http://www.w3.org/1999/XSL/Transform&amp;quot; xmlns:getterUtil=&amp;quot;xalan://com.liferay.portal.kernel.util.GetterUtil&amp;quot;xmlns:java=&amp;quot;http://xml.apache.org/xalan/java&amp;quot;xmlns:userLocalServiceUtil=&amp;quot;xalan://com.liferay.portal.service.UserLocalServiceUtil&amp;quot;exclude-result-prefixes=&amp;quot;java&amp;quot; extension-element-prefixes=&amp;quot;getterUtil userLocalServiceUtil&amp;quot;&amp;gt;&amp;lt;xsl:output method=&amp;quot;html&amp;quot; omit-xml-declaration=&amp;quot;yes&amp;quot;/&amp;gt;&amp;lt;xsl:template match=&amp;quot;/&amp;quot;&amp;gt;&amp;lt;xsl:variable name=&amp;quot;remote-user&amp;quot; select=&amp;quot;/root/request/remote-user&amp;quot; /&amp;gt;&amp;lt;xsl:text&amp;gt;Remote User: &amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;$remote-user&amp;quot; /&amp;gt;&amp;lt;div class=&amp;quot;separator&amp;quot;&amp;gt;&amp;lt;!--//--&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;xsl:choose&amp;gt;&amp;lt;xsl:when test=&amp;quot;$remote-user != ''&amp;quot;&amp;gt;&amp;lt;xsl:variable name=&amp;quot;user&amp;quot; select=&amp;quot;userLocalServiceUtil:getUserById(getterUtil:getLong($remote-user))&amp;quot; /&amp;gt;&amp;lt;xsl:text&amp;gt;Hello &amp;lt;/xsl:text&amp;gt;&amp;lt;xsl:value-of select=&amp;quot;java:getFullName($user)&amp;quot; /&amp;gt;&amp;lt;xsl:text&amp;gt;!&amp;lt;/xsl:text&amp;gt;&amp;lt;/xsl:when&amp;gt;&amp;lt;xsl:otherwise&amp;gt;&amp;lt;xsl:text&amp;gt;Hello! Please log in.&amp;lt;/xsl:text&amp;gt;&amp;lt;/xsl:otherwise&amp;gt;&amp;lt;/xsl:choose&amp;gt;&amp;lt;/xsl:template&amp;gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/pre&gt;  VM (VTL) is great and all, but when it comes to dealing with XML data, you simply can't beat the power of XSL. Add to that the power of inline java and you can do some nifty stuff.  &lt;style type="text/css"&gt;.post-body {}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 300px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-09-04T02:28:31Z</dc:date>  </entry>  <entry>    <title>Liferay Portlets as Standalone Desktop Apps??? OOTB</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/1237696" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-08-28T19:29:37Z</updated>    <published>2008-08-28T19:29:37Z</published>    <summary type="html">&lt;p&gt;In Liferay we have this cool new feature to run portlets as embedded widgets from any webpage anywhere.&lt;/p&gt;&lt;p&gt;Tired of loosing you chat sessions on every page refresh???&lt;/p&gt;&lt;p&gt;No more!!!&lt;/p&gt;&lt;p&gt;Well feast your eyes on Liferay Portls running as APPS on your desktop. Completely standalone, no shared memory, if one process crashes the others will not.&lt;/p&gt;&lt;p&gt;Take a look at these babies.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Chat Portlet:&lt;/p&gt;&lt;p&gt;&lt;img height="461" width="729" src="http://cdn.www.liferay.com/image/image_gallery?uuid=43158920-325a-46d2-a9e6-c89621632abd&amp;amp;groupId=10529&amp;amp;t=1219951978226" lt="Caht Portlet" alt="" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Message Boards:&lt;/p&gt;&lt;p&gt;&lt;img height="581" width="754" src="http://cdn.www.liferay.com/image/image_gallery?uuid=9925c26d-6088-48c7-90d3-94dc5ccf3cab&amp;amp;groupId=10529&amp;amp;t=1219951990369" alt="Message Boards" /&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Zimbra eat your heart out... Every portlet build on Liferay automatically becomes your latest Desktop App using none other than the Prism Mozilla project.&lt;/p&gt;&lt;p&gt;Check it out at &lt;a href="https://wiki.mozilla.org/WebRunner"&gt;http://wiki.mozilla.org/WebRunner&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;All you need is to grab the Shared &amp;quot;widget URL&amp;quot; we generate for your portlet and setup a Prism link, done!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-08-28T19:29:37Z</dc:date>  </entry>  <entry>    <title>Is your laptop battery this dead?</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/1130223" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-07-30T23:48:22Z</updated>    <published>2008-07-30T23:48:22Z</published>    <summary type="html">&lt;p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=1130221&amp;amp;t=1217461670416" alt="" /&gt;&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-07-30T23:48:22Z</dc:date>  </entry>  <entry>    <title>Sample scripts for the Ruby Console portlet [1]</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/sample-scripts-for-the-ruby-console-portlet-[1]" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-07-09T16:19:18Z</updated>    <published>2008-07-09T16:19:18Z</published>    <summary type="html">&lt;div&gt;&lt;p&gt;Here is a sample script for the Ruby Console portlet to test various aspects of the Dynamic Query API.&lt;/p&gt;  &lt;pre&gt;import com.liferay.portal.kernel.util.Timeimport java.lang.Systemimport java.util.Datemodule Orm   include_package &amp;quot;com.liferay.portal.kernel.dao.orm&amp;quot;endmodule Model   include_package &amp;quot;com.liferay.portal.model&amp;quot;endmodule Service   include_package &amp;quot;com.liferay.portal.service&amp;quot;end$resourceResponse.setContentType &amp;quot;text/html&amp;quot;out = $resourceResponse.getPortletOutputStreamdate24HoursAgo = Date.new(System.currentTimeMillis - Time::HOUR * 24)dq1 = Orm::DynamicQueryFactoryUtil.forClass(Model::User.java_class)pl1 = Orm::ProjectionFactoryUtil.projectionListpl1.add(Orm::ProjectionFactoryUtil.property(&amp;quot;emailAddress&amp;quot;))pl1.add(Orm::ProjectionFactoryUtil.property(&amp;quot;lastLoginDate&amp;quot;))pl1.add(Orm::ProjectionFactoryUtil.property(&amp;quot;lastLoginIP&amp;quot;))dq1.setProjection(pl1)dq1.add(Orm::PropertyFactoryUtil.forName(&amp;quot;lastLoginDate&amp;quot;).gt(date24HoursAgo))dq1.addOrder(Orm::OrderFactoryUtil.desc(&amp;quot;emailAddress&amp;quot;))result = Service::UserLocalServiceUtil.dynamicQuery(dq1, 0, 20)out.println result.sizeout.println &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;out.println &amp;quot;&amp;lt;table width='100%' border='1'&amp;gt;&amp;quot;result.each do |row|  out.println &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;  row.each do |field|    out.println &amp;quot;&amp;lt;td&amp;gt;#{field}&amp;lt;/td&amp;gt;&amp;quot;  end  out.println &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;endout.println &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;/pre&gt;  &lt;style type="text/css"&gt;.post-body {}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 300px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-07-09T16:19:18Z</dc:date>  </entry>  <entry>    <title>[HOWTO] Personalization - Getting current user attributes</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/[howto]-personalization---getting-current-user-attributes" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-05-16T17:20:43Z</updated>    <published>2008-05-16T17:20:43Z</published>    <summary type="html">&lt;div class="post-body"&gt;&lt;p&gt;Portals are all about personalization, and Liferay is no different.&lt;/p&gt;&lt;p&gt;I mean, there are many, &lt;span class="b"&gt;many&lt;/span&gt; other things involved in portals &lt;em&gt;other&lt;/em&gt; than personalization. But none of them &lt;em&gt;really&lt;/em&gt; make sense without it.&lt;/p&gt;&lt;p&gt;Personalization is the &lt;span class="b"&gt;dressing&lt;/span&gt; that makes your portal salad bearable. So, it has to be pretty good dressing.&lt;/p&gt;&lt;p&gt;Beyond the portal and personalization, we have the reason the portal exists at all: &lt;span class="b"&gt;Portlets&lt;/span&gt;. Portlets are the ingredients needed to make a portal salad worth eating.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;You have your greens (the hard working portlets: E-Commerce, BI, BPM, CMS, etc.).&lt;/li&gt;&lt;li&gt;Then you have your non-green veggies (Email, Forums, Blogs, Wiki, etc.).&lt;/li&gt;&lt;li&gt;Finally you have your crutons and bacon bits, the stuff we know we can live without but just can't bring ourselves to leave out (social networking and other generally superfluous gadgets, widgetry, and such).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Yet it doesn't end there, it's gotta be &lt;span class="b"&gt;MY&lt;/span&gt; salad, not someone else's... and it's gotta have MY name on it too. &lt;span class="em little"&gt;Who wants to eat someone else's food anyway...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;That said, personalization at the portlet level is very important. There are two types of portlet personalization mechanisms available in Liferay.&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span class="em b"&gt;Personalization defined by the Portlet spec(s)&lt;/span&gt; (&lt;span class="em"&gt;low fat, low cal. dressing&lt;/span&gt;)&lt;/li&gt;&lt;li&gt;&lt;span class="em b"&gt;Liferay personalization&lt;/span&gt; (&lt;span class="em"&gt;fatty, rich, tasty dressing&lt;/span&gt;)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Let's go through putting both to use in your Portlet application and then you can decide for yourself which best suites your diet.&lt;/p&gt;&lt;p&gt;&lt;span class="b"&gt;Personalizaton via Portlet spec(s)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Both the 1.0 version and 2.0 (upcomming) versions of the spec define a mechanism called "user-attributes" defined as such:&lt;/p&gt;&lt;p class="quote"&gt;&lt;em&gt;"The deployment descriptor of a portlet application must define the user attribute names the portlets use. The following example shows a section of a deployment descriptor defining a few user attributes:&lt;pre&gt;&amp;lt;portlet-app&amp;gt;...&amp;lt;user-attribute&amp;gt;&amp;lt;description&amp;gt;User Given Name&amp;lt;/description&amp;gt;&amp;lt;name&amp;gt;user.name.given&amp;lt;/name&amp;gt;&amp;lt;/user-attribute&amp;gt;&amp;lt;user-attribute&amp;gt;&amp;lt;description&amp;gt;User Last Name&amp;lt;/description&amp;gt;&amp;lt;name&amp;gt;user.name.family&amp;lt;/name&amp;gt;&amp;lt;/user-attribute&amp;gt;&amp;lt;user-attribute&amp;gt;&amp;lt;description&amp;gt;User eMail&amp;lt;/description&amp;gt;&amp;lt;name&amp;gt;user.home-info.online.email&amp;lt;/name&amp;gt;&amp;lt;/user-attribute&amp;gt;&amp;lt;user-attribute&amp;gt;&amp;lt;description&amp;gt;Company Organization&amp;lt;/description&amp;gt;&amp;lt;name&amp;gt;user.business-info.postal.organization&amp;lt;/name&amp;gt;&amp;lt;/user-attribute&amp;gt;...&amp;lt;portlet-app&amp;gt;&lt;/pre&gt;   A deployer must map the portlet application’s logical user attributes to the corresponding   user attributes offered by the runtime environment. At runtime, the portlet container uses   this mapping to expose user attributes to the portlets of the portlet application. User   attributes of the runtime environment not mapped as part of the deployment process must   not be exposed to portlets."&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="attr"&gt;[JavaTM Portlet Specification, version 1.0, PLT.17.1]&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This means that without having to do anything other than configuration, the portlet is requesting access to the defined user attributes. The list of available attributes can be found in the spec docs.&lt;/p&gt;&lt;p&gt;The next part is of course getting access to those user attributes at runtime.&lt;/p&gt;&lt;p&gt;Here is a complete JSP which handles getting the &lt;span class="tt"&gt;user.name.given&lt;/span&gt; attribute.&lt;/p&gt;&lt;pre&gt;&amp;lt;%@ taglib uri=&amp;quot;http://java.sun.com/portlet&amp;quot; prefix=&amp;quot;portlet&amp;quot; %&amp;gt;&amp;lt;%@ page import=&amp;quot;javax.portlet.PortletRequest&amp;quot;%&amp;gt;&amp;lt;%@ page import=&amp;quot;java.util.Map&amp;quot;%&amp;gt;&amp;lt;portlet:defineObjects /&amp;gt;&amp;lt;%Map userInfo = (Map)renderRequest.getAttribute(PortletRequest.USER_INFO);String givenName = (userInfo != null) ? (String)userInfo.get(&amp;quot;user.name.given&amp;quot;) : &amp;quot;&amp;quot;;%&amp;gt;Given Name: &amp;lt;%= givenName %&amp;gt;&lt;/pre&gt;&lt;p&gt;The result looks something like the following:&lt;/p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=809807&amp;amp;t=1210957073013" alt="[Image 1]" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;That's 100% spec complient and includes the following list of attributes:&lt;/p&gt;&lt;pre&gt;package com.liferay.portlet;public class UserAttributes {// Mandatory Liferay attributespublic static final String LIFERAY_COMPANY_ID = &amp;quot;liferay.company.id&amp;quot;;public static final String LIFERAY_USER_ID = &amp;quot;liferay.user.id&amp;quot;;public static final String USER_NAME_FULL = &amp;quot;user.name.full&amp;quot;;// See page 119 of the JSR 168 specpublic static final String USER_BDATE = &amp;quot;user.bdate&amp;quot;;public static final String USER_GENDER = &amp;quot;user.gender&amp;quot;;public static final String USER_EMPLOYER = &amp;quot;user.employer&amp;quot;;public static final String USER_DEPARTMENT = &amp;quot;user.department&amp;quot;;public static final String USER_JOBTITLE = &amp;quot;user.jobtitle&amp;quot;;public static final String USER_NAME_PREFIX = &amp;quot;user.name.prefix&amp;quot;;public static final String USER_NAME_GIVEN = &amp;quot;user.name.given&amp;quot;;public static final String USER_NAME_FAMILY = &amp;quot;user.name.family&amp;quot;;public static final String USER_NAME_MIDDLE = &amp;quot;user.name.middle&amp;quot;;public static final String USER_NAME_SUFFIX = &amp;quot;user.name.suffix&amp;quot;;public static final String USER_NAME_NICKNAME = &amp;quot;user.name.nickName&amp;quot;;public static final String USER_HOME_INFO_POSTAL_NAME = &amp;quot;user.home-info.postal.name&amp;quot;;public static final String USER_HOME_INFO_POSTAL_STREET = &amp;quot;user.home-info.postal.street&amp;quot;;public static final String USER_HOME_INFO_POSTAL_CITY = &amp;quot;user.home-info.postal.city&amp;quot;;public static final String USER_HOME_INFO_POSTAL_STATEPROV = &amp;quot;user.home-info.postal.stateprov&amp;quot;;public static final String USER_HOME_INFO_POSTAL_POSTALCODE = &amp;quot;user.home-info.postal.postalcode&amp;quot;;public static final String USER_HOME_INFO_POSTAL_COUNTRY = &amp;quot;user.home-info.postal.country&amp;quot;;public static final String USER_HOME_INFO_POSTAL_ORGANIZATION = &amp;quot;user.home-info.postal.organization&amp;quot;;public static final String USER_HOME_INFO_TELECOM_TELEPHONE_INTCODE = &amp;quot;user.home-info.telecom.telephone.intcode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_TELEPHONE_LOCCODE = &amp;quot;user.home-info.telecom.telephone.loccode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_TELEPHONE_NUMBER = &amp;quot;user.home-info.telecom.telephone.number&amp;quot;;public static final String USER_HOME_INFO_TELECOM_TELEPHONE_EXT = &amp;quot;user.home-info.telecom.telephone.ext&amp;quot;;public static final String USER_HOME_INFO_TELECOM_TELEPHONE_COMMENT = &amp;quot;user.home-info.telecom.telephone.comment&amp;quot;;public static final String USER_HOME_INFO_TELECOM_FAX_INTCODE = &amp;quot;user.home-info.telecom.fax.intcode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_FAX_LOCCODE = &amp;quot;user.home-info.telecom.fax.loccode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_FAX_NUMBER = &amp;quot;user.home-info.telecom.fax.number&amp;quot;;public static final String USER_HOME_INFO_TELECOM_FAX_EXT = &amp;quot;user.home-info.telecom.fax.ext&amp;quot;;public static final String USER_HOME_INFO_TELECOM_FAX_COMMENT = &amp;quot;user.home-info.telecom.fax.comment&amp;quot;;public static final String USER_HOME_INFO_TELECOM_MOBILE_INTCODE = &amp;quot;user.home-info.telecom.mobile.intcode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_MOBILE_LOCCODE = &amp;quot;user.home-info.telecom.mobile.loccode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_MOBILE_NUMBER = &amp;quot;user.home-info.telecom.mobile.number&amp;quot;;public static final String USER_HOME_INFO_TELECOM_MOBILE_EXT = &amp;quot;user.home-info.telecom.mobile.ext&amp;quot;;public static final String USER_HOME_INFO_TELECOM_MOBILE_COMMENT = &amp;quot;user.home-info.telecom.mobile.comment&amp;quot;;public static final String USER_HOME_INFO_TELECOM_PAGER_INTCODE = &amp;quot;user.home-info.telecom.pager.intcode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_PAGER_LOCCODE = &amp;quot;user.home-info.telecom.pager.loccode&amp;quot;;public static final String USER_HOME_INFO_TELECOM_PAGER_NUMBER = &amp;quot;user.home-info.telecom.pager.number&amp;quot;;public static final String USER_HOME_INFO_TELECOM_PAGER_EXT = &amp;quot;user.home-info.telecom.pager.ext&amp;quot;;public static final String USER_HOME_INFO_TELECOM_PAGER_COMMENT = &amp;quot;user.home-info.telecom.pager.comment&amp;quot;;public static final String USER_HOME_INFO_ONLINE_EMAIL = &amp;quot;user.home-info.online.email&amp;quot;;public static final String USER_HOME_INFO_ONLINE_URI = &amp;quot;user.home-info.online.uri&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_NAME = &amp;quot;user.business-info.postal.name&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_STREET = &amp;quot;user.business-info.postal.street&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_CITY = &amp;quot;user.business-info.postal.city&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_STATEPROV = &amp;quot;user.business-info.postal.stateprov&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_POSTALCODE = &amp;quot;user.business-info.postal.postalcode&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_COUNTRY = &amp;quot;user.business-info.postal.country&amp;quot;;public static final String USER_BUSINESS_INFO_POSTAL_ORGANIZATION = &amp;quot;user.business-info.postal.organization&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_TELEPHONE_INTCODE = &amp;quot;user.business-info.telecom.telephone.intcode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_TELEPHONE_LOCCODE = &amp;quot;user.business-info.telecom.telephone.loccode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_TELEPHONE_NUMBER = &amp;quot;user.business-info.telecom.telephone.number&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_TELEPHONE_EXT = &amp;quot;user.business-info.telecom.telephone.ext&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_TELEPHONE_COMMENT = &amp;quot;user.business-info.telecom.telephone.comment&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_FAX_INTCODE = &amp;quot;user.business-info.telecom.fax.intcode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_FAX_LOCCODE = &amp;quot;user.business-info.telecom.fax.loccode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_FAX_NUMBER = &amp;quot;user.business-info.telecom.fax.number&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_FAX_EXT = &amp;quot;user.business-info.telecom.fax.ext&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_FAX_COMMENT = &amp;quot;user.business-info.telecom.fax.comment&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_MOBILE_INTCODE = &amp;quot;user.business-info.telecom.mobile.intcode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_MOBILE_LOCCODE = &amp;quot;user.business-info.telecom.mobile.loccode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_MOBILE_NUMBER = &amp;quot;user.business-info.telecom.mobile.number&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_MOBILE_EXT = &amp;quot;user.business-info.telecom.mobile.ext&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_MOBILE_COMMENT = &amp;quot;user.business-info.telecom.mobile.comment&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_PAGER_INTCODE = &amp;quot;user.business-info.telecom.pager.intcode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_PAGER_LOCCODE = &amp;quot;user.business-info.telecom.pager.loccode&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_PAGER_NUMBER = &amp;quot;user.business-info.telecom.pager.number&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_PAGER_EXT = &amp;quot;user.business-info.telecom.pager.ext&amp;quot;;public static final String USER_BUSINESS_INFO_TELECOM_PAGER_COMMENT = &amp;quot;user.business-info.telecom.pager.comment&amp;quot;;public static final String USER_BUSINESS_INFO_ONLINE_EMAIL = &amp;quot;user.business-info.online.email&amp;quot;;public static final String USER_BUSINESS_INFO_ONLINE_URI = &amp;quot;user.business-info.online.uri&amp;quot;;}&lt;/pre&gt;&lt;p&gt;It should be noted that as of right now, we haven't implemented the additonal attributes defined in &lt;span class="tt"&gt;javax.portlet.PortletRequest.P3PUserInfos&lt;/span&gt;, of the current version 2.0 recommendation.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span class="b"&gt;Liferay Personalizaton&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Liferay personalization provides access to pojos which can be interacted with in a much richer fashion than above. It also ties your application to Liferay so, if you plan to target more than just Liferay as plaform, don't use it.&lt;/p&gt;&lt;p&gt;The easiest way to take avantage of Liferay personalization is to use a couple of Liferay's taglibs; more precisely, the &lt;span class="tt"&gt;liferay-theme&lt;/span&gt; and &lt;span class="tt"&gt;liferay-ui&lt;/span&gt; taglibs.&lt;/p&gt;&lt;p&gt;Since these are not included in a deployed portlet by default, add the following property to your &lt;span class="tt"&gt;liferay-plugin-package.properties&lt;/span&gt; file:&lt;/p&gt;&lt;pre&gt;portal.dependency.tlds=\    liferay-ui.tld,\    liferay-theme.tld&lt;/pre&gt;&lt;p&gt;and the following taglib definitions to your &lt;span class="tt"&gt;web.xml&lt;/span&gt;:&lt;/p&gt;&lt;pre&gt;	&amp;lt;taglib&amp;gt;&amp;lt;taglib-uri&amp;gt;http://liferay.com/tld/theme&amp;lt;/taglib-uri&amp;gt;&amp;lt;taglib-location&amp;gt;/WEB-INF/tld/liferay-theme.tld&amp;lt;/taglib-location&amp;gt;&amp;lt;/taglib&amp;gt;&amp;lt;taglib&amp;gt;&amp;lt;taglib-uri&amp;gt;http://liferay.com/tld/ui&amp;lt;/taglib-uri&amp;gt;&amp;lt;taglib-location&amp;gt;/WEB-INF/tld/liferay-ui.tld&amp;lt;/taglib-location&amp;gt;&amp;lt;/taglib&amp;gt;&lt;/pre&gt;&lt;p&gt;Now that we have the right tools available, let's start with the very simplest example that gives us the most bang.&lt;/p&gt;&lt;pre&gt;&amp;lt;%@ taglib uri=&amp;quot;http://java.sun.com/portlet&amp;quot; prefix=&amp;quot;portlet&amp;quot; %&amp;gt;&amp;lt;%@ taglib uri=&amp;quot;http://liferay.com/tld/theme&amp;quot; prefix=&amp;quot;liferay-theme&amp;quot; %&amp;gt;&amp;lt;%@ taglib uri=&amp;quot;http://liferay.com/tld/ui&amp;quot; prefix=&amp;quot;liferay-ui&amp;quot; %&amp;gt;&amp;lt;liferay-theme:defineObjects /&amp;gt;&amp;lt;portlet:defineObjects /&amp;gt;&amp;lt;liferay-ui:user-display userId=&amp;quot;&amp;lt;%= user.getUserId() %&amp;gt;&amp;quot; /&amp;gt;&lt;/pre&gt;&lt;p&gt;The result of the above JSP code is something like the following:&lt;/p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=809812&amp;amp;t=1210957092763" alt="[Image 2]" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Now you might be asking yourself &lt;span class="em"&gt;"Where did that &lt;span class="b"&gt;user&lt;/span&gt; object suddenly come from?"&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The answer is simple! It comes from the &lt;span class="tt"&gt;&amp;lt;liferay-theme:defineObjects /&amp;gt;&lt;/span&gt; tag we used. This tag puts a whole bunch of context sensitive objects into our JSP page context, including the &lt;span class="tt"&gt;com.liferay.portal.model.User&lt;/span&gt; pojo associated with the current user..&lt;/p&gt;&lt;p&gt;The objects that are injected into the pageContext by the &lt;span class="tt"&gt;&amp;lt;liferay-theme:defineObjects /&amp;gt;&lt;/span&gt; tag are:&lt;ul&gt;&lt;li&gt;&lt;span class="tt"&gt;themeDisplay - com.liferay.portal.theme.ThemeDisplay&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;company - com.liferay.portal.model.Company&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;account - com.liferay.portal.model.Account&lt;/span&gt; (deprecated)&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;user - com.liferay.portal.model.User&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;realUser - com.liferay.portal.model.User&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;contact - com.liferay.portal.model.Contact&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;?layout - com.liferay.portal.model.Layout&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;?layouts - List&amp;lt;com.liferay.portal.model.Layout&amp;gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;plid - java.lang.Long&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;?layoutTypePortlet - com.liferay.portal.model.LayoutTypePortlet&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;portletGroupId - java.lang.Long&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;permissionChecker - com.liferay.portal.security.permission.PermissionChecker&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;locale - java.util.Locale&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;timeZone - java.util.TimeZone&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;theme - com.liferay.portal.model.Theme&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;colorScheme - com.liferay.portal.model.ColorScheme&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="tt"&gt;portletDisplay - com.liferay.portal.theme.PortletDisplay&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Wow, now &lt;span class="b"&gt;that's&lt;/span&gt; lots of bacon!!! &lt;span class="em little"&gt;I like bacon!&lt;/span&gt;&lt;/p&gt;&lt;p&gt;I won't try to explain the use of all of these objects here on this post. Suffice it to say that after exploring all of these objects and the remaining tags available to you in the Liferay taglibs, you shouldn't run out of personalization options any time soon.&lt;/p&gt;&lt;p&gt;&lt;span class="b"&gt;You can't use JSP taglibs?&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Never fear! &lt;span class="b"&gt;All&lt;/span&gt; these objects are actually obtained by various getter methods on the &lt;span class="tt"&gt;com.liferay.portal.theme.ThemeDisplay&lt;/span&gt; object. We just put them right into the page context to cut down on code.&lt;/p&gt;&lt;p&gt;So, all you need is the &lt;span class="tt"&gt;themeDisplay&lt;/span&gt; object, and you can get it from the portletRequest like so:&lt;/p&gt;&lt;pre&gt;ThemeDisplay themeDisplay = (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);themeDisplay.getCompany();themeDisplay.getAccount();themeDisplay.getUser();themeDisplay.getRealUser();themeDisplay.getContact();if (themeDisplay.getLayout() != null) {themeDisplay.getLayout();}if (themeDisplay.getLayouts() != null) {themeDisplay.getLayouts();}themeDisplay.getPlid());if (themeDisplay.getLayoutTypePortlet() != null) {themeDisplay.getLayoutTypePortlet();}new Long(themeDisplay.getPortletGroupId());themeDisplay.getPermissionChecker();themeDisplay.getLocale();themeDisplay.getTimeZone();themeDisplay.getTheme();themeDisplay.getColorScheme();themeDisplay.getPortletDisplay();&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Well, feel to ask questions about the ones for which the meanings are less than obvious. The Message Boards are also full of answers regarding various personalization topics. And remember to have a look at the API docs on the site for the methods provided from each of these objects.&lt;/p&gt;&lt;style type="text/css"&gt;.post-body {}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 200px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;script type="text/javascript"&gt;//jQuery('.post-body pre').each(function() {//	this.scrollTop = this.scrollHeight;//});&lt;/script&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-05-16T17:20:43Z</dc:date>  </entry>  <entry>    <title>[HOWTO] Dowloading files from a Portlet</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/[howto]-dowloading-files-from-a-portlet" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-05-14T17:47:31Z</updated>    <published>2008-05-14T17:47:31Z</published>    <summary type="html">&lt;div class="post-body"&gt;&lt;p&gt;Many people ask how to achieve file downloads from a portlet project.&lt;/p&gt;&lt;p&gt;I'm going to try putting this mystery to rest by first explaining the limitations and then showing some old and new ways of overcoming them in Liferay. To be clear on terminology we'll call anything, other than the usual presentation content, that we want to deliver to the user a &lt;span class="tt"&gt;resource&lt;/span&gt;. Examples of &lt;span class="tt"&gt;resources&lt;/span&gt; are: images, binary documents, css stylesheets, js files, and typically things that we want to dynamically generate access to users to download. They are also often generated on the fly, like a PDF report, or a captcha image.&lt;/p&gt;&lt;p&gt;To begin, the &lt;span class="tt b"&gt;JSR-168/Portlet 1.0&lt;/span&gt; spec did not have support for delivering anything other than a blob of &lt;span class="tt"&gt;text/html&lt;/span&gt; content. Nor did it support delivering such content outside of a response which included the portal's wrappings.&lt;/p&gt;&lt;p&gt;Here is a brief (and not all encompassing) recent history of &lt;span class="b"&gt;resource downloads&lt;/span&gt; in Liferay.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Liferay &amp;lt;= 4.1.x:&lt;/strong&gt; At this point the ONLY ways to provide download links were:&lt;ol&gt;&lt;li&gt;provide a direct link to the resource&lt;/li&gt;&lt;li&gt;delegate the functionality to a servlet&lt;/li&gt;&lt;/ol&gt;&lt;/p&gt;&lt;p&gt;In Liferay's core this was very simple because we use Struts, so creating an action (and mapping) of type &lt;span class="tt"&gt;org.apache.struts.action.Action&lt;/span&gt; was relatively painless and we could quickly develop any download mechanism we needed. The only issue was that the URL could not be generated using the &lt;span class="tt"&gt;&amp;lt;portlet /&amp;gt;&lt;/span&gt; tag. A developer had to be aware of the path associated with the &lt;span class="tt"&gt;Action&lt;/span&gt; in order to access it.&lt;/p&gt;&lt;p&gt;For example, consider the following action-mapping from version 4.3.x's Document Library portlet:&lt;/p&gt;&lt;pre&gt;&amp;lt;action path=&amp;quot;/document_library/get_file&amp;quot; type=&amp;quot;com.liferay.portlet.documentlibrary.action.GetFileAction&amp;quot; /&amp;gt;&lt;/pre&gt;&lt;p&gt;If you look at the code for &lt;span class="tt"&gt;com.liferay.portlet.documentlibrary.action.GetFileAction&lt;/span&gt; you will see that it implements a method:&lt;/p&gt;&lt;pre&gt;public ActionForward strutsExecute(ActionMapping mapping, ActionForm form, HttpServletRequest req, HttpServletResponse res)throws Exception;&lt;/pre&gt;&lt;p&gt;This method, though found in &lt;span class="tt"&gt;com.liferay.portal.struts.PortletAction&lt;/span&gt;, is simply a helper method which makes the PortletAction behave as a servlet by calling &lt;span class="tt"&gt;super.execute(mapping, form, req, res)&lt;/span&gt; directly.&lt;/p&gt;&lt;p&gt;In order to put this method to work we create an url as follows:&lt;/p&gt;&lt;pre&gt;&amp;lt;a href=&amp;quot;&amp;lt;%= themeDisplay.getPathMain() %&amp;gt;/document_library/get_file?folderId=&amp;lt;%= folderId %&amp;gt;&amp;amp;name=&amp;lt;%= HttpUtil.encodeURL(name) %&amp;gt;&amp;quot;&amp;gt;&amp;lt;%= fileEntry.getTitle() %&amp;gt;&amp;lt;/a&amp;gt;&lt;/pre&gt;&lt;p&gt;Notice that we haven't used any tag to generate the URL and we needed to include the portals context.&lt;/p&gt;&lt;p&gt;So, at this point if you were creating a portlet to be JSR-168 complient (outside of ext, or core), you could create an URL to a &lt;span class="b"&gt;static resource&lt;/span&gt; like so (JSP example):&lt;/p&gt;&lt;pre&gt;&amp;lt;a href=&amp;quot;&amp;lt;%= renderRequest.getContextPath() %&amp;gt;/static_path/file_name.pdf&amp;quot;&amp;gt;file_name.pdf&amp;lt;/a&amp;gt;&lt;/pre&gt;&lt;p&gt;To create an URL to a &lt;span class="b"&gt;dynamic resource&lt;/span&gt;, you would have to delegate to some servlet, like so:&lt;/p&gt;&lt;pre&gt;&amp;lt;a href=&amp;quot;&amp;lt;%= renderRequest.getContextPath() %&amp;gt;/servlet_path?p1=va&amp;amp;p2=v2&amp;quot;&amp;gt;&amp;lt;%= someLinkText %&amp;gt;&amp;lt;/a&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;!--&lt;p&gt;&lt;img src="http://www.liferay.com/html/js/editor/fckeditor/editor/images/smiley/msn/regular_smile.gif" alt="" /&gt;&lt;/p&gt;--&gt;&lt;p&gt;&lt;strong&gt;Liferay &amp;lt;= 4.4.x and &amp;gt; 4.1.x:&lt;/strong&gt; At this point we started to experiment with delivering &lt;span class="b"&gt;portlet resources&lt;/span&gt; in a way similar to what was proposed in the upcoming &lt;span class="tt b"&gt;JSR-286/Portlet 2.0&lt;/span&gt; spec.&lt;/p&gt;&lt;p&gt;We had at our disposal a feature we had created which allowed us to deliver resources from portlets. It was used by specifying &lt;span class="tt"&gt;LiferayWindowState.EXCLUSIVE&lt;/span&gt; on a &lt;span class="tt"&gt;portlet URL&lt;/span&gt;. If using it from a actionURL it could deliver binary resources.&lt;/p&gt;&lt;p&gt;Example of returning a captcha image, from 4.3.x:&lt;/p&gt;&lt;pre&gt;&amp;lt;portlet:actionURL windowState=&amp;quot;&amp;lt;%= LiferayWindowState.EXCLUSIVE.toString() %&amp;gt;&amp;quot; var=&amp;quot;captchaURL&amp;quot;&amp;gt;&amp;lt;portlet:param name=&amp;quot;struts_action&amp;quot; value=&amp;quot;/message_boards/captcha&amp;quot; /&amp;gt;&amp;lt;/portlet:actionURL&amp;gt;&lt;/pre&gt;&lt;p&gt;On a renderURL it could be used for handling things like Ajax requests.&lt;/p&gt;&lt;pre&gt;&amp;lt;form action=&amp;quot;&amp;lt;liferay-portlet:renderURL windowState=&amp;quot;&amp;lt;%= LiferayWindowState.EXCLUSIVE.toString() %&amp;gt;&amp;quot;&amp;gt;&amp;lt;portlet:param name=&amp;quot;struts_action&amp;quot; value=&amp;quot;/password_generator/view&amp;quot; /&amp;gt;&amp;lt;/liferay-portlet:renderURL&amp;gt;&amp;quot; method=&amp;quot;post&amp;quot; name=&amp;quot;&amp;lt;portlet:namespace /&amp;gt;fm&amp;quot; onSubmit=&amp;quot;AjaxUtil.submit(this, {update: this.parentNode}); return false;&amp;quot;&amp;gt;&lt;/pre&gt;&lt;p&gt;Unfortunately, this feature was only usable from within the EXT environment or from within the core for handling binary resources, because final handling of the response required accessing the original HttpServletResponse.&lt;/p&gt;&lt;p&gt;Here is the response handling side of the capcha example:&lt;/p&gt;&lt;pre&gt;public void processAction(ActionMapping mapping, ActionForm form, PortletConfig config,ActionRequest req, ActionResponse res)throws Exception {try {PortletSession ses = req.getPortletSession();String captchaText = _producer.createText();ses.setAttribute(WebKeys.CAPTCHA_TEXT, captchaText);&lt;span class="new"&gt;		HttpServletResponse httpRes =((ActionResponseImpl)res).getHttpServletResponse();_producer.createImage(httpRes.getOutputStream(), captchaText);setForward(req, ActionConstants.COMMON_NULL);&lt;/span&gt;}catch (Exception e) {_log.error(e);}}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span class="b"&gt;Solution for external portlets:&lt;/span&gt; In order to provide a feature which was usable from portlets outside of EXT &amp;amp; core, we created a interface available in portal-kernel.jar and made RenderResponseImpl implement this interface. The interface provide the following methods:&lt;/p&gt;&lt;pre&gt;public interface LiferayRenderResponse extends RenderResponse {public void addDateHeader(String name, long date);public void addHeader(String name, String value);public void addIntHeader(String name, int value);public void setDateHeader(String name, long date);public void setHeader(String name, String value);public void setIntHeader(String name, int value);public void setResourceName(String resourceName);}&lt;/pre&gt;&lt;p&gt;Additionally, we added support for changing the content type returned from &lt;span class="tt b"&gt;RenderResponse&lt;/span&gt;, but &lt;span class="b"&gt;ONLY&lt;/span&gt; if the WinowState was equal to &lt;span class="tt"&gt;LiferayWindowState.EXCLUISVE&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;Here is an example which returns a png image:&lt;/p&gt;&lt;pre&gt;public void doView(RenderRequest req, RenderResponse res)throws IOException, PortletException {boolean logo = ParamUtil.getBoolean(req, &amp;quot;logo&amp;quot;);if (logo &amp;amp;&amp;amp; req.getWindowState().equals(LiferayWindowState.EXCLUSIVE)) {LiferayRenderResponse liferayRes = (LiferayRenderResponse)res;liferayRes.setContentType(&amp;quot;image/png&amp;quot;);liferayRes.addHeader(HttpHeaders.CACHE_CONTROL, &amp;quot;max-age=3600, must-revalidate&amp;quot;);OutputStream out = liferayRes.getPortletOutputStream();InputStream in = // some InputStreamif (in == null) {out.close();}else {byte[] buffer = new byte[4096];int len;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);}out.flush();in.close();out.close();}}else {include(viewJSP, req, res);}}&lt;/pre&gt;&lt;p&gt;Notice that you can specify the contentType, set headers, and write binary/text data directly to the portlet's OutputStream. One note is that the content type &lt;span class="b"&gt;MUST be set before&lt;/span&gt; attempting to call &lt;span class="tt"&gt;liferayRes.getPortletOutputStream();&lt;/span&gt; otherwise an exception will be raised.&lt;/p&gt;&lt;p&gt;And the url looks like this:&lt;/p&gt;&lt;pre&gt;&amp;lt;img src=&amp;quot;&amp;lt;portlet:renderURLportletMode=&amp;quot;view&amp;quot;windowState=&amp;quot;&amp;lt;%= LiferayWindowState.EXCLUSIVE.toString() %&amp;gt;&amp;quot;&amp;gt;&amp;lt;portlet:param name=&amp;quot;logo&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;&amp;lt;/portlet:renderURL&amp;gt;&amp;quot;alt=&amp;quot;Image returned by portlet.&amp;quot; /&amp;gt;&lt;/pre&gt;&lt;p&gt;This was pretty handy for everything from &lt;span class="b"&gt;XML&lt;/span&gt; and &lt;span class="b"&gt;JSON&lt;/span&gt; AJAX data, to &lt;span class="b"&gt;dynamically generated binary resources&lt;/span&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Liferay &amp;gt;= 5.0.0:&lt;/strong&gt; In the new release of Liferay, while still supporting all of the methods described above, we also support the brand new (and as of yet still unofficial) JSR-286/Portlet 2.0 feature &lt;span class="tt p"&gt;ResourceRequest&lt;/span&gt; which is specifically designed for handling any resource delivery purely as a function of a portal &amp;amp; portlet which are JSR-286 compliant. It can equally well handle everything from binary files to JSON data returned by an AJAX request.&lt;/p&gt;&lt;p&gt;An example usage looks like this:&lt;/p&gt;&lt;pre&gt;public void serveResource(ResourceRequest req, ResourceResponse res)throws PortletException, IOException {boolean logo = ParamUtil.getBoolean(req, &amp;quot;logo&amp;quot;);if (logo) {res.setContentType(&amp;quot;image/png&amp;quot;);res.addProperty(HttpHeaders.CACHE_CONTROL, &amp;quot;max-age=3600, must-revalidate&amp;quot;);OutputStream out = res.getPortletOutputStream();InputStream in = // some InputStreamif (in == null) {out.close();}else {byte[] buffer = new byte[4096];int len;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);}out.flush();in.close();out.close();}}}&lt;/pre&gt;&lt;p&gt;And the URL looks like this:&lt;/p&gt;&lt;pre&gt;&amp;lt;img src=&amp;quot;&amp;lt;portlet:resourceURL&amp;gt;&amp;lt;portlet:param name=&amp;quot;logo&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;&amp;lt;/portlet:resourceURL&amp;gt;&amp;quot;alt=&amp;quot;Image returned by portlet.&amp;quot; /&amp;gt;&lt;/pre&gt;&lt;p&gt;Well, there you have it.&lt;/p&gt;&lt;style type="text/css"&gt;.post-body {}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 200px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}&lt;/style&gt;&lt;script type="text/javascript"&gt;//jQuery('.post-body pre').each(function() {//	this.scrollTop = this.scrollHeight;//});&lt;/script&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-05-14T17:47:31Z</dc:date>  </entry>  <entry>    <title>[Tip] EhCache and Ubuntu (Hardy 8.04)</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/792624" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-05-12T19:45:00Z</updated>    <published>2008-05-12T19:45:00Z</published>    <summary type="html">&lt;p&gt;If you are or you have tried installing a Liferay on a cluster of Ubuntu machines and are/were having problems with the ehCache, do this check:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;]$ ping `hostname`&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If it responds using an address on the loopback device, there's your problem.&lt;br /&gt;&lt;br /&gt;By default, Ubuntu defines the machine's hostname on the loopback address (&lt;code&gt;127.0.1.1&lt;/code&gt;), and since ehCache uses:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;InetAddress.getLocalHost().getHostAddress()&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;to resolve the address used in association with the RMICachePeers it creates, these peers will have addresses of &lt;code&gt;127.0.1.1:port&lt;/code&gt; which will render them unreachable to other peers on the network.&lt;/p&gt;&lt;p&gt;Here is a successful test:&lt;/p&gt;&lt;p&gt;&lt;code&gt;rotty@liferay-laptop:~$ ping `hostname`&lt;br /&gt;PING liferay-laptop (192.168.0.243) 56(84) bytes of data.&lt;br /&gt;64 bytes from liferay-laptop (192.168.0.243): icmp_seq=1 ttl=64 time=0.046 ms&lt;br /&gt;64 bytes from liferay-laptop (192.168.0.243): icmp_seq=2 ttl=64 time=0.056 ms&lt;br /&gt;64 bytes from liferay-laptop (192.168.0.243): icmp_seq=3 ttl=64 time=0.055 ms&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Note that the address is one on the private subnet where the rest of the cluster nodes can see it.&lt;/p&gt;&lt;p&gt;FYI!&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-05-12T19:45:00Z</dc:date>  </entry>  <entry>    <title>Expandos - What are they? And how do they help me? (Liferay Portal &gt;5.0.1)</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/715049" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-04-24T13:29:27Z</updated>    <published>2008-04-24T13:29:27Z</published>    <summary type="html">&lt;div class="post-body"&gt;&lt;p&gt;&lt;span class="em little"&gt;&lt;span class="b"&gt;Updated:&lt;/span&gt; Wed May 28 10:53:09 EDT 2008&lt;/span&gt;&lt;/p&gt;&lt;p&gt;In Javascript, "expando" means "to attach additional properties to an object".&lt;/p&gt;&lt;p&gt;That's a little bit of hint as to what Expandos are in Liferay.&lt;/p&gt;&lt;p&gt;In Liferay the Expando service is a "generic" service which allows you to dynamically define a collection of data. This data can be &lt;ul&gt;&lt;li&gt;typed (boolean, Date, double, int, long, short, String, and arrays of all those)&lt;/li&gt;&lt;li&gt;associated with a specific entity (e.g. 'com.liferay.portal.model.User')&lt;/li&gt;&lt;li&gt;arranged into any number of "columns"&lt;/li&gt;&lt;li&gt;available to plugins&lt;/li&gt;&lt;li&gt;accessed from Velocity templates&lt;/li&gt;&lt;li&gt;accessed via Liferay's JSON API through AJAX&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;The service also provides all the CRUD methods you typically need (Create/Retreive/Update/Delete).&lt;/p&gt;&lt;p&gt;You often need a way to add some custom field to Users or other portal entities and sometimes you need to do it fast and with minimal effort... it might even be temporary. Well, &lt;strong&gt;Expando&lt;/strong&gt; is comming to the rescue.&lt;/p&gt;&lt;p&gt;To demonstrate how Expando works, I'll take you through a sample application which runs completely in as a velocity Journal Template.  &lt;img src="http://www.liferay.com/html/js/editor/fckeditor/editor/images/smiley/msn/regular_smile.gif" alt="" /&gt;&lt;/p&gt;&lt;p&gt;So first off, lets get the required details out of the way.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Step 1)&lt;/strong&gt; Create a Journal Structure. The structure is a requirement of any Journal Template because the connection between a Journal Article and a Journal Template is a function of the Journal Structure.&lt;/p&gt;&lt;p&gt;A very basic Structure is all we need in this case:&lt;/p&gt;&lt;pre&gt;&amp;lt;root&amp;gt;&amp;lt;dynamic-element name='content' type='text'&amp;gt;&amp;lt;/dynamic-element&amp;gt;&amp;lt;/root&amp;gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Step 2)&lt;/strong&gt; Create our Template&lt;/p&gt;&lt;p&gt;Our template "Language Type" will be VM (for Velocity), and we'll disable template caching by unckecking "Cacheable".&lt;/p&gt;&lt;p&gt;We'll start simple and work our way through the code. The first few lines will get some utility objects that we'll use later, and set a title for our app.&lt;/p&gt;&lt;pre&gt;#set ($locale = $localeUtil.fromLanguageId($request.get("locale")))#set ($dateFormatDateTime = $dateFormats.getDateTime($locale))&amp;lt;h1&amp;gt;First Expando Bank&amp;lt;/h1&amp;gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Step 3)&lt;/strong&gt; We create our Article.&lt;/p&gt;&lt;p&gt;On the Article tab we essentially click "Add Article", give it a "Name", choose the Structure we created in Step 1) and click "Save".&lt;/p&gt;&lt;p&gt;The result should be something like this:&lt;/p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=715004&amp;t=1209043489152" /&gt;&lt;div class="separator"&gt;&lt;!----&gt;&lt;/div&gt;&lt;p&gt;Now, the first task when using Expando is to define our &lt;em&gt;table&lt;/em&gt;. Expando lets you do this programatically and with very little code.&lt;/p&gt;&lt;p&gt;Give our table a name.&lt;/p&gt;&lt;pre&gt;...&amp;lt;h1&amp;gt;First Expando Bank&amp;lt;/h1&amp;gt;&lt;span class="new"&gt;#set ($accountsTableName = &amp;quot;AccountsTable&amp;quot;)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Check to see if the table exists, and if not, create it.&lt;/p&gt;&lt;pre&gt;...#set ($accountsTableName = &amp;quot;AccountsTable&amp;quot;)&lt;span class="new"&gt;#set ($accountsTable = $expandoTableLocalService.getTable($accountsTableName, $accountsTableName))#if (!$accountsTable)#set ($accountsTable = $expandoTableLocalService.addTable($accountsTableName, $accountsTableName))#end&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;We now have a table and we want to add columns to it. Since we're building a Bank app fields we need are &lt;em&gt;firstName&lt;/em&gt;, &lt;em&gt;lastName&lt;/em&gt;, and &lt;em&gt;balance&lt;/em&gt;. We'll also keep track of the last time the account was updated, &lt;em&gt;modifiedDate&lt;/em&gt;. The account number will be automitically generated and will represent the &lt;em&gt;primary key&lt;/em&gt; for our table. In Expando, this &lt;em&gt;primary key&lt;/em&gt; doesn't require a standalone column.&lt;/p&gt;&lt;p&gt;Also, we don't want this process to happen every time, so we'll only do it when the table is first created.&lt;/p&gt;&lt;pre&gt;...#if (!$accountsTable)#set ($accountsTable = $expandoTableLocalService.addTable($accountsTableName, $accountsTableName))&lt;span class="new"&gt;	#set ($accountsTableId = $accountsTable.getTableId())#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;firstName&amp;quot;, 15)) ## STRING#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;lastName&amp;quot;, 15)) ## STRING#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;balance&amp;quot;, 5)) ## DOUBLE#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;modifiedDate&amp;quot;, 3)) ## DATE&lt;/span&gt;#end&lt;/pre&gt;&lt;p&gt;Notice how we specified, for each column, an integer as the last parameter. These integer constants are defined in &lt;span class="tt"&gt;com.liferay.portlet.expando.model.ExpandoColumnConstants&lt;/span&gt;, and include the types I mentioned above.&lt;/p&gt;&lt;p&gt;Now that we have our table and columns setup, we want to add some logic that will detect and handle the various operations of our application. These are the CRUD operations we need for a complete app.&lt;/p&gt;&lt;p&gt;Let's start with some request handling and param setup.&lt;/p&gt;&lt;pre&gt;...&lt;span class="new"&gt;#set ($renderUrl = $request.get("render-url"))#set ($namespace = $request.get("portlet-namespace"))#set ($cmd = $request.get("parameters").get("cmd"))#set ($firstName = '')#set ($lastName = '')#set ($balance = 0.0)&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;I won't go into much detail regarding the request handling abilitites of Journal Templates. Suffice it to say that it supports it. (I'll cover that in another Blog entry.)&lt;/p&gt;&lt;p&gt;Determine whether we have been passed an account number. As I mentioned earlier, Expando "records" have a &lt;em&gt;primary key&lt;/em&gt;. That field is called "classPK", so we will keep that name to re-inforce the concept.&lt;/p&gt;&lt;pre&gt;...&lt;span class="new"&gt;#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Now we're going to check and see what operation (if any) we were asked to perform.&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))&lt;span class="new"&gt;#if ($cmd.equals("add") || $cmd.equals("update"))...#elseif ($cmd.equals("delete"))...#elseif ($cmd.equals("edit"))...#end&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Adding/Updating an account is our first operation. In this case, we get the params from the request.&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))#if ($cmd.equals("add") || $cmd.equals("update"))&lt;span class="new"&gt;	#set ($firstName = $request.get("parameters").get("firstName"))#set ($lastName = $request.get("parameters").get("lastName"))#set ($balance = $getterUtil.getDouble($request.get("parameters").get("balance")))#set ($date = $dateTool.getDate())&lt;/span&gt;#elseif ($cmd.equals("delete"))...#elseif ($cmd.equals("edit"))...#end&lt;/pre&gt;&lt;p&gt;Do some form input checking (this one just does a basic check).&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))#if ($cmd.equals("add") || $cmd.equals("update"))#set ($firstName = $request.get("parameters").get("firstName"))#set ($lastName = $request.get("parameters").get("lastName"))#set ($balance = $getterUtil.getDouble($request.get("parameters").get("balance")))#set ($date = $dateTool.getDate())&lt;span class="new"&gt;	#if (($cmd.equals("add") &amp;&amp; !$firstName.equals("") &amp;&amp; !$lastName.equals("") &amp;&amp; $balance &gt;= 50) || ($cmd.equals("update") &amp;&amp; !$firstName.equals("") &amp;&amp; !$lastName.equals("")))...#elsePlease fill the form completely in order to create an account. The minimum amount of cash required to create an account is $50.#end&lt;/span&gt;#elseif ($cmd.equals("delete"))...#elseif ($cmd.equals("edit"))...#end&lt;/pre&gt;&lt;p&gt;So now, if we're ok, store the data.&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))#if ($cmd.equals("add") || $cmd.equals("update"))#set ($firstName = $request.get("parameters").get("firstName"))#set ($lastName = $request.get("parameters").get("lastName"))#set ($balance = $getterUtil.getDouble($request.get("parameters").get("balance")))#set ($date = $dateTool.getDate())#if (($cmd.equals("add") &amp;&amp; !$firstName.equals("") &amp;&amp; !$lastName.equals("") &amp;&amp; $balance &gt;= 50) || ($cmd.equals("update") &amp;&amp; !$firstName.equals("") &amp;&amp; !$lastName.equals("")))&lt;span class="new"&gt;		#if ($classPK &lt;= 0)#set ($classPK = $dateTool.getDate().getTime())#end#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "firstName", $classPK, $firstName))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "lastName", $classPK, $lastName))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "balance", $classPK, $balance))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "modifiedDate", $classPK, $date))#if ($cmd.equals("update"))Thank you, ${firstName}, for updating your account with our bank!#elseThank you, ${firstName}, for creating an account with our bank!#end&lt;/span&gt;#elsePlease fill the form completely in order to create an account. The minimum amount of cash required to create an account is $50.#end#elseif ($cmd.equals("delete"))...#elseif ($cmd.equals("edit"))...#end&lt;/pre&gt;&lt;p&gt;Before we can continue we need to do some cleanup.&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))#if ($cmd.equals("add") || $cmd.equals("update"))#set ($firstName = $request.get("parameters").get("firstName"))#set ($lastName = $request.get("parameters").get("lastName"))#set ($balance = $getterUtil.getDouble($request.get("parameters").get("balance")))#set ($date = $dateTool.getDate())#if (($cmd.equals("add") &amp;&amp; !$firstName.equals("") &amp;&amp; !$lastName.equals("") &amp;&amp; $balance &gt;= 50) || ($cmd.equals("update") &amp;&amp; !$firstName.equals("") &amp;&amp; !$lastName.equals("")))#if ($classPK &lt;= 0)#set ($classPK = $dateTool.getDate().getTime())#end#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "firstName", $classPK, $firstName))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "lastName", $classPK, $lastName))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "balance", $classPK, $balance))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, "modifiedDate", $classPK, $date))#if ($cmd.equals("update"))Thank you, ${firstName}, for updating your account with our bank!#elseThank you, ${firstName}, for creating an account with our bank!#end#elsePlease fill the form completely in order to create an account. The minimum amount of cash required to create an account is $50.#end&lt;span class="new"&gt;	#set ($classPK = 0)#set ($firstName = '')#set ($lastName = '')#set ($balance = 0.0)&lt;/span&gt;#elseif ($cmd.equals("delete"))...#elseif ($cmd.equals("edit"))...#end&lt;/pre&gt;&lt;p&gt;The next operation to handle is Deleting an account.&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))#if ($cmd.equals("add") || $cmd.equals("update"))...#elseif ($cmd.equals("delete"))&lt;span class="new"&gt;	#if ($classPK &gt; 0)#set ($V = $expandoRowLocalService.deleteRow($accountsTableName, $accountsTableName, $classPK))Account deleted!#set ($classPK = 0)#end&lt;/span&gt;#elseif ($cmd.equals("edit"))...#end&lt;/pre&gt;&lt;p&gt;That's it... Pretty simple? Sure is! Next, the Edit operation.&lt;/p&gt;&lt;pre&gt;...#set ($classPK = $getterUtil.getLong($request.get("parameters").get("classPK")))#if ($cmd.equals("add") || $cmd.equals("update"))...#elseif ($cmd.equals("delete"))...#elseif ($cmd.equals("edit"))&lt;span class="new"&gt;	Editting...#if ($classPK &gt; 0)#set ($firstName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, "firstName", $classPK, ""))#set ($lastName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, "lastName", $classPK, ""))#set ($balance = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, "balance", $classPK, 0.0))#end&lt;/span&gt;#end&lt;/pre&gt;&lt;p&gt;So, we retrieved the data stored in the requested account and put them into some placeholder variables.&lt;/p&gt;&lt;p&gt;Finally, we're ready to display some UI elements. We'll have two different views, a table listing the accounts, and a form for adding/editing accounts.&lt;/p&gt;&lt;pre&gt;...&lt;span class="new"&gt;&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals("edit"))...#else...#end&lt;/span&gt;&lt;/pre&gt;&lt;p&gt;Ok, so when we show the table we need some frillies like a "Create Account" button and some column headers.&lt;/p&gt;&lt;pre&gt;...&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals("edit"))&lt;span class="new"&gt;	&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Create Account&amp;quot; onClick=&amp;quot;self.location = '${renderUrl}&amp;amp;${namespace}cmd=edit';&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Account Number&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;First Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Last Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Balance&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Modified Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;&amp;lt;!----&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;#else...#end&lt;/pre&gt;&lt;p&gt;Here we're going to add the calls to get the number of, and list of all the existing accounts.&lt;/p&gt;&lt;pre&gt;...&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals("edit"))&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Create Account&amp;quot; onClick=&amp;quot;self.location = '${renderUrl}&amp;amp;${namespace}cmd=edit';&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Account Number&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;First Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Last Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Balance&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Modified Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;&amp;lt;!----&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;span class="new"&gt;	#set ($rowsCount = $expandoRowLocalService.getRowsCount($accountsTableName, $accountsTableName))#set ($rows = $expandoRowLocalService.getRows($accountsTableName, $accountsTableName, -1, -1))&lt;/span&gt;#else...#end&lt;/pre&gt;&lt;p&gt;Iterate through the list.&lt;/p&gt;&lt;pre&gt;...&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals("edit"))&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Create Account&amp;quot; onClick=&amp;quot;self.location = '${renderUrl}&amp;amp;${namespace}cmd=edit';&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Account Number&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;First Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Last Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Balance&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Modified Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;&amp;lt;!----&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;#set ($rowsCount = $expandoRowLocalService.getRowsCount($accountsTableName, $accountsTableName))#set ($rows = $expandoRowLocalService.getRows($accountsTableName, $accountsTableName, -1, -1))&lt;span class="new"&gt;	#foreach($row in $rows)#set ($currentClassPK = $row.getClassPK())...#end#if ($rowsCount &amp;lt;= 0)&amp;lt;tr&amp;gt;&amp;lt;td colspan=&amp;quot;5&amp;quot;&amp;gt;No Accounts were found.&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;#end&amp;lt;/table&amp;gt;# of Accounts: ${rowsCount}&lt;/span&gt;#else...#end&lt;/pre&gt;&lt;p&gt;Let's draw each table row (the accounts).&lt;/p&gt;&lt;pre&gt;...&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals("edit"))&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Create Account&amp;quot; onClick=&amp;quot;self.location = '${renderUrl}&amp;amp;${namespace}cmd=edit';&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Account Number&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;First Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Last Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Balance&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Modified Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;&amp;lt;!----&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;#set ($rowsCount = $expandoRowLocalService.getRowsCount($accountsTableName, $accountsTableName))#set ($rows = $expandoRowLocalService.getRows($accountsTableName, $accountsTableName, -1, -1))#foreach($row in $rows)#set ($currentClassPK = $row.getClassPK())&lt;span class="new"&gt;		&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;${currentClassPK}&amp;lt;/td&amp;gt;#set ($currentFirstName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;firstName&amp;quot;, $currentClassPK, &amp;quot;&amp;quot;))&amp;lt;td&amp;gt;${currentFirstName}&amp;lt;/td&amp;gt;#set ($currentLastName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;lastName&amp;quot;, $currentClassPK, &amp;quot;&amp;quot;))&amp;lt;td&amp;gt;${currentLastName}&amp;lt;/td&amp;gt;#set ($currentBalance = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;balance&amp;quot;, $currentClassPK, 0.0))&amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;${numberTool.currency($currentBalance)}&amp;lt;/td&amp;gt;#set ($currentModifiedDate = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;modifiedDate&amp;quot;, $currentClassPK, $dateTool.getDate()))&amp;lt;td&amp;gt;${dateFormatDateTime.format($currentModifiedDate)}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;${renderUrl}&amp;amp;amp;${namespace}cmd=edit&amp;amp;amp;${namespace}classPK=${currentClassPK}&amp;quot;&amp;gt;Edit&amp;lt;/a&amp;gt; |&amp;lt;a href=&amp;quot;${renderUrl}&amp;amp;amp;${namespace}cmd=delete&amp;amp;amp;${namespace}classPK=${currentClassPK}&amp;quot;&amp;gt;Delete&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;#end#if ($rowsCount &amp;lt;= 0)&amp;lt;tr&amp;gt;&amp;lt;td colspan=&amp;quot;5&amp;quot;&amp;gt;No Accounts were found.&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;#end&amp;lt;/table&amp;gt;# of Accounts: ${rowsCount}#else...#end&lt;/pre&gt;&lt;p&gt;Well, that was a mouthfull, but it should be pretty familliar design pattern. The general theme here is "ease of use". It didn't take much time or anything too tricky to get at the data.&lt;/p&gt;&lt;p&gt;The final piece of code is of course the input form.&lt;/p&gt;&lt;pre&gt;...&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals("edit"))...#else&lt;span class="new"&gt;	&amp;lt;form action=&amp;quot;$renderUrl&amp;quot; method=&amp;quot;post&amp;quot; name=&amp;quot;${namespace}fm10&amp;quot;&amp;gt;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;${namespace}classPK&amp;quot; value=&amp;quot;${classPK}&amp;quot; /&amp;gt;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;${namespace}cmd&amp;quot;#if ($classPK &amp;gt; 0)value=&amp;quot;update&amp;quot;#elsevalue=&amp;quot;add&amp;quot;#end/&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;First Name:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;${namespace}firstName&amp;quot; value=&amp;quot;${firstName}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;Last Name:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;${namespace}lastName&amp;quot; value=&amp;quot;${lastName}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;Balance:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;${namespace}balance&amp;quot; value=&amp;quot;${numberTool.format($balance)}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Save&amp;quot; /&amp;gt;&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Cancel&amp;quot; onclick=&amp;quot;self.location = '${renderUrl}'&amp;quot; /&amp;gt;&amp;lt;/form&amp;gt;&lt;/span&gt;#end&lt;/pre&gt;&lt;p&gt;Two significant items to note are &lt;span class="tt"&gt;${renderUrl}&lt;/span&gt; and &lt;span class="tt"&gt;${namespace}&lt;/span&gt;. Remember that we're running within the context of a portlet, which means that we have to get a base url from the portal (we can't just use any old url). The code we wrote earlier got us an url from the request. Secondly, we need to &lt;strong&gt;namespace&lt;/strong&gt; any parameter we're going to post back to that url so that the container knows to which portlet it belongs (there might be more that one portlet on the page).&lt;/p&gt;&lt;p&gt;Now you have a result which should look something like this:&lt;/p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=715009&amp;t=1209043550555" /&gt;&lt;p&gt;Creating/Editting an account:&lt;/p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=715018&amp;t=1209043632708" /&gt;&lt;p&gt;List with 4 accounts:&lt;/p&gt;&lt;img src="http://cdn.www.liferay.com/image/image_gallery?img_id=715030&amp;t=1209043686630" /&gt;&lt;p&gt;Here's the whole template with comments.&lt;/p&gt;&lt;pre&gt;#set ($locale = $localeUtil.fromLanguageId($request.get(&amp;quot;locale&amp;quot;)))#set ($dateFormatDateTime = $dateFormats.getDateTime($locale))&amp;lt;h1&amp;gt;First Expando Bank&amp;lt;/h1&amp;gt;#### Define the &amp;quot;name&amp;quot; for our ExpandoTable.###set ($accountsTableName = &amp;quot;AccountsTable&amp;quot;)#### Get/Create the ExpandoTable to hold our data.###set ($accountsTable = $expandoTableLocalService.getTable($accountsTableName, $accountsTableName))#if (!$accountsTable)#set ($accountsTable = $expandoTableLocalService.addTable($accountsTableName, $accountsTableName))#set ($accountsTableId = $accountsTable.getTableId())#### Create an ExpandoColumn for each field in the form.###set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;firstName&amp;quot;, 15)) ## STRING#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;lastName&amp;quot;, 15)) ## STRING#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;balance&amp;quot;, 5)) ## DOUBLE#set ($V = $expandoColumnLocalService.addColumn($accountsTableId, &amp;quot;modifiedDate&amp;quot;, 3)) ## DATE#end#### Do some request handling setup.###set ($renderUrl = $request.get(&amp;quot;render-url&amp;quot;))#set ($namespace = $request.get(&amp;quot;portlet-namespace&amp;quot;))#set ($cmd = $request.get(&amp;quot;parameters&amp;quot;).get(&amp;quot;cmd&amp;quot;))#set ($firstName = '')#set ($lastName = '')#set ($balance = 0.0)#### Check to see if a classPK was passed in the request.###set ($classPK = $getterUtil.getLong($request.get(&amp;quot;parameters&amp;quot;).get(&amp;quot;classPK&amp;quot;)))#### Check if we have received a form submission?###if ($cmd.equals(&amp;quot;add&amp;quot;) || $cmd.equals(&amp;quot;update&amp;quot;))#### Let's get the form values from the request.###set ($firstName = $request.get(&amp;quot;parameters&amp;quot;).get(&amp;quot;firstName&amp;quot;))#set ($lastName = $request.get(&amp;quot;parameters&amp;quot;).get(&amp;quot;lastName&amp;quot;))#set ($balance = $getterUtil.getDouble($request.get(&amp;quot;parameters&amp;quot;).get(&amp;quot;balance&amp;quot;)))#set ($date = $dateTool.getDate())#### Validate the params to see if we should proceed.###if (($cmd.equals(&amp;quot;add&amp;quot;) &amp;amp;&amp;amp; !$firstName.equals(&amp;quot;&amp;quot;) &amp;amp;&amp;amp; !$lastName.equals(&amp;quot;&amp;quot;) &amp;amp;&amp;amp; $balance &amp;gt;= 50) || ($cmd.equals(&amp;quot;update&amp;quot;) &amp;amp;&amp;amp; !$firstName.equals(&amp;quot;&amp;quot;) &amp;amp;&amp;amp; !$lastName.equals(&amp;quot;&amp;quot;)))#### Check to see if it's a new Account.###if ($classPK &amp;lt;= 0)#set ($classPK = $dateTool.getDate().getTime())#end#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, &amp;quot;firstName&amp;quot;, $classPK, $firstName))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, &amp;quot;lastName&amp;quot;, $classPK, $lastName))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, &amp;quot;balance&amp;quot;, $classPK, $balance))#set ($V = $expandoValueLocalService.addValue($accountsTableName, $accountsTableName, &amp;quot;modifiedDate&amp;quot;, $classPK, $date))#### Show a response.###if ($cmd.equals(&amp;quot;update&amp;quot;))Thank you, ${firstName}, for updating your account with our bank!#elseThank you, ${firstName}, for creating an account with our bank!#end#elsePlease fill the form completely in order to create an account. The minimum amount of cash required to create an account is $50.#end#set ($classPK = 0)#set ($firstName = '')#set ($lastName = '')#set ($balance = 0.0)#elseif ($cmd.equals(&amp;quot;delete&amp;quot;))#### Delete the specified Row.###if ($classPK &amp;gt; 0)#set ($V = $expandoRowLocalService.deleteRow($accountsTableName, $accountsTableName, $classPK))Account deleted!#set ($classPK = 0)#end#elseif ($cmd.equals(&amp;quot;edit&amp;quot;))#### Edit the specified Row.##Editting...#if ($classPK &amp;gt; 0)#### Get the account specific values###set ($firstName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;firstName&amp;quot;, $classPK, &amp;quot;&amp;quot;))#set ($lastName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;lastName&amp;quot;, $classPK, &amp;quot;&amp;quot;))#set ($balance = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;balance&amp;quot;, $classPK, 0.0))#end#end&amp;lt;span style=&amp;quot;display: block; border-top: 1px solid #CCC; margin: 5px 0px 5px 0px;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;#if (!$cmd.equals(&amp;quot;edit&amp;quot;))#### Now we're into the display logic.##&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Create Account&amp;quot; onClick=&amp;quot;self.location = '${renderUrl}&amp;amp;${namespace}cmd=edit';&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Account Number&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;First Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Last Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Balance&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Modified Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;&amp;lt;!----&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;#### Get all the current records in our ExpandoTable. We can paginate by passing a## &amp;quot;begin&amp;quot; and &amp;quot;end&amp;quot; params.###set ($rowsCount = $expandoRowLocalService.getRowsCount($accountsTableName, $accountsTableName))#set ($rows = $expandoRowLocalService.getRows($accountsTableName, $accountsTableName, -1, -1))#foreach($row in $rows)#### Get the classPK of this row.###set ($currentClassPK = $row.getClassPK())&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;${currentClassPK}&amp;lt;/td&amp;gt;#set ($currentFirstName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;firstName&amp;quot;, $currentClassPK, &amp;quot;&amp;quot;))&amp;lt;td&amp;gt;${currentFirstName}&amp;lt;/td&amp;gt;#set ($currentLastName = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;lastName&amp;quot;, $currentClassPK, &amp;quot;&amp;quot;))&amp;lt;td&amp;gt;${currentLastName}&amp;lt;/td&amp;gt;#set ($currentBalance = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;balance&amp;quot;, $currentClassPK, 0.0))&amp;lt;td align=&amp;quot;right&amp;quot;&amp;gt;${numberTool.currency($currentBalance)}&amp;lt;/td&amp;gt;#set ($currentModifiedDate = $expandoValueLocalService.getData($accountsTableName, $accountsTableName, &amp;quot;modifiedDate&amp;quot;, $currentClassPK, $dateTool.getDate()))&amp;lt;td&amp;gt;${dateFormatDateTime.format($currentModifiedDate)}&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;a href=&amp;quot;${renderUrl}&amp;amp;amp;${namespace}cmd=edit&amp;amp;amp;${namespace}classPK=${currentClassPK}&amp;quot;&amp;gt;Edit&amp;lt;/a&amp;gt; |&amp;lt;a href=&amp;quot;${renderUrl}&amp;amp;amp;${namespace}cmd=delete&amp;amp;amp;${namespace}classPK=${currentClassPK}&amp;quot;&amp;gt;Delete&amp;lt;/a&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;#end#if ($rowsCount &amp;lt;= 0)&amp;lt;tr&amp;gt;&amp;lt;td colspan=&amp;quot;5&amp;quot;&amp;gt;No Accounts were found.&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;#end&amp;lt;/table&amp;gt;# of Accounts: ${rowsCount}#else#### Here we have our input form.##&amp;lt;form action=&amp;quot;$renderUrl&amp;quot; method=&amp;quot;post&amp;quot; name=&amp;quot;${namespace}fm10&amp;quot;&amp;gt;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;${namespace}classPK&amp;quot; value=&amp;quot;${classPK}&amp;quot; /&amp;gt;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;${namespace}cmd&amp;quot;#if ($classPK &amp;gt; 0)value=&amp;quot;update&amp;quot;#elsevalue=&amp;quot;add&amp;quot;#end/&amp;gt;&amp;lt;table class=&amp;quot;lfr-table&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;First Name:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;${namespace}firstName&amp;quot; value=&amp;quot;${firstName}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;Last Name:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;${namespace}lastName&amp;quot; value=&amp;quot;${lastName}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;Balance:&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;${namespace}balance&amp;quot; value=&amp;quot;${numberTool.format($balance)}&amp;quot; /&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Save&amp;quot; /&amp;gt;&amp;lt;input type=&amp;quot;button&amp;quot; value=&amp;quot;Cancel&amp;quot; onclick=&amp;quot;self.location = '${renderUrl}'&amp;quot; /&amp;gt;&amp;lt;/form&amp;gt;#end&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;/pre&gt;&lt;style type="text/css"&gt;.post-body {width: 98%;}p.quote {margin: 1em 2em;}pre {background-color: #DDD;border: 1px solid #999;font: normal 10px sans-serif;max-height: 200px;padding: 5px;overflow: auto;margin-bottom: 12px;}span.attr {display: block;text-align: right;font-size: smaller;}span.new {background-color: #bbccff;}span.tt {font-family: Courier, monospace;}span.b {font-weight: bold;}span.em {font-style: italic;}span.little {font-size: smaller;}&lt;/style&gt;&lt;script type="text/javascript"&gt;jQuery('.post-body pre').each(function() {this.scrollTop = this.scrollHeight;});&lt;/script&gt;&lt;/div&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-04-24T13:29:27Z</dc:date>  </entry>  <entry>    <title>Making Liferay Social - Opensocial</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/making-liferay-social---opensocial" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-04-15T20:27:41Z</updated>    <published>2008-04-15T20:27:41Z</published>    <summary type="html">&lt;p&gt;Social networks and portals are a natural fit. To that end we're currently doing work to integrate the brand new (still in incubation) Shindig project.&lt;/p&gt;&lt;p&gt;Shindig is the Gadget and Opensocial reference implementation currently in incubation under the Apache Software Foundation and being worked on by Google, MySpace, Orkut, Salesforce.com, and many other large social networking sites. Liferay is now joining the fray by integrating the project.&lt;/p&gt;&lt;p&gt;Currently, it takes the form of a Liferay Plugin, which means it's hot deployable. As the Shindig container evolves it may become more or less integrated into Liferay depending on the direction of it's internals.&lt;/p&gt;&lt;p&gt;Currently, it already passes Opensocial 0.7 Compliance Tests:&lt;/p&gt;&lt;p&gt;&lt;img width="100%" src="http://cdn.www.liferay.com/image/image_gallery?img_id=687030&amp;amp;t=1208289491643" alt="Opensocial 0.7 Complience Tests" /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;While, not officially a member of the spec team, Liferay will be following along closely and participating in ongoing development.&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-04-15T20:27:41Z</dc:date>  </entry>  <entry>    <title>Announcements and Alerts</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/announcements-and-alerts" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-04-14T14:24:15Z</updated>    <published>2008-04-14T14:24:15Z</published>    <summary type="html">&lt;p&gt;In Liferay 5.0.0+ we have added a new feature: &lt;b&gt;Announcements/Alerts&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;Announcements/Alerts are two portlets which can be used to broadcast some message to a list of users within a known scope. Essentially they provide a mass messaging engine you'd tend to think of as a &amp;quot;news letter&amp;quot; or one-way messaging.&lt;/p&gt;&lt;p&gt;&lt;br /&gt; They provide the following features:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Configurable, unlimited number of &lt;b&gt;Announcement Types&lt;/b&gt; through portal.properties (add locatized type names via Liferay's resource bundle extension mechanism).&lt;/li&gt;&lt;li&gt;Delivery to known scopes, called &lt;b&gt;Delivery Scope&lt;/b&gt;, including: &lt;br /&gt;&lt;ol&gt;&lt;li&gt;Individual User (API only)&lt;/li&gt;&lt;li&gt;Role&lt;/li&gt;&lt;li&gt;User Group&lt;/li&gt;&lt;li&gt;Community&lt;/li&gt;&lt;li&gt;Organization&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Delivery Mechanisms&lt;/b&gt; include &lt;b&gt;Email&lt;/b&gt;, &lt;b&gt;SMS&lt;/b&gt;, and &lt;b&gt;Website&lt;/b&gt;. (&lt;b&gt;Website&lt;/b&gt; delivery is acheived simply by adding the portlet to any page accessible to the user. The content of the portlet is allways sensitive to the viewing user.)&lt;/li&gt;&lt;li&gt;&lt;b&gt;Scheduled delivery&lt;/b&gt;. Each entry has a Display and Expiration Date. The entry won't be delivered until Display Date is &amp;lt;= now. Expiration Date is used primarily for the &amp;quot;Website&amp;quot; delivery mechanism.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Read Tracking&lt;/b&gt;. Website delivery tracks timestamped &lt;b&gt;read&lt;/b&gt; status per user, per entry.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Subscription Control&lt;/b&gt; per user, per Announcement Type. A user determines which Delivery Mechanism to use for each individual Announcement Type. &lt;i&gt;See the &lt;b&gt;My Account&lt;/b&gt; page.&lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;Broadcast Control&lt;/b&gt; of announcements. Broadcasting Announcements is not a permission which should be granted lightly. As such, a user must first be granted &lt;b&gt;Add Entry&lt;/b&gt; permission on the portlet to access the Announcement management functions. Secondly, a user must have &lt;b&gt;Assign Member&lt;/b&gt; permission on a given scope in order to be able to broadcast an Announcement/Alert to that scope.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;i.e. a Community Owner &lt;b&gt;is not&lt;/b&gt;, by default, granted permission to broadcast Announcements or Alerts to their own Community. The Portal Admin must first has to grant them &lt;b&gt;Add Entry&lt;/b&gt; permission on the portlet. Only then would they have all the permissions required (they would already have &lt;b&gt;Assign Member&lt;/b&gt; as a function of being the owner) to broacast to their &lt;b&gt;own&lt;/b&gt; Community. This is to prevent abuse of portal resources and prevent needless spamming of portal users.&lt;/p&gt;&lt;p&gt;Via the service API we now have a means of enabling event based messaging to individual users (or any delivery scope) without having to explicitly write new delivery services or functions.&lt;/p&gt;&lt;p&gt;There are still a few features that I'd like to see added to this portlet, but I think we now have a good base to build on.&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-04-14T14:24:15Z</dc:date>  </entry>  <entry>    <title>Staging v1.2</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/staging-v1-2" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-03-01T01:53:56Z</updated>    <published>2008-03-01T01:53:56Z</published>    <summary type="html">&lt;p&gt;Well finally it's in trunk...&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;The sponsored &lt;b&gt;staging enhancements (workflow)&lt;/b&gt; (which I've dubbed v1.2) features are now in.&lt;/p&gt; &lt;p&gt;Let me know what you think.&lt;/p&gt; &lt;object width="600" height="460"&gt;&lt;param name="movie" value="http://www.jeroenwijering.com/embed/mediaplayer.swf"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embedsrc="http://www.jeroenwijering.com/embed/mediaplayer.swf"width="600"height="460"allowscriptaccess="always"allowfullscreen="true"flashvars="width=600&amp;height=460&amp;file=http://files.liferay.com/temp/staging/staging-v1.2-2.flv"/&gt;&lt;/object&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-03-01T01:53:56Z</dc:date>  </entry>  <entry>    <title>Making remote API calls to Liferay's JSON services from PHP</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/making-remote-api-calls-to-liferay-s-json-services-from-php" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-02-12T22:59:22Z</updated>    <published>2008-02-12T22:59:22Z</published>    <summary type="html">&lt;p&gt;Liferay offers a wide variety of different remote API's. &lt;/p&gt;&lt;p&gt;A very useful one is JSON. It's useful because most languages have JSON processing functions. So the only dependencies are those and HTTP request handling functions.&lt;/p&gt;&lt;p&gt;Here is a complete example of adding a Country from a PHP script:&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# This example uses HTTP_Request package located found here:&lt;br /&gt;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://pear.php.net/package/HTTP_Request&lt;br /&gt;#&lt;br /&gt;# Tested with:&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Liferay Portal - revision 13652&lt;br /&gt;#&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; HTTP_Request - version 1.4.1&lt;br /&gt;#&lt;br /&gt;require_once(&amp;quot;HTTP/Request.php&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# URL to the tunnle-web JSON API.&lt;br /&gt;#&lt;br /&gt;$a = &amp;amp;new HTTP_Request('http://localhost:8080/tunnel-web/secure/json');&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Use POST in 99% of cases, so that encoding of passed data is handled properly.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;setMethod(&amp;quot;POST&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Specify the authentication credientials.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;setBasicAuth(&amp;quot;2&amp;quot;,&amp;quot;test&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Specify the service class.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;addPostData(&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&amp;quot;serviceClassName&amp;quot;, &amp;quot;com.liferay.portal.service.http.CountryServiceJSON&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Specify the service method.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;serviceMethodName&amp;quot;, &amp;quot;addCountry&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# List the method parameters.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;serviceParameters&amp;quot;, &amp;quot;name,a2,a3,number,idd,active&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Give the values to use in the method.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;name&amp;quot;,&amp;nbsp;&amp;nbsp; &amp;quot;AAAAAA&amp;quot;);&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;a2&amp;quot;,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;AA&amp;quot;);&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;a3&amp;quot;,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;AA&amp;quot;);&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;number&amp;quot;, &amp;quot;1000000&amp;quot;);&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;idd&amp;quot;,&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;quot;1000000&amp;quot;);&lt;br /&gt;$a-&amp;gt;addPostData(&amp;quot;active&amp;quot;, &amp;quot;true&amp;quot;);&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# Send the request.&lt;br /&gt;#&lt;br /&gt;$a-&amp;gt;sendRequest();&lt;br /&gt;&lt;br /&gt;echo $a-&amp;gt;getResponseBody();&lt;br /&gt;&lt;br /&gt;#&lt;br /&gt;# If the country does not exist, this will print:&lt;br /&gt;#&lt;br /&gt;#&amp;nbsp;&amp;nbsp; {&amp;quot;active&amp;quot;:true,&amp;quot;a2&amp;quot;:&amp;quot;AA&amp;quot;,&amp;quot;a3&amp;quot;:&amp;quot;AA&amp;quot;,&amp;quot;idd&amp;quot;:&amp;quot;1000000&amp;quot;,&amp;quot;countryId&amp;quot;:10904,&amp;quot;name&amp;quot;:&amp;quot;AAAAAA&amp;quot;,&amp;quot;number&amp;quot;:&amp;quot;1000000&amp;quot;}&lt;br /&gt;#&lt;br /&gt;#&lt;br /&gt;# The above indicates success. Notice that &amp;quot;countryId&amp;quot; is included in the result.&lt;br /&gt;#&lt;br /&gt;?&amp;gt;&lt;/span&gt;&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-02-12T22:59:22Z</dc:date>  </entry>  <entry>    <title>Runtime control of your Liferay theme</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/runtime-control-of-your-liferay-theme" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-02-11T19:35:12Z</updated>    <published>2008-02-11T19:35:12Z</published>    <summary type="html">&lt;p&gt;Sometimes you want some ability to tweak your Liferay theme at runtime...&lt;/p&gt; &lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; Don't do this when you are packaging a theme for public consumption.&lt;/p&gt; &lt;p&gt;It's very easy to add some &amp;quot;on-line&amp;quot; editability/tweakability to your themes, because we all know that we never get it just right the first time... and finding a bug inevitably happens the moment it goes live and/or you are presenting it to the CEO, and/or the moment you don't have access to the source to do another build...&lt;/p&gt; &lt;p&gt;The solution, for convenience or as a safety net, is simple.&lt;/p&gt; &lt;p&gt;- Create a Journal Template of type CSS, not associated with any Structure. Take note of the &lt;b&gt;URL of the template&lt;/b&gt;.&lt;/p&gt; &lt;p&gt;- In the theme file &amp;quot;portal_normal.vm&amp;quot;, find the section in the head:&lt;/p&gt; &lt;p&gt;&lt;span style="font-family: Courier New;"&gt;[snip]&lt;br /&gt; &amp;lt;head&amp;gt;&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;title&amp;gt;$company_name - $the_title&amp;lt;/title&amp;gt;&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; $theme.include($top_head_include)&lt;br /&gt; &lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; #css ($css_main_file)&lt;br /&gt; &amp;nbsp;&amp;nbsp;&amp;nbsp; #js ($js_main_file)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Courier New;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;br /&gt; [/snip]&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Now, using the convenient VM macros available, include a reference to the URL you noted above.&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Courier New;"&gt;&amp;nbsp; &amp;nbsp; #css ($css_main_file)&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; #css (&amp;quot;http://...&amp;quot;)&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;It's better to include the reference after the portal's own, because otherwise you can't overrride it's definitions.&lt;/p&gt;&lt;p&gt;That's it!&lt;/p&gt;&lt;p&gt;Now you can access that template and tweak the theme whenever (and wherever) you are... everyone is happy... of course eventually you can migrate permanent changes to a next iteration of the theme.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Hope that helps!&lt;/p&gt;</summary>    <dc:creator>Ray Augé</dc:creator>    <dc:date>2008-02-11T19:35:12Z</dc:date>  </entry>  <entry>    <title>Publishing Flash (SWF) in a flexible way using the Liferay CMS</title>    <link rel="alternate" href="http://www.liferay.com/web/rauge/blog/-/blogs/472148" />    <author>      <name>Ray Augé</name>    </author>    <updated>2008-02-11T19:18:32Z</updated>    <published>2008-02-11T19:18:32Z</published>    <summary type="html">&lt;p&gt;Many people ask how to build portlets which display Flash (SWF) content in the portal with options for changing and controlling that content.&lt;/p&gt;&lt;p&gt;Well, you don't have to build a portlet to handle this. I mean, you can... but you don't have too. Liferay provides all the tools you need to do this right out of the CMS and you can give yourself as much control as you like while still making it simple for non-techincal people...&lt;/p&gt;&lt;p&gt;- First, make sure that the Document Library portlet is configured to allow SWF files (should by default in recent versions).&lt;/p&gt;&lt;p&gt;in portal(-ext).properties:&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: Courier New;"&gt;dl.file.extensions=...,.swf,...&lt;/span&gt;&lt;/p&gt;&lt;p&gt;- Upload a file.&lt;/p&gt;&lt;p&gt;- Next, in the &lt;b&gt;Journal&lt;/b&gt; portlet, create a Journal Structure with the following basic schema (add more params as you see fit).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Journal Structure:&lt;/b&gt;&lt;br /&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;root&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;dynamic-element name='swf-file' type='document_library'&amp;gt;&amp;lt;/dynamic-element&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;dynamic-element name='width' type='text'&amp;gt;&amp;lt;/dynamic-element&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;dynamic-element name='height' type='text'&amp;gt;&amp;lt;/dynamic-element&amp;gt;&lt;br /&gt;&amp;lt;/root&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;- Create a Journal Template which will process the values defined in the Structure.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Journal Template (Velocity - VM):&lt;/b&gt;&lt;br /&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;object width=&amp;quot;${width.getData()}&amp;quot; height=&amp;quot;${height.getData()}&amp;quot;&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;param name=&amp;quot;movie&amp;quot; value=&amp;quot;http://@portal_url@${swf-file.getData()}&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;param name=&amp;quot;wmode&amp;quot; value=&amp;quot;transparent&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;&amp;nbsp; &amp;lt;embed src=&amp;quot;http://@portal_url@${swf-file.getData()}&amp;quot;&lt;/span&gt;&amp;nbsp; &lt;span style="font-family: Courier New;"&gt;type=&amp;quot;application/x-shockwave-flash&amp;quot; wmode=&amp;quot;transparent&amp;quot; width=&amp;quot;${width.getData()}&amp;quot; height=&amp;quot;${height.getData()}&amp;quot;&amp;gt;&amp;lt;/embed&amp;gt;&lt;br /&gt;&amp;lt;/object&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Associate this template with the structure above.&lt;/p&gt;&lt;p&gt;Now create articles which use the above structure and you'll have a file selection dialog available to select a file from Document Library (select your file). Set the desired height, width, &amp;quot;Save&amp;quot;, &amp;quot;Approve&amp;quot;.&lt;/p&gt;&lt;p&gt;Now all you need to do is add a Journal Content portlet to any page and then select the Article you just created.&lt;/p&gt;&lt