Blog

Making Freemarker templates truly flexible with Liferay Document and Media Library

In my previous post about Freemarker I explained what was needed in order to use a custom template loader for loading your Freemarker template views from Liferay’s Document and Media Library. In this blog post I will go more into detail on how it is used and how I made it.

First you need to have your class implement freemarker.cache.TemplateLoader in order to have your template loader bean recognize your class as a Freemarker Template Loader. There are four methods that you need to implement, well actually there is three since you can leave one empty.

  • public final Object findTemplateSource(final String name)
  • public long getLastModified(final Object templateSource)
  • public final Reader getReader(final Object templateSource, final String encoding)
  • public void closeTemplateSource(final Object templateSource)

It also has a constructor that takes five arguments which initializes the instance variables. These variables decides some of the behavior of the template loader and they are set using a properties file called freemarker.properties which is located in src/main/resources. The variables are then loaded into the liferayFreemarkerTemplateLoader bean in applicationContext.xml through constructor-arg parameters and the use of property-placeholder for reading the properties file.

Snippet of the bean in applicationContext.xml:

 <context:property-placeholder  location="classpath:freemarker.properties"  ignore-unresolvable="true" />

    <bean id="liferayFreemarkerTemplateLoader"  class="com.monator.freemarker.service.LiferayFreemarkerTemplateLoader">
        <constructor-arg value="${site.name}" index="0"/>
        <constructor-arg value="${create.site.if.not.exists}" index="1"/>
        <constructor-arg value="${freemarker.template.path}" index="2"/>
        <constructor-arg value="${create.folder.if.not.exists}" index="3"/>
        <constructor-arg value="/WEB-INF/freemarker/default/view.ftl" index="4"/>
    </bean>

 

The instance variables and how they are initialized in the constructor:

    /** Name of the Site under which the template will be found. */
    private String site_name;

    /** Path to the folder where your templates lies. */
    private String template_folder_path;

    /** Determines if the Site should be created or not when not existing. */
    private boolean create_site;

    /** Determines if the folders should be created or not when not existing. */
    private boolean create_folder;

    /** Default template which will be copied to the created folder path if
create_folder is true. */

    private Resource default_template;

 public LiferayFreemarkerTemplateLoader(final String siteName,  final boolean createSite, final String folderPath,  final boolean createFolder, final Resource defaultTemplate) {
        this.site_name = siteName;
        this.create_site = createSite;
        this.template_folder_path = folderPath;
        this.create_folder = createFolder;
        this.default_template = defaultTemplate;
    }

 

As seen above the behavior that is controlled with instance variables is if the template loader should create the Site and folder path if they don’t exist and thereby also copy a default template into the newly created template folder. If either of the booleans are false the template loader will return null and print a log message explaining what has happened and what could be done to fix it.

The method findTemplateSource is where all the magic happens and to speed things up it uses Liferay’s cache to save which folderId the template lies in and also under which groupId (Site) the folder is located. If the folderId is available in the cache the template loader uses it to fetch the template from the Document and Media Library. If it should not exist in the cache the template loader gets the folderId using the template_folder_path. The path is split into an array and then used as an argument to the recursive method getTemplateFolderIdFromPath(long groupId, long folderId, List<String> templateFoldersArray, ServiceContext serviceContext), which traverses through the folder array and returns the folderId for the last folder in the array. If the folder path does not exist, the method creates the folders or returns MISSING_FOLDER if the boolean create_folder is false.

The same goes for groupId, if it is available in the cache it is used when fetching the template. If not, the template loader uses the private method getTemplatesGroupId(final String siteName, final ServiceContext serviceContext) to collect it depending on the boolean create_site. If the boolean is true the method creates the Site and returns the new groupId, otherwise it prints a log message explaining the problem and a possible solution and returns MISSING_SITE.

When we have both the folderId and the groupId (Site) for where the template is located we can use DLFileEntryLocalServiceUtil.getFileEntry(long groupId, long folderId, String title) to fetch the template and return it to the view resolver.

We have now done the bulk job for the template loader and only have three small methods left. public final long getLastModified(final Object templateSource) returns in milliseconds when the template (DLFileEntry) was last modified, public final Reader getReader(final Object templateSource, final String encoding) just returns an InputStreamReader created from the DLFileEntry’s ContentStream and the last method to implement is public void closeTemplateSource(final Object templateSource) which in our case doesn’t need any code and can be left blank.

Now we are done with the Freemarker Template Loader and only need to make sure that our applicationContext.xml is configured for using custom template loaders. If your freemarkerConfig bean looks like this you’re all set.

 <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer" p:preferFileSystemAccess="false" >
        <property name="preTemplateLoaders" ref="freemarkerTemplateLoaderList" />
    </bean>

 

This was kind of a crash course in how to create your own Freemarker Template Loader which reads its templates from Liferay’s Document and Media Library. But for those of you interested in seeing all of the code and try out the portlet, you can find the project on GitHub: https://github.com/monator/freemarker-template-loader-samples

Stay tuned for coming blog posts about how to use this newly learned knowledge of loading Freemarker templates from Liferay’s Document and Media Library together with Liferay Sync to get seamless live editing of your portlet views! And as if that wasn’t enough I will also explain more in detail how to create a Freemarker Template Loader which uses CMIS for reading templates from different repositories.

An Awesome Day of Liferay

During the last 24 hours the Liferay community has been gathered around Google Hangout and Youtube watching and listening to interesting topics covering everything from OSGi support in Liferay to development with JSF and even Russian skiing. First of all, let's give a big shout out to James Falkner at Liferay who did a tremendous job hosting the event. Can't believe how he managed to stay up for the full 24 hours and still maintain focus and energy throughout the full session. Kudos to James!

We at Monator are proud to be one of the invited guests at the event. Andreas talked about Freemarker and custom template loaders. Our time slot was during session 6 (see below) at around 2:58:00.

Andreas first blog post about Freemarker can be found here. Stay tuned for additional posts digging more deeply into the topic of Freemarker and template loaders.

If you missed any session or would like to watch any session again here are the recorded sessions:

Session 1

Link to Youtube page

Session 2

Link to Youtube page

Session 3

Link to Youtube page

Session 4

Link to Youtube page

Session 5

Link to Youtube page

Session 6

Link to Youtube page

A Day of Liferay

Liferay's Community Manager James Falkner just announced that A Day of Liferay is about to begin. The event is a 24 hour webcast where Liferay will take a virtual trip around the world to explore the fantastic Liferay community.

Liferay and Freemarker

At Monator we are honoured to have been invited to participate during this epic event. During our (Team Monator) 30 minutes slot, Andreas Magnusson will talk about Freemarker and Liferay. More specifically he will discuss and show an awesome Freemarker template loader implementation with Spring Portlet MVC. With this implementation your portlet views can live as content in the Document and Media Library in Liferay Portal. This allows you to take advantage of pre-built Liferay features such as versioning and workflows. Also, Andreas will show how you can use Liferay Sync for live editing your views.

Join us tomorrow (Wednesday) at 2PM CEST. More info can be found here.

Watch the event live

You can watch the event live directly from monator.com:

Freemarker, not just another template language

This is the first in a series of blog post about how to use Freemarker in portlet views and also in Liferay WCM and themes. Some of the topics I will discuss in this and coming blog posts are:

  • How to switch to Freemarker for portlet views and still have accsess to JSP taglibs, using Spring Portlet MVC.
  • Using custom Freemarker Template Loaders for reading template files from outside of the portlet.
  • Creating a Freemarker Template Loader for reading templates from Liferay’s Document Library.
  • Creating a Freemarker Template Loader for reading templates from different repositories using CMIS, in my case I use Alfresco as the repository.
  • Using Liferay Sync together with the Liferay Document Library Template Loader for seamless view template editing.

Freemarker is a great template language with a lot of neat built-in functions. Therefore it is nice to see that it is so easy to change template language from JSP to Freemarker in a portlet when using Spring Portlet MVC. You can even use the same taglibs as in JSP in Freemarker. In that way you can develop your views with the same richness and also have access to Freemarker’s built-in functions for extra convenience.

All that is needed for you to use JSP taglibs in Freemarker is this small code:

<#assign liferay_ui=JspTaglibs["/WEB-INF/tld/liferay-ui.tld"]>

Now you use ”liferay_ui” in the same way as you would have used ”liferay-ui” in JSP:

<@liferay_ui.message key="liferay-message-key" /> VS <liferay-ui:message key="liferay-message-key"/>

When using Spring framework with your Liferay portlet you only need to change the view resolver bean in applicationContext.xml to use Freemarker instead of JSP, in order to make it work. Just create a freemarker config bean, specifying templateLoaderPath to a similar path where you usually have your JSP’s. Then, instead of using the regular InternalResourceViewResolver, you use org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver.

Here is an example how the applicationContext.xml can look like:

 <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer" p:templateLoaderPath="/WEB-INF/freemarker/" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
    <property name="cache" value="true" />
    <property name="prefix" value="" />
    <property name="suffix" value=".ftl" />
</bean>

 

But there is another great feature with Freemarker, namely that you can use a Template Loader to load your template from outside of the portlet. Freemarker has developed a FileTemplateLoader and a URLTemplateLoader that can be used to either get the template from a file in a specified directory or from a URL. Besides that, you also have the opportunity to create your own custom TemplateLoader that reads a template from a source of your choice.

To manage this kind of template loading you need to do some changes in applicationContext.xml. First you need to create a freemarkerTemplateLoader bean which should point to your custom template loader. Also you need to create a freemarkerTemplateLoaderList bean where you specify the template loaders which should be used, in our case a reference to the freemarkerTemplateLoader bean. Then you need to change the Freemarker config bean we created earlier so that it uses the newly freemarkerTemplateLoaderList bean instead.

The applicationContext.xml could look something like this when using a custom template loader:

 <bean id="freemarkerTemplateLoader" class="com.monator.freemarker.service.FreemarkerTemplateLoader" />
    
<bean id="freemarkerTemplateLoaderList" class="java.util.ArrayList">
    <constructor-arg>
        <list>
            <ref bean="freemarkerTemplateLoader"/>
        </list>
    </constructor-arg>
</bean>
 
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer" p:preferFileSystemAccess="false" >
    <property name="preTemplateLoaders" ref="freemarkerTemplateLoaderList" />
</bean>

 

As stated in the beginning of this blog post I have created a template loader that reads a template from the Document Library in your Liferay Portal in the same way as I described above. More detailed information on how it works and how it was done is presented in a later blog post.

So there you have it! How you easily can use Freemarker instead of JSP in your portlet and how to use you own custom template loader for reading Freemarker templates from your source of choice. Now this isn’t stopping at just portlets, you can also use Freemarker as a template language instead of Velocity for Liferay themes as well. Mika Koivisto has an excellent blog post on how to do this: http://www.liferay.com/web/mika.koivisto/blog/-/blogs/using-freemarker-in-your-theme-templates. The same goes for WCM templates, Liferay supports Freemarker as a template language there also. With all of these combined one could get a homogeneous template language to use throughout the whole portal.

Liferay Trainings in Helsinki 2013

Erik and I just arrived home to Gothenburg from one and a half week of Liferay training in Helsinki where we held the Liferay Administration Training, the Liferay Developer Training and Liferay System Administrator Training. Erik presented most of the material in the Admin and Developers training. I presented most of the content for the System Administrator training since I have installed and configured a lot of production installations of Liferay and have good knowledge in this domain area. It was three good trainings and I think that the attendees were happy with their training. We had some good discussions about different Liferay topics. I think this is one of the best parts of the training, when the attendees have the opportunity to discuss what they think is interesting for them in liferay and also tell us what their experience is about Liferay. That feedback is very interesting to hear about.

We had some time left in the end of the last training, the System Administrator, so Erik and I added an extra part about installing jMeter, create a load test and use VisualVM to tune a Liferay installation. I think that this part was especially appreciated. 

Helsinki is a very nice city that I began to appreciate during my stay there. I didn’t have that much time to see the city but the central area that got the chancet to explore was especially nice. Maybe a little to cold now when the spring just started to come here in Gothenburg. Hopefully we will have a successful Liferay training next year in Helsinki aswell. 

Showing 1 - 5 of 17 results.
Items per Page 5
of 4



Liferay on Twitter

What are people saying about Liferay on Twitter?