Blogs
This blog post contains a simple procedure to upgrade the templates created for Web Content in Liferay Portal 6.x EE with Velocity to Freemarker in Liferay Portal/DXP 7.x. As you might know, the use of Velocity is deprecated in Liferay Portal/DXP 7.x and it’s advisable to migrate from Velocity to Freemarker.
This post is not magic or rocket science, just a set of steps that can help in automatization of some steps that would otherwise be manual.
Let’s start reviewing the tools used and then the tips and tricks within a complete procedure.
Tools used and configuration
-
Sublime Text is the Text editor used.
- Freemarker Bundle for Sublime Text useful for syntax highlighting.
- For its installation, follow the instructions on the link above.
- HTMLBeautify package for Sublime Text
-
It can be installed in Sublime Text following Preferences > Package Control > Install and write HTMLBeautify.
- Configure the indentation rules to include the Freemarker tags for HTMLBeautify to work. To do this, you need:
- In Sublime Text go to Preferences > Package Settings > HTMLBeautify > Settings - Default and copy the text that is displayed.
- Go to Preferences > Package Settings > HTMLBeautify > Settings - user and paste the text copied from the previous item.
- Include the Freemarker tags (separated with the pipe character | ) in the following sections and then save these changes:
- In tag_indent section, include:
<#if,<#foreach - In tag_unindent section, include:
</#if,</#foreach - In tag_unindent_line section, include:
<#elseif,<#else
- In tag_indent section, include:
Below is a picture of the final configuration:

-
- USCavalry tool, that allows to translate Velocity templates to Freemarker. Although it’s legacy and not maintained anymore it’s very useful and it can still be downloaded from the Sourceforge repository.
- Unzip USCavalry in a working directory.
-
Obviously, Liferay Portal or DXP 7.x after its database has been upgraded.
Tips and Tricks
Follow this procedure with one of your Velocity templates for Web Content to test if it’s useful for you:
-
In order to copy the code inside the templates, it can be very useful to load them using WebDAV. For that, open a remote connection using the File Explorer in your OS to one URL like this one: http://localhost:8080/webdav/guest/journal/Templates/ . In the URL, remember to change:
-
Instead of “localhost:8080”, the domain of your Liferay server where the templates are hosted.
-
Instead of “guest”, the name of the Website where the templates are stored.
-
-
Once the templated directory is mounted through WebDAV, copy the files to the working directory where you unzipped USCavalry.
-
Add an extension to those files, such as “.vm”.
-
In the working directory, run the following command with one of the existing files as the first argument and a new file as the second argument:
java -jar cavalry.jar 11843.vm 11843.ftl -
After the previous step is completed, you will see that the output of the Freemarker file (the .ftl one), is unindented making the readability of that file very difficult. In order to tidy up that file, follow these steps:
-
First, separate every HTML or Freemarker tag into a different line. To achieve this, in Sublime Text replace the closing tag character (
>) with the same character followed by the new line escape sequence (>\n), as you can see in the following image:
-
Second, apply HTMLBeautify. In Sublime Text, go to Edit > Beautify HTML (if it doesn’t work, go to line 1 and run it again)
At this stage, the code in the templates would be correctly indented and formatted to allow easy reading.
-
-
Lastly, you will need to apply additional adaptations to your template, such as:
- First, after the process by the USCavalry tool, you will need to change some if sentences. To check if a variable is not null, follow this example:
<#if someVariable && someVariable.getData() != "">
You must change it and include the ‘??’ operator:
<#if someVariable?? && someVariable.getData() != ""> - Second, if you need to access services through serviceLocator, keep in mind that the fully qualified name of the class changed in Liferay 7.x. So, after the process with USCavalry tool you will still have the original names, like this one:
<#assign journalArticleLocalService = serviceLocator.findService("com.liferay.portlet.journal.service.JournalArticleLocalService")>
And you will need to replace them for the current service names:
<#assign journalArticleLocalService = serviceLocator.findService("com.liferay.journal.service.JournalArticleLocalService")>
The solution above applies to other services like: assetEntryLocalService, assetCategoryLocalService and assetVocabularyLocalService, the names need to be replaced as follows:Orinal service name Replacement service name com.liferay.portlet.asset.service.AssetEntryLocalService
com.liferay.asset.kernel.service.AssetEntryLocalService
com.liferay.portlet.asset.service.AssetCategoryLocalService
com.liferay.asset.kernel.service.AssetCategoryLocalService
com.liferay.portlet.asset.service.AssetVocabularyLocalService
com.liferay.asset.kernel.service.AssetVocabularyLocalService
- If you need to access metadata from the Web Content piece, after the process through USCavalry you will see sentences like this one:
<#assign ja = journalArticleLocalService.getArticle(getterUtil.getLong(groupId), getterUtil.getString(reserved-article-id.data))>
This needs to be changed to the following syntax:
<#assign ja = journalArticleLocalService.getArticle(getterUtil.getLong(groupId), getterUtil.getString(.vars['reserved-article-id'].data))>Keep in mind that other data fields are available. See the following class for a complete reference.
- If you have repeatable fields in your Web Content structure and you need to check them in your template, after the process through USCavalry, your Freemarker template will have something similar to this:
<#if !repeatableField.getSiblings().isEmpty()>
In these cases, you will need to change them to the following:
<#if repeatableField.getSiblings()?has_content> - Be careful if you have anchors (href="#something") in your <a> tags because USCavalry might have deleted them.
- Lastly, remember that you have a wide selection of variables that allow you to access a great amount of data and information stored in Liferay. Here is the list of variables and the type of each variable. You can check the methods available for each type at the javadocs site.
VARIABLE
TYPE
accountPermission
com.liferay.portal.kernel.service.permission.AccountPermission
arrayUtil
com.liferay.portal.kernel.util.ArrayUtil_IW
auditMessageFactoryUtil
com.liferay.portal.kernel.audit.AuditMessageFactory
bodyCssClass
java.lang.String
browserSniffer
com.liferay.portal.kernel.servlet.BrowserSniffer
calendarFactory
com.liferay.portal.kernel.util.CalendarFactory
colorScheme
com.liferay.portal.kernel.model.ColorScheme
commonPermission
com.liferay.portal.kernel.service.permission.CommonPermission
company
com.liferay.portal.kernel.model.Company
dateFormatFactory
com.liferay.portal.kernel.util.FastDateFormatFactory
dateFormats
com.liferay.portal.kernel.util.FastDateFormatFactory
dateUtil
com.liferay.portal.kernel.util.DateUtil_IW
enumUtil
freemarker.ext.beans.BeansWrapper
expandoColumnLocalService
com.liferay.expando.kernel.service.ExpandoColumnLocalService
expandoRowLocalService
com.liferay.expando.kernel.service.ExpandoRowLocalService
expandoTableLocalService
com.liferay.expando.kernel.service.ExpandoTableLocalService
expandoValueLocalService
com.liferay.expando.kernel.service.ExpandoValueLocalService
freeMarkerPortletPreferences
com.liferay.portal.template.TemplatePortletPreferences
fullCssPath
java.lang.String
fullTemplatesPath
java.lang.String
getterUtil
com.liferay.portal.kernel.util.GetterUtil_IW
groupPermission
com.liferay.portal.kernel.service.permission.GroupPermission
htmlUtil
com.liferay.portal.kernel.util.Html
httpUtil
com.liferay.portal.kernel.util.Http
httpUtilUnsafe
com.liferay.portal.kernel.util.Http
imageToken
com.liferay.portal.kernel.webserver.WebServerServletToken
imageToolUtil
com.liferay.portal.kernel.image.ImageTool
init
java.lang.String
jsonFactoryUtil
com.liferay.portal.kernel.json.JSONFactory
languageUtil
com.liferay.portal.kernel.language.Language
layout
com.liferay.portal.kernel.model.Layout
layoutPermission
com.liferay.portal.kernel.service.permission.LayoutPermission
layouts
java.util.List
layoutTypePortlet
com.liferay.portal.kernel.model.LayoutTypePortlet
locale
java.util.Locale
localeUtil
com.liferay.portal.kernel.util.LocaleUtil
locationPermission
com.liferay.portal.kernel.service.permission.OrganizationPermission
navItems
java.util.List
objectUtil
freemarker.template.TemplateMethodModelEx
organizationPermission
com.liferay.portal.kernel.service.permission.OrganizationPermission
pageTitle
java.lang.String
paramUtil
com.liferay.portal.kernel.util.ParamUtil_IW
passwordPolicyPermission
com.liferay.portal.kernel.service.permission.PasswordPolicyPermission
permissionChecker
com.liferay.portal.kernel.security.permission.PermissionChecker
plid
java.lang.String
portal
com.liferay.portal.kernel.util.Portal
portalPermission
com.liferay.portal.kernel.service.permission.PortalPermission
portalUtil
com.liferay.portal.kernel.util.Portal
portletDisplay
com.liferay.portal.kernel.theme.PortletDisplay
portletGroupId
java.lang.Long
portletModeFactory
com.liferay.portal.kernel.portlet.PortletModeFactory_IW
portletPermission
com.liferay.portal.kernel.service.permission.PortletPermission
portletProviderAction
java.util.Map
portletURLFactory
com.liferay.portal.kernel.portlet.PortletURLFactory
prefsPropsUtil
com.liferay.portal.kernel.util.PrefsProps
propsUtil
com.liferay.portal.kernel.util.Props
randomizer
com.liferay.portal.kernel.util.Randomizer
realUser
com.liferay.portal.kernel.model.User
request
javax.servlet.http.HttpServletRequestWrapper
rolePermission
com.liferay.portal.kernel.service.permission.RolePermission
saxReaderUtil
com.liferay.portal.kernel.xml.SAXReader
scopeGroupId
java.lang.Long
serviceLocator
com.liferay.portal.template.ServiceLocator
sessionClicks
com.liferay.portal.kernel.util.SessionClicks_IW
xmlRequest
java.lang.Object
staticFieldGetter
com.liferay.portal.kernel.util.StaticFieldGetter
staticUtil
freemarker.ext.beans.StaticModels
stringUtil
com.liferay.portal.kernel.util.StringUtil_IW
theme
com.liferay.portal.kernel.model.Theme
themeDisplay
com.liferay.portal.kernel.theme.ThemeDisplay
timeZone
java.util.TimeZone
timeZoneUtil
com.liferay.portal.kernel.util.TimeZoneUtil_IW
unicodeFormatter
com.liferay.portal.kernel.util.UnicodeFormatter_IW
unicodeLanguageUtil
com.liferay.portal.kernel.language.UnicodeLanguage
user
com.liferay.portal.kernel.model.User
userGroupPermission
com.liferay.portal.kernel.service.permission.UserGroupPermission
userPermission
com.liferay.portal.kernel.service.permission.UserPermission
utilLocator
com.liferay.portal.template.UtilLocator
validator
com.liferay.portal.kernel.util.Validator_IW
webServerToken
com.liferay.portal.kernel.webserver.WebServerServletToken
- First, after the process by the USCavalry tool, you will need to change some if sentences. To check if a variable is not null, follow this example:
Take-aways
I hope this blog post was useful for you and it can save you some time in the task of migrating off your Web content templates from Velocity to Freemarker.
The combination of the migration tool (USCavalry) and a Text editor that can tidy up your template will make all that process easier.

