Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin

模板脚本绘制通常为HTML形式的输出。脚本通过FreeMarker或JSP这样的模板语言来编写,它可以指导绘制程序将内容放到页面的什么位置,包含了为类似标题和图片的内容提供的占位符。

Children Display

模板脚本绘制输出。通常这种输出是HTML。脚本用如FreeMarker或JSP这样的模板语言编写。脚本指导绘制者将内容放到页面的什么位置,并为类似标题和图片的内容提供占位符。

Table of Contents

脚本语言和绘制器

Magnolia CMS为FreeMarker和JSP提供可立即使用的绘制器。您可以选择偏好的语言,甚至可以在相同的网站上混合使用,在一些模板里使用FreeMarker而另一些里用JSP。 

Freemarker

At Magnolia we prefer Freemarker for its flexibility, cleaner syntax and better error reporting, but also because it does not depend on the file system. Templates do not have to be extracted to the file system. This means you can store them in the repository and access like any other resources, apply version control if you wish, and add custom properties and metadata.

Example: Render the page title, or render the name if no title exists.

Code Block
<h3>${content.title!content.@name}</h3>

Here are some of the benefits that Freemarker offers:

  • It is a general templating language and not bound to any Java context.
  • Provides basic directives such as [if][else] and [list].
  • Has a large set of built-ins.
  • Can call public methods of any Java object and any returned object.
  • Does not need to be rendered in the request/response context.
  • Provides direct access to node objects (beans).
  • Can directly iterate any content collections that extend a Java list.
  • Strict null value handling leads to stable templates.
  • Allows the use of JSP taglibs if required.

JSP

JSP stands for JavaServer Pages. It is an extension of Java Servlet technology for combining Java server-side programs and HTML.

Example: Render the page title or the name if no title exists.

Code Block
<h3>
<c:choose>
    <c:when test="${not empty content.title}">${content.title}</c:when>
    <c:otherwise>${content['@name']}</c:otherwise>
</c:choose>
</h3>

Other languages

You can also use another templating language. Some sites use Apache Velocity, for example. If a renderer for your language is available, you can most likely incorporate it into Magnolia CMS. This allows you to use a templating language that you are already familiar with.

Renderer configuration

FreemarkerRenderer and JspRenderer are configured in the Configuration app /modules/rendering/renderers/freemarker and /jsp.

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

...

modules

...

rendering

...

renderers

...

freemarker

...

contextAttributes

...

jsp

...

contextAttributes

...

The STK defines its own renderer STKRenderer configured in the Configuration app > /modules/standard-templating kit/rendering/renderers/stk.

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

...

modules

...

standard-templating-kit

...

renderers

...

stk

...

contextAttributes

...

The renderType property in pagearea and component definitions defines the template renderer to use. For STK templates the type is stk.

Loading scripts

The system can load Freemarker scripts from three places in this order:

  1. Filesystem of the webapp. You can place a script file in /<CATALINA_HOME>/webapps/<contextPath>/templates folder.
  2. templates workspace. The template needs to be enabled to be considered.
  3. Web application's classpath. This is where default STK templates are located. Best practice is to package scripts inside the module JAR. A module JAR resides in the classpath so the system finds it.

Template loaders

Freemarker template loaders are configured in the Configuration app > /server/rendering/freemarker/jcr and /webapp:

You can also write your own custom template loader if needed.

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

...

server

...

filters

...

IPConfig

...

i18n

...

security

...

rendering

...

freemarker

...

templateLoaders

...

jcr

...

webapp

...

Tip

JSP scripts can only reside on the file system because they require pre-compiling by the server before they can be rendered. JSP scripts are extracted onto the file system during module installation or update

Editing scripts

Panel
bgColor#ffffff
titleBGColor#ffffff
titleBest practice
Multiexcerpt
MultiExcerptNamestoring scripts

The best practice is to store scripts in your project module. This way the scripts can be stored in a version control system and are automatically part of a controlled software development lifecycle. You can edit them on the file system in your favorite editor that supports syntax highlighting and other helpful features.

While doing development it is sometimes useful to edit scripts in the repository. This method only works with Freemarker scripts. If you bootstrap scripts into the templates workspace they become visible and editable in STK > Templates. All STK scripts follow this model. As you can see from the loading order above a script in the templates workspace will override a script loaded from the classpath. Once added or modified, it is necessary to select the Enable template checkbox to force Magnolia CMS to load the template from the repository.

Image Removed

 

Warning

Editing template scripts in the repository is ideal for evaluation, prototyping or smaller projects. However, it is not maintainable in the long run. Don't do it in a production environment. We strongly recommend that you store the scripts in a version control system and package them into a project module.

 

STK scripts

STK template scripts are in the classpath of the webapp. Copies are editable in-place in STK > Templates. STK scripts are written in FreeMarker.

The scripts are stored in child folders of the /templating-kit folder:

  • components contains component templates sorted by component type.
  • pages contains the main page script and area scripts in subfolders.

This structure follows the template prototype structure. Area templates referenced in the template prototype are stored in the /global folder.

Image Removed

main script

main is the the master page script (Git). It is a good starting point to understand the system. It initializes the page editor on the author instance using the @cms.init directive. It also contains the logic to set the bodyClass and bodyID.

The main script calls areas to render themselves using the @cms.area directive. It renders the navigation using the #include directive. The main script wraps the areas in three DIV elements that creates a flexible page grid.

Area scripts

See STK areas for descriptions of STK area scripts.

Directives

Key templating features are available as directives in the Magnolia CMS 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

Code Block
[#if content.header?has_content]
   <h1>${content.header}</h1>
[#else]
   DO_SOMETHING_ELSE
[/#if]
Value comparison
Code Block
[#if content.imageLocation == "top"]
   …
[/#if]
Alternatives
Code Block
[#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.

Code Block
[#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.

Code Block
[#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. .

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

macros

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

Code Block
[#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 CMS provides the following custom directives:

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:

Code Block
[@<tag library>.<macro> <parameter>=<value> /]

Freemarker example: Render a component

Code Block
[@cms.component content=component /]

JSP example: Render a component

Code Block
<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.

Example:

Code Block
[@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.

Example:

Code Block
[@cms.area name="main"/]

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

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

Image Removed

If the area definition contains a templateScript property then the referenced script renders the area. If no script is given then the following default scripts are used instead:

Area type single

Code Block
[@cms.component content=component /]

Area type list

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

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.

Image Removed

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:

Code Block
[#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 content node it should operate on. Scripts typically operate on the "current" content 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 content 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 content node, which is a page because I am a page-level script".

Code Block
<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 content node which is (still) the page".

Code Block
<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.

Image Removed

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 content node such as myClass. Name it after the purpose of your class.
  5. Under myClass, create two data nodes:
    • 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.

Code Block
[#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:

Code Block
[#assign asset = damfn.getAssetForId(content.link)][#assign assetMap = damfn.getAssetMapForAssetId(content.link)]

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.

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

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

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

 

 

...

modules

...

rendering

...

renderers

...

freemarker

...

contextAttributes

...

cms

...

cmsfn

...

jsp

...

listeners

...

standard-templating-kit

...

renderers

...

stk

...

contextAttributes

...

cms

...

cmsfn

...

stkfn

...

damfn

...

 

 

Freemarker 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: substringuncap_firstcapitalizedate, time, datetimeends_withhtmlindex_oflast_index_oflengthlower_caseupper_casecontainsreplacestarts_withtrim.

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:

Code Block
[#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.

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

eval evaluates the passed Freemarker code.

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

Java objects

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

content: the current content node.

Code Block
${content.header!}

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

Code Block
${model.navigation!}

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

Code Block
${def.headingLevel!}

ctx: See WebContext.

Code Block
${ctx.user.name!}

state: See AggregationState.

Code Block
${state.locale!}

Checking for null

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 content node's name.

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

You can also specify the value.

Code Block
[#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.

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

Templating examples

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

Code Block
[#-- 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 paragraph rendering: model --]
The parent model: {model.parent}
The result of the execute method: ${actionResult}

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

References

Useful links to get started with Freemarker:

...