« Back to Templating Languages

Custom Velocity Variables

Introduction #

If you ever wanted to create more custom Velocity Variables that would be available for your theme to reference it's as easy as setting an attribute in ServicePreAction.java!

Background #

If you look at the template code inside a typical Liferay theme, you will see many velocity variables being referenced. For example inside of the Classic theme's dock.vm, there is a line of code:

 <h2 class="user-greeting">
  <span>$user_greeting</span>
 <h2>

$user_greeting fetches the "Welcome Joe Bloggs" sign you'll see when you log in as test@liferay.com Where did $user_greeting come from? Look inside init.vm, and you'll that it's set here:

 #set ($user_greeting = $user.getGreeting())

It gets the User object ($user) and retrieves the 'greeting' (.getGreeting()) from the database. Where is the User object set as a velocity variable? The magic happens inside of VelocityVariables.java:

 vc.put("user", themeDisplay.getUser());

'vc' is an instance of VelocityContext, and all we are doing here is getting the user object (themeDisplay.getUser()) and assigning it the key of "user". This translates to the ability to using $user as the User object.

Custom Velocity Variables #

  1. First see what variables are already available.
    1. Some commonly used ones that are already available include $user, $company, $layout, and $themeDisplay. You probably don't want to create a new one for getting a user's screen name, because you can just do $user.getScreenName().
  2. ServicePreAction.java
    1. You'll want to extend the ServicePreAction class first and reference it in portal-ext.properties:
 servlet.service.events.pre=com.liferay.portal.events.ServicePreAction,your.custom.EXTServicePreAction

Inside your extended ServicePreAction class, create a Map object with the key and value of the vm variables you want to insert, and then set that into the attribute under the key WebKeys.VM_VARIABLES.

Finally, the WebKeys.VM_VARIABLES gets picked up in VelocityVariables.java:

 // Insert custom vm variables
 Map vmVariables = (Map)req.getAttribute(WebKeys.VM_VARIABLES);
 if (vmVariables != null) {
   Iterator itr = vmVariables.entrySet().iterator();
   while (itr.hasNext()) {
    Map.Entry entry = (Map.Entry)itr.next();
    String key = (String)entry.getKey();
    Object value = entry.getValue();
    if (Validator.isNotNull(key)) {
      vc.put(key, value);
    }
   }
 }
0 Attachments
124618 Views
Average (9 Votes)
The average rating is 4.11111111111111 stars out of 5.
Comments
Threaded Replies Author Date
I managed to do this. Create a class with a... Alex Brdar November 27, 2008 7:59 AM
Since this is read at the end of setting the... Tor Iver Wilhelmsen May 14, 2009 5:50 AM
I am facing one problem. I created the class... Arunjyoti Banik March 16, 2015 9:25 PM
If I am not mistaken the... Edgar Vonk September 28, 2009 6:02 AM
I am also facing the same problem..... Nitesh Kr Sahay July 3, 2013 8:07 AM
This does not seem to work for a CMS template. ... Thomas Kellerer November 27, 2009 2:44 AM
This is a great explanation. Thank you! jeff Leitman March 18, 2010 10:58 AM
"1. You'll want to extend the ServicePreAction... Eric Dobbs June 14, 2010 1:07 PM
You can put it under liferay-portal-ext in... Leon Fleysher August 26, 2010 12:17 AM
Hi, Leon. I create my EXTServicePerAction... Blair Long March 29, 2011 1:12 AM
Sorry for my bad english, If anybody know more... Blair Long March 29, 2011 1:49 AM
See this example: Look at this example: public... Diego Andres Garcia March 30, 2011 12:07 PM
Thank you, Diego! I create a new EXT... Blair Long March 30, 2011 10:29 PM
And, If I re-name my custom class in existing... Blair Long March 30, 2011 10:49 PM
I realize this page has been here for a long... Dana Oredson March 13, 2012 8:35 AM
if you use 6.1 see... 凌 李 May 6, 2013 1:49 AM
My question is we do we need to iterate the... Adnan Yaqoob July 30, 2015 8:52 AM

I managed to do this. Create a class with a no-arg constructor that extends com.liferay.portal.kernel.events.Action:
<pre>
public class EXTServicePreAction extends Action {
public void run(HttpServletRequest req, HttpServletResponse res)
throws ActionException {
Map<String, Object> vmVariables = new HashMap<String, Object>();
// Setup some variables
vmVariables.put("extendedVariable", "test");
req.setAttribute(WebKeys.VM_VARIABLES, vmVariables);
}
}
</pre>

Now you can define this in portal-ext.properties, you still need ServicePreAction included first:
<pre>
servlet.service.events.pre=com.liferay.portal.events.ServicePreAction­,your.custom.EXTServicePreAction
<pre>

And that's it, the Velocity theme templates can now see the variables!
Posted on 11/27/08 7:59 AM.
Since this is read at the end of setting the variables you can also use this to override the preexisting variables. E.g. we have some custom page types where we want to have "Add Application" available, and added an action listener like this which set "show_add_content" based on permissions instead of permissions plus "portlet" and whatever else it uses hardcoded.
Posted on 5/14/09 5:50 AM in reply to Alex Brdar.
If I am not mistaken the servlet.service.events.pre is triggered for _every_ HTTP request in Liferay? Is this true? When I load a page (with just one portlet on it) I see that my custom action is invoked around 10 times..

Since I invoke quite heavy business logic in my action (calling a remote EJemoticon I am looking for another solution. Is there another event I can couple my custom action to?

One solution I can think of could be: create a custom post login action, do the business logic there, set the result in the session, and then from my servlet.service.events.pre action retrieve this result from the session and place it in the Velocity variables in the request.
Posted on 9/28/09 6:02 AM.
This does not seem to work for a CMS template.
I can see the variable in the request attributes and I can retrieve something, but calling a method on it doesn't work:

#set ($vars = $request.get('attributes').get('VM_VARIABLES'))
#set ($myHelper = $vars.get('myHelper'))

If I show $vars.toString() it display a "toString" of the map, including the key "myHelper", but when trying to call a method on $myHelper it does not work.

What am I missing?
Posted on 11/27/09 2:44 AM.
This is a great explanation. Thank you!
Posted on 3/18/10 10:58 AM.
"1. You'll want to extend the ServicePreAction class first and reference it in portal-ext.properties"

Where do I create the package for this class???
Posted on 6/14/10 1:07 PM.
You can put it under liferay-portal-ext in whatever package you want -- just write the same package name in portal-ext.properties when referencing your ServicePreAction. I'd also recommend to change the name of your extended class.
Posted on 8/26/10 12:17 AM in reply to Eric Dobbs.
Hi, Leon.
I create my EXTServicePerAction extends ServicePreAction and override "run" method. Is it right?
My code below:
public class CustomVelocityVariableAction extends ServicePreAction{
public void run(HttpServletRequest request, HttpServletResponse response)
throws ActionException {
super.run(request, response);
Map vmVariables = (Map)request.getAttribute(WebKeys.VM_VARIABLES);
if (vmVariables != null) {
vmVariables.put("my_vm_var", "my_vm_var");
}

}
}


Which portal-ext.properties file I need to change?
"webapps\ROOT\WEB-INF\classes" or EXT plugins project?
Posted on 3/29/11 1:12 AM in reply to Leon Fleysher.
Sorry for my bad english, If anybody know more material about custom velocity variables, A thousand thanks !
Posted on 3/29/11 1:49 AM in reply to Blair Long.
See this example:
Look at this example:
public void run(HttpServletRequest request, HttpServletResponse response)throws ActionException {
Map<String, Object> variablesVM= new HashMap<String, Object>();
variablesVM.put("test", "test");
request.setAttribute(WebKeys.VM_VARIABLES, variablesVM);
}

Change the properties in EXT project and deploy it.

In the .VM you can use the var:
$test

...good luck
Posted on 3/30/11 12:07 PM in reply to Blair Long.
Thank you, Diego!
I create a new EXT project and follow your example. It's ok.
But I can't apply it in existing EXT project, Because portal-ext.properties file doesn't copy to %Tomcat%webapps\ROOT\WEB-INF\classes this folder.
I see that just deploy project first will copy this file.
Posted on 3/30/11 10:29 PM in reply to Diego Andres Garcia.
And, If I re-name my custom class in existing EXT project, then change portal-ext.properties suitably, Copy this file to tomcat manually. When re-start tomcat, It occured "ClassNotFoundException". This mean my custom class didn't deploy successfully.
Does this issue cause some difference between Community Edition and Enterprise Edition?
Posted on 3/30/11 10:49 PM in reply to Blair Long.
I realize this page has been here for a long time, but if your theme depends on these velocity variables, isn't it better to use the theme's init_custom.vm?

Then you can pick up your theme and deploy it on a separate site that doesn't have the service pre action defined and your theme should still work.
Posted on 3/13/12 8:35 AM.
if you use 6.1 see this:http://www.opensourceforlife.com/2012/06/custom-velocity-variable-in-lifera­y-61.html
Posted on 5/6/13 1:49 AM.
I am also facing the same problem.....
Posted on 7/3/13 8:07 AM in reply to Edgar Vonk.
I am facing one problem. I created the class inside a hook project and referenced it as said in portal-ext.properties. When I am restarting the server, console messages are throwing ClassNotFoundException (Unable to load ServicePreActionExtended with the portal class loader or the current context class loader).

But when I am placing the same code in portal.properties of the hook it is throwing ClassNotFoundException for the original ServicePreAction class.
Cant figure out where I am wrong .............. Can you give a suggestion??
Posted on 3/16/15 9:25 PM in reply to Alex Brdar.
My question is we do we need to iterate the vmVariables unless we need to locate some existing variable.
Why can't we simple put a new entry after getting the map from request
Posted on 7/30/15 8:52 AM.