Migrating Web content templates from Velocity to Freemarker: tools, tips and tricks

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

  1. Sublime Text is the Text editor used.

  2. Freemarker Bundle for Sublime Text useful for syntax highlighting.
    • For its installation, follow the instructions on the link above.
  3. 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

    Below is a picture of the final configuration:

    HTMLBeautify configuration
  4. 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.
  5. 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:

  1. 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.

  2. Once the templated directory is mounted through WebDAV, copy the files to the working directory where you unzipped USCavalry.

  3. Add an extension to those files, such as “.vm”.

  4. 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

  5. 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.

  6. 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

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.