These are template scripting examples using the FreeMarker language. 

Basics

Useful links to get started with FreeMarker:

Directives

Key templating features are available as directives in the Magnolia tag library. Directives are quick to type but can render complex output.

Standard FreeMarker directives

Here are the most useful FreeMarker directives with sample code:

if, else and elseif

Common operators ( &&, ||, !, ==, !=, >, <, >=, <= ) are supported

Boolean test

[#if content.header?has_content]
   <h1>${content.header}</h1>
[#else]
   DO_SOMETHING_ELSE
[/#if]
Value comparison
[#if content.imageLocation == "top"]
   …
[/#if]
Alternatives
[#if content.date?has_content]
   ${content.date?time?string.short}
[#elseif content.endDate?has_content]
   ${content.endDate?time?string.short}
[#else]
   No date is set!
[/#if]

list

Can iterate over any collection that extends a Java collection.

[#list model.getSomeList() as elem]
   <li>${elem.title!}</li>
[/#list] 

assign

Assign allows you to define variables. Any object except null can be passed to a variable.

[#assign title = content.title!content.@name]
[#assign hasDate = content.date?has_content]
[#assign dateShort = content.date?time?string.short]
[#assign events = model.events]
[#assign stringgy = "Some direct string data"]

include

The include directive includes a FreeMarker template script. .

[#include "/templating-kit/templates/content/myScript.ftl"]

macros

Macros allow you to reuse snippets of FreeMarker code. See STK  > Templates /templating-kit/components/macros for examples used in the STK.

[#macro test foo bar="Bar" baaz=-1]
    Test text, and the params: ${foo}, ${bar}, ${baaz}
[/#macro]
[@test foo="a" bar="b" baaz=5*5-2/]
[@test foo="a" bar="b"/]
[@test foo="a" baaz=5*5-2/]
[@test foo="a"/]

Custom Magnolia directives

Magnolia provides the following custom directives:

  • cms:init: embeds JavaScript and CSS needed to author pages.
  • cms:area renders an area.
  • cms:component renders a component

For FreeMarker, these directives are implemented by the Directives class. This class is configured in the Configuration app > modules/rendering/renderers/freemarker/contextAttributes/cms/componentClass.

For JSP, the directives are provided by the Templating JSP module.

The directive syntax differs slightly depending on the templating language. FreeMarker directives start with the # character in the case of standard directives and with the @ character for custom directives. All directives in the Magnolia cms tag library start with @. What follows is the tag library name such as cms, a dot character, the name of the macro, and any parameters. In JSP the limiting characters are different.

Syntax:

[@<tag-library>.<macro> <attribute>=<value> /]

Example: Rendering a component

Freemarker
[@cms.component content=component /]
JSP
<cms:component content="${component}"/>

cms:init

The InitElement Java class embeds the JavaScript and CSS needed to edit pages on the author instance. The output goes in the head element in the page HTML.

Freemarker
[@cms.init /]

cms:area

The cms:area directive (AreaDirective) renders an area and any components inside it. Editors can add components inside the area. Available components are configured in the area definition.

Freemarker
[@cms.area name="main"/]

The directive references an area by its name. The area name is the node that contains the area definition such as mainfooter or stage.

The result on the page is an area bar and an end marker. The title property is rendered on the bar. When an editor clicks the Add icon in the New Component box they can add components inside the area.

Attributes:

AttributeDescriptionValue
nameName of the area definition node such as mainfooter or stage.String
createAreaNodeCreates a node for the area in the repository when set to true. See Area properties. (warning) Magnolia 5.2.4+true (default)
false 
contextAttributes

Any custom attribute and its value as retrieved from the current context.

[@cms.area name="horizontalTabItems" contextAttributes={"divIDPrefix":divIDPrefix, "componentModel":model} /]
A hash of key-value pairs.
templateScript

Optional. A script that renders the area. If no script is given then the following default scripts are used:

Area type single
[@cms.component content=component /]
Area type list
[#list components as component]
   [@cms.component content=component /]
[/#list]
 Path to template script.

cms:component

The cms:component directive (ComponentDirective) renders a component. The content attribute defines what content the component edits. This tag is commonly used inside the list directive to loop through the components in a map.

The content to render, and possibly edit in case of an editable component, is passed in the content attribute. On the author instance the directive renders a component toolbar. The value of the title property in the component definition is rendered on the bar.

AttributeDescriptionDefault Value
editableDefines whether edit icons should be displayed. Mainly useful if content is inherited.cmsfn.isFromCurrentPage()
templateName of the component definition to useThe template defined in the node.

Example:

[#list components as component ]
   [@cms.component content=component /]
[/#list]

Common directive attributes

The following attributes can be passed with any directive. They define which content the element created by the directive should work on.

AttributeDescriptionDefault value
contentA Node or ContentMap. 
workspaceWorkspace used if path is defined.Same as of the current content
pathPath in the workspace. 

Content attribute

The content attribute tells a script which node it should operate on. Scripts typically operate on the "current" node. For a page-level script the current node is the page, for an area-level script the current node is the area, and for a component-level script the current node is the component. However, there are cases where you want the script to operate on a different node. This is where the content attribute comes handy.

For example, the intro area has no content of its own. It doesn't contain any components either since it is of type noComponent. The area operates on page content instead. It edits and renders the page title and abstract. We achieve this by using the content attribute.

In the main.ftl script we tell the main area "You should operate on the current node, which is a page because I am a page-level script".

<div id="wrapper-3">
    [@cms.area name="platform"/]
    [@cms.area name="main" content=content/]
    [@cms.area name="extras"/]
</div>

In the mainArea area script we again pass the same instruction down to the intro area: "You should operate on the current node which is (still) the page".

<div id="main" role="main">
    [@cms.area name="breadcrumb" content=content/]
    [@cms.area name="intro" content=content/]
    [@cms.area name="opener"/]
    [@cms.area name="content"/]
</div><!-- end main -->

Now the intro area edits page content. Although the intro area resides inside the main area DIV element on the page, the title and the abstract really belong to the page. They are the page's properties, not the area's. So it makes sense to store those properties under the page node in the content structure.

Workspace attribute

The workspace attribute tells the directive which workspace of the magnolia JCR repository the content resides in. This is almost always the website workspace and defaults to website automatically if the current content resides in the website workspace.

Example of directive rendering

Here is an example how directives are rendered on the page.

  1. The main script contains a cms.init directive which embeds the necessary CSS and JavaScript on the author instance.
  2. The cms.area directive calls an area to be rendered. The directive identifies the area by name, in this case extras. If the area has child areas you need a separate script which calls the children to be rendered. However, if the area contains only components you don't need an area script.
  3. The footer of the page is also rendered with an cms.area directive. This area does not have child areas, only components.

Adding your own directives

You can add your own directives. They make Java methods and functions in your own classes available to template scripts:

  1. Write and compile your Java class as you normally would.
  2. Copy the class file to WEB-INF/classes folder of your Magnolia web application.
  3. Go to the Configuration app > /modules/rendering/renderers/freemarker/contextAttributes.
  4. Under /contextAttributes, create a node such as myClass. Name it after the purpose of your class.
  5. Under myClass, create two properties:
    • componentClass and set its value to the fully-qualified name of the class you placed in WEB-INF/classes.
    • name and set the value to myClass.

This allows you to access all the static methods in myClass from templates using the ${myClass.myMethod()} FreeMarker syntax.

Templating functions

TemplatingFunctions  includes useful methods that you can use in your templates. The methods are exposed as cmsfn. The decode method that removes escaping of HTML on properties is is an example and the snippet below shows its use in the stkTextImage component script.

[#if content.text?has_content]
   ${cmsfn.decode(content).text}
[/#if]

DamTemplatingFunctions  provides direct access to Assets and defines useful methods. The methods are exposed as damfn. See DAM templating for more information. For example:

[#assign asset = damfn.getAssetForId(content.identifier)]
[#assign assetMap = damfn.getAssetMap(asset)]

Magnolia 5.3 introduced a new DAM API. For Magnolia 5.2 please see DAM templating 1.x for examples.

STKTemplatingFunctions  makes additional methods available for use in STK templates. The methods are exposed as stkfn. The snippet below from the promos component script contains the abbreviateString example.

[#assign text = stkfn.abbreviateString(text, 80)]

The templating functions classes are configured for each renderer in /<module>/rendering/renderers/<renderer>/contextAttributes/<tag library>. Note that the <tag library> node and the value of the name property matches the syntax used to expose the methods in scripts.

Node nameValue

 modules

 

 rendering

 

 renderers

 

 freemarker

 

 contextAttributes

 

 cms

 

 cmsfn

 

 componentClass

info.magnolia.templating.functions.TemplatingFunctions

 name

cmsfn

 class

info.magnolia.rendering.renderer.FreemarkerRenderer

 type

freemarker

 jsp

 

 listeners

 

 version

5.0.2

 standard-templating-kit

 

 renderers

 

 stk

 

 contextAttributes

 

 cms

 

 cmsfn

 

 stkfn

 

 componentClass

info.magnolia.module.templatingkit.functions.STKTemplatingFunctions

 name

stkfn

 damfn

 

 class

info.magnolia.module.templatingkit.renderers.STKRenderer

Useful methods

(warning) The lists below are not exhaustive. See Javadocs for additional methods.

TemplatingFunctions

MethodDescriptionReturn
contentByPath(String path)Returns the ContentMap for the given path from the website repositoryContentMap
contentByPath(String path, String workspace)Returns the ContentMap for the given path from the given repositoryContentMap
contentById(String id)Returns the ContentMap by the given identifier from the website repositoryContentMap
page(ContentMap content) Returns the page's ContentMap of the passed ContentMapContentMap
decode(ContentMap content)Removes escaping of HTML on propertiesContentMap
parent(ContentMap contentMap) ContentMap
parent(ContentMap contentMap, String nodeTypeName) ContentMap
asContentMapList(Collection<javax.jcr.Node> nodeList)Wraps a Node collection inside a ContentMap ListList<ContentMap>
children(ContentMap content) List<ContentMap>
children(ContentMap content, String nodeTypeName) List<ContentMap>
nodeByPath(String path)Returns the Node for the given path from the website repositoryNode
nodeByPath(String path, String workspace)Returns the Node for the given path from the given repositoryNode
nodeById(String id)Returns the Node by the given identifier from the website repositoryNode
asJCRNode(ContentMap contentMap)Unwraps a ContentMap to a NodeNode
decode(javax.jcr.Node content)Removes escaping of HTML on propertiesNode

encode(javax.jcr.Node content)

Adds escaping of HTML on properties and changed line breaks into <br> tagsNode
children(javax.jcr.Node content, String nodeTypeName) Node
parent(javax.jcr.Node content) Node
parent(javax.jcr.Node content, String nodeTypeName) Node
children(javax.jcr.Node content) List<Node>
metaData(ContentMap content, String property)Wraps a Node instance inside a ContentMap String

metaData(javax.jcr.Node content, String property)

Returns the string representation of a property from the metaData of the nodeString
link(javax.jcr.Node content)  String
link(ContentMap contentMap)  String
link(String workspace, String nodeIdentifier) Creates a link for the Node identified by nodeIdentifier in the specified workspaceString
language Gets the language currently usedString
isEditMode  Boolean
isPreviewMode Boolean
isAuthorInstance Boolean
isPublicInstance Boolean
siblings(ContentMap node)  SiblingsHelper

siblings(javax.jcr.Node node)

Returns an instance of SiblingsHelper (utility class) for the given nodeSiblingsHelper

STKTemplatingFunctions

MethodDescriptionReturn
siteRoot(info.magnolia.jcr.util.ContentMap content)Returns the site's root ContentMap of the @param content ContentMapContentMap
siteRoot(info.magnolia.jcr.util.ContentMap content, String siteRootTemplateCategory)Returns the site's root Node of the passed @param content NodeContentMap
siteRoot(javax.jcr.Node content)Returns the site's root Node of the @param content NodeNode
siteRoot(javax.jcr.Node content, String siteRootTemplateCategory)Returns the site's root Node of the passed @param content NodeNode
getReferencedContent(javax.jcr.Node content, String idPropertyName, String referencedWorkspace)Returns a Node object which is referenced by its id, stored in the @param propertyNameNode
site(info.magnolia.jcr.util.ContentMap content)Returns the Site corresponding to the passed content ContentMapSite
site(javax.jcr.Node content)Returns the Site corresponding to the passed content NodeSite
homeLink(info.magnolia.jcr.util.ContentMap content) String
homeLink(javax.jcr.Node content)Returns the home link of this siteString

For asset related methods see DAM templating.

Built-ins

FreeMarker provides a powerful set of built-ins. These are used for basic manipulation of data and no Java code is necessary. Built-ins are used with a preceding ? character. For example ?exists checks if a value/object exists and ?has_content checks if a value/object is empty and exists.

Strings

Most Java String are implemented and can be used directly in FreeMarker. Examples:  substring uncap_first capitalize date, time, datetime ends_with html index_of last_index_of length lower_case upper_case contains replace starts_with trim .

Booleans

String (when used with a boolean value) converts a boolean to a string. You can use it in two ways:

  • foo?string converts the boolean to string using the default strings to represent true and false values.
  • foo?string("yes", "no") returns the first parameter "yes" if the boolean is true, otherwise the second parameter "no".

Dates

There are various built-ins for dates with formating capabilities. For example:

[#assign microFormatDate = content.date?string("yyyy-MM-dd") + "T" + ontent.date?string("hh:mm:ss")]

Expert

There are various expert built-ins. The most commonly used are:

has_content determines if HTML is rendered to avoid empty HTML tags.

[#if content.author?has_content]
 <p>
  <cite>${content.author}</cite>
 </p>
[/#if]

eval evaluates the passed FreeMarker code.

[#assign indexString = ('"'+(ctx.indexString!)+'"')?eval]

Java objects

These rendering context objects are set in  AbstractRenderer  and its child classes,

content: the current node.

${content.header!}

model: The example code below corresponds to getNavigation() method of the model class.

${model.navigation!}

def: The current page, area or component definition object.

${def.headingLevel!}

ctx: See  WebContext .

${ctx.user.name!}

state: See  AggregationState .

${state.locale!}

Checking for null values

Null checks stabilize your templates. FreeMarker throws an exception if null is encountered. There are two options:

Use the ! character to provide default values. The content after ! is executed.

This code tries to assign title from content, if not it falls back to the node's name.

<meta name="keywords" content="${content.keywords!content.title!content.@name}" />

You can also specify the value.

[#if content.keywordsEnabled!false]
     <meta name="keywords" content="${content.keywords!"These are some keywords"}" />
[/#if]

Or use the ?has_content built-in. The example renders the header in h1 tags if a value exists.

[#if content.header?has_content]
     <h1>${content.header}</h1>
[/#if]

Internationalization and localization

In an ftl-script you can retrieve translated keys of the bundle which you have defined by its i18nBasename in the template definition.

Node nameValue

  templates

 

 components

 

 your-component

 

 templateScript 

/reuse-templates/components/i18n-test.ftl

 i18nBasename

info.magnolia.module.documentation.contentreusing.templateMessages

The snippet below shows how you can access the translation of a certain key (for instance 'documentation-reuse-content.templates.i18n-test.testMessage'). Use  i18n to translate the key in the current language of a website visitor.

<div>${i18n['documentation-reuse-content.templates.i18n-test.testMessage']}</div>

(warning) Since Magnolia 5.3.6 FreemarkerHelper exposes also the locale of the authoring context. To display a message in the language of the currently logged in user (editor or administrator) use i18nAuthoring.

[#if cmsfn.editMode] <div>${i18nAuthoring['just-another-key']}</div> [/#if]

For more information concerning localization and internationalization see Language and its subpages (for instance Examples of new and old i18n API usage).

Examples

Here are the most common Freemarker examples for use in your template scripts:

[#-- Accessing content --]
The value of "someProperty": ${content.someProperty}
Accessing a child node: ${content.childNode}
Accessing the child node collection: ${content?children}
Accessing the parent node: ${content?parent}
[#-- Special content properties --]
The content object is an instance of ContentMap and the following attributes are available:
The current node name: ${content.@name}
The current node path: ${content.@path}
The current node id: ${content.@id}
The current node depth: ${content.@depth}
The current node node type: ${content.@nodeType}

[#-- MetaData --]
The creation date: ${content.metaData.creationDate}
Metadata.modificationDate: ${content.metaData.modificationDate!" 
This node has never been modified."}

[#-- Component definition --]
The current component definition: ${def.name}
A component definition property: ${def.style}

[#-- Context: ctx --]
A request parmeter: ${ctx.myParam}
The current user name ${ctx.user.name} 
The current locale ${ctx.locale}

[#-- TemplatingFunctions: cmsfn--]
Create a link to a page: ${cmsfn.link(content)}
Create a binary link: ${cmsfn.link(content.image)}

[#-- Status based rendering --]
This is ${cmsfn.authorInstance?string('indeed', 'not')} an author instance.
This is ${cmsfn.editMode?string('indeed', 'not')} the edit mode.
This is ${cmsfn.previewMode?string('indeed', 'not')} the preview mode.

[#-- The Model executed before the component rendering: model --]
The parent model: {model.parent}
The result of the execute method: ${actionResult}

[#-- AggregationState: state --]
Entry point of the rendering: ${state.mainContent}
Current node: ${state.currentContent}

 

metaData(ContentMap content, String property)

#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))