FreeMarker template language

FreeMarker is the template language used with Funnelback’s modern UI. FreeMarker is a generic template engine popular in the Java world (although you don’t need to know Java to write FreeMarker templates).

Its purpose is to generate an HTML output by merging the data model with a search template. Funnelback will take care of generating a relevant data model based on your query terms and collection and merge it with the template of your choice. All you need to do is write a template to display the search results.

FreeMarker template file ends with the .ftl extension (stands for FreeMarker Template Library). A default template is provided when you create a collection, named simple.ftl. It’s usually a good starting point for customization.

A FreeMarker template contains 3 element types, in addition to HTML code:

  • ${...} statements that will get replaced by data model values (e.g. ${result.title})

  • FreeMarker tags, either core directives like <#if [condition]> ... </#if> or custom tags like <@s.AfterSearchOnly> ... </@s.AfterSearchOnly>.

  • FreeMarker comments: <#-- ... -->

Data model variables

The ${...} syntax allows you to access and display content from the data model. You must use the full path of the data model node you want to display, for example:

Searching for ${question.query} on collection ${question.collection.id}
${response.resultPacket.resultsSummary.totalMatching} results found.

Accessing results page and search package configuration

The configuration object in Freemarker contains all the data source and search package configuration settings within a hidden data model object.

These configuration parameters can be used in Freemarker templates by accessing this object:

Freemarker template (e.g. simple.ftl)
question.currentProfileConfig.get("CONFIGURATION-KEY")

where CONFIGURATION-KEY is a key from the results page. This call has the ability to see all user defined parameters found in the configuration and all default values.

Example 1. Directly access the data model configuration object

Access the data model hidden object with code similar to:

Freemarker template (e.g. simple.ftl)
<#if question??
  && question.currentProfileConfig.get("spelling_enabled")??
  && is_enabled(question.currentProfileConfig.get("spelling_enabled")) >
  <#--- Insert code for when configuration parameter exists and is enabled --->
</#if>

In the Freemarker template:

Freemarker template (e.g. simple.ftl)
<p>Contact the search administrator: <a href="mailto:${question.currentProfileConfig.get"admin_email")?url}">${question.currentProfileConfig.get"admin_email")}</a></p>

would result in the following being printed out by the template:

<p>Contact the search administrator: <a href="mailto:webmaster@site.com">mailto:webmaster@site.com</a></p>

HTTP request information

FreeMarker templates also have access to a special variable httpRequest which is not part of the data model but contains useful information about the HTTP request. This variable is of type HttpServletRequest and its fields can be accessed like other data model nodes, e.g. request.requestURL.

FreeMarker built-in functions

FreeMarker has an extensive set of built-in functions that can be called when a value is printed. A built-in function is called by appending a question mark (?) to the variable name and adding the function. E.g. ${s.result.title?upper_case} prints the result title as an uppercase string. Function calls can also be chained. ${s.result.title?upper_case?esc} will convert the title to upper case then HTML encode it.

The built-in functions provide a rich set of tools that can be used to manipulate or transform the variables allowing a huge amount of flexibility to be built into the search result templates.

e.g.

HTML escaped query: ${question.query?esc}
Query transformation: ${question.query?replace("cat", "dog")}

The most commonly used built-in is !. It allows you to specify a default value if the value you’re looking for is not defined: ${question.query!"No query"}. This will default to "No query" if query is not defined. Note that if question is not defined the template rendering will fail with an error. To use the default value built-in over the whole expression you must use parenthesis: ${(question.query)!"No query"}. This way if question or query is undefined, the default value will be used instead.

Variable escaping

Each FreeMarker template has associated output format. The output format dictates the escaping rules. Escaping can be applied to a complete template or just part of it.

Automatic HTML escaping is turned on in Funnelback’s default template (simple.ftl), with the escape function called immediately after the <#import> directives. Configuring escaping is an important part of ensuring the template is secure as it prevents injection attacks affecting any of the escaped variables.

FreeMarker has built-in support for the automatic escaping of variables that are being represented in different format, for example HTML, JSON or URLs. When one of these functions is called the relevant special characters will be automatically escaped by FreeMarker.

This can be accessed using FreeMarker’s ?esc, ?json_string and ?url string built-in functions.

FreeMarker directives

FreeMarker provides range of directives that allows executing various operations on data for example conditional display (<#if>), iterating over list (<#list>) or reuse the same part of code (<#macro>). For more please see FreeMarker directives.

Funnelback custom tags

Funnelback ships with two libraries of FreeMarker macros, providing quick pre-built functions that can be used to build your search interface.

The libraries are loaded into the search template using <#import> directives located at the top of the template file.

<#ftl encoding="utf-8" />
<#import "/web/templates/modernui/funnelback_classic.ftl" as s/>
<#import "/web/templates/modernui/funnelback.ftl" as fb/>

The two import directives above import the funnelback_classic and funnelback macro libraries into the s and fb namespaces respectively.

The macros included within these libraries can be accessed once imported by prefixing the macro call with the namespace. The libraries contain functions that implement conditional logic (e.g. whatever appears inside this tag is run only after a search keyword is entered) or handle logic required to implement the display of a feature.

E.g. <@s.Results> calls the Results macro from the s namespace (which corresponds to the funnelback_classic macro library).

  • funnelback_classic: contains macros providing access to Funnelback’s core functionality.

  • funnelback: contains macros providing access to extended and newer functionality.

FreeMarker comments

You can use <#-- ... --> to indicate comments to FreeMarker. Those comments will not be processed by the template engine and won’t be shown in the resulting HTML output.

For example if your template contains a mix of HTML and FreeMarker comments as in:

<div class="title">
  <!-- This is the title -->
  <#-- We first check that it's not empty -->
  <#if r.title?exists>${r.title}</#if>
</div>

Then the HTML output will be (Assuming the title is "Title of the result"):

<div class="title">
  <!-- This is the title -->
  Title of the result
</div>

FreeMarker documentation

FreeMarker is extensively documented in its official documentation, the FreeMarker manual, available here: http://freemarker.org/

The following guides are of specific relevance for Funnelback templates:

ensure that you are viewing the manual for the correct version of FreeMarker as the available built-in functions can change from version to version. See: Funnelback - FreeMarker versions to check the version of FreeMarker used with your version of Funnelback.