Page History
模板脚本绘制通常为HTML形式的输出。脚本通过FreeMarker或JSP这样的模板语言来编写,它可以指导绘制程序将内容放到页面的什么位置,包含了为类似标题和图片的内容提供的占位符。
Children Display |
---|
模板脚本绘制输出。通常这种输出是HTML。脚本用如FreeMarker或JSP这样的模板语言编写。脚本指导绘制者将内容放到页面的什么位置,并为类似标题和图片的内容提供占位符。
Table of Contents |
---|
脚本语言和绘制器
Magnolia CMS为FreeMarker和JSP提供可立即使用的绘制器。您可以选择偏好的语言,甚至可以在相同的网站上混合使用,在一些模板里使用FreeMarker而另一些里用JSP。
FreeMarker
在Magnolia里,我们更倾向于使用FreeMarker,因其灵活性,较清晰的语法,更好的错误报告机制,也因为它不依赖于文件系统。模板不需要导出到文件系统,这意味着您可以将它们存储在内容库中,并像其他资源一样获得,随意添加版本控制,以及添加定制的属性和元数据。
样例:绘制页面标题,如果没有标题则绘制名字。
Code Block |
---|
<h3>${content.title!content.@name}</h3> |
这是FreeMarker提供的一些好处:
- 它是一种通用的模板语言,不束缚于Java环境。
- 提供基本的指令,如
[if]
,[else]和
[list]
。 - 有一大套built-ins。
- 可以调用任何Java对象和返回对象的公共方法。
- 不需要在请求/回复环境中绘制。
- 提供直接获取节点对象(beans)的方法。
- 可直接迭代任何扩展一个Java列表的内容集合。
- 严格的null值处理导致稳定的模板。
- 需要的话允许使用JSP标签库。
JSP
JSP代表JavaServer Pages,是Java Servlet技术的一个延伸,用于综合Java服务器端的程序和HTML。
样例:绘制页面标题,如果没有标题则绘制名字。
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> |
其他语言
您也可以使用另一模板语言。例如,有一些网站使用Apache Velocity。如果有您使用的语言可用的绘制器,很有可能可以将它合入Magnolia CMS。这就能够让您使用您熟悉的模板语言了。
绘制器配置
FreemarkerRenderer和
JspRenderer
在Configuration应用的/modules/rendering/renderers/freemarker
和/jsp
里配置。
...
heading | 0 |
---|---|
multiple | false |
enableHeadingAttributes | false |
enableSorting | false |
class | m5-configuration-tree |
enableHighlighting | false |
...
modules
...
rendering
...
renderers
...
freemarker
...
contextAttributes
...
jsp
...
contextAttributes
...
STK定义它自己的绘制器STKRenderer
,在Configuration应用 > /modules/standard-templating kit/rendering/renderers/stk
里配置。
...
heading | 0 |
---|---|
multiple | false |
enableHeadingAttributes | false |
enableSorting | false |
class | m5-configuration-tree |
enableHighlighting | false |
...
modules
...
standard-templating-kit
...
renderers
...
stk
...
contextAttributes
...
页面, 区域和组件定义里的renderType
属性定义使用的模板绘制器。对STK模板来说,类型是stk
。
加载脚本
系统可以从三个地方加载FreeMarker脚本,以下列顺序:
- 网络应用程序的文件系统。您可以将脚本文件放在
/<CATALINA_HOME>/webapps/<contextPath>/templates
文件夹。 templates
工作区。模板需被使能才能被考虑进去。- 网络应用程序的类路径(classpath)。这是STK模板默认存放的地方。最佳实践是在模块JAR里打包脚本。模块JAR在类路径里好让系统找得到它。
模板加载器
FreeMarker模板加载器在Configuration应用的/server/rendering/freemarker/jcr
和/webapp
里配置:
LazyWebappTemplateLoader
默认从webapp文件夹里加载模板。如果没找到匹配的模板,则将会从类路径中加载。JcrRepoTemplateLoader
允许从内容库中加载模板。加载器包含在In-place Templating模块。LazyFileTemplateLoader
允许从文件系统的专有位置中加载模板。
需要的话您也可以写下您自己定制的模板加载器。
...
heading | 0 |
---|---|
multiple | false |
enableHeadingAttributes | false |
enableSorting | false |
class | m5-configuration-tree |
enableHighlighting | false |
...
server
...
filters
...
IPConfig
...
i18n
...
security
...
rendering
...
freemarker
...
templateLoaders
...
jcr
...
webapp
...
Tip |
---|
JSP脚本只能存在于文件系统中,因为它们在被绘制前需要服务器预编译。JSP脚本在模块安装或更新时被解压到文件系统。 |
编辑脚本
Panel | ||||||
---|---|---|---|---|---|---|
| ||||||
|
开发时,在内容库里编辑脚本有时候很有用。这种方法只对FreeMarker脚本有效。如果您将脚本导入templates
工作区,它们就会在STK > Templates里可见可用。所有的STK脚本都遵循这个模式。您可以从以上的加载顺序看出,在templates
工作区里的脚本会覆写从类路径里加载的脚本。一旦被添加或修改,则需要选中Enable template复选框来强迫Magnolia CMS从内容库中加载模板。
Warning |
---|
在内容库里编辑模板脚本对评估,制造原型,或较小的项目比较理想。然后,长期来说这不利于维护。不要在生产环境中这样做。我们强烈建议您在一个版本控制系统中存储脚本,并将它们打包进一个项目模块。 |
参考模板样例 > 编辑模板脚本里更多信息。
STK脚本
STK模板脚本在网络应用程序的类路径中。其副本在STK > Templates里就可以编辑。STK脚本是用FreeMarker写的。
脚本存储在/templating-kit
文件夹的子文件夹里:
components
包含按照组件类型分类的组件模板。pages
包含main
页面脚本,区域脚本在子文件夹里。
此结构遵循模板原型结构。在模板原型里引用的区域模板都被存在/global
文件夹里。
main脚本
main
是主页面脚本(Git)。它是理解这个系统的一个好起点。它在编写实例上使用@cms.init
指令初始化页面编辑器,也包含了设置bodyClass
和bodyID
的逻辑。
main脚本使用@cms.area
指令来调用区域绘制它们自身。它使用#include
指令绘制导航。main
脚本用三个DIV元件包装区域,建立了一个可调的页面网格。
区域脚本
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] |
Code Block |
---|
[#if content.imageLocation == "top"]
…
[/#if] |
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:
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:
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 main
, footer
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.
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.
Attribute | Description | Default Value |
---|---|---|
editable | Defines whether edit icons should be displayed. Mainly useful if content is inherited. | cmsfn.isFromCurrentPage() |
template | Name of the component definition to use | The 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.
Attribute | Description | Default value |
---|---|---|
content | A Node or ContentMap. | |
workspace | Workspace used if path is defined. | Same as of the current content |
path | Path 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.
- The
main
script contains acms.init
directive which embeds the necessary CSS and JavaScript on the author instance. - The
cms.area
directive calls an area to be rendered. The directive identifies the area by name, in this caseextras
. 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. - 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:
- Write and compile your Java class as you normally would.
- Copy the class file to
WEB-INF/classes
folder of your Magnolia web application. - Go to the Configuration app >
/modules/rendering/renderers/freemarker/contextAttributes
. - Under
/contextAttributes
, create a content node such asmyClass
. Name it after the purpose of your class. - Under
myClass
, create two data nodes: componentClass
and set its value to the fully-qualified name of the class you placed inWEB-INF/classes
.name
and set the value tomyClass
.
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.
...
heading | 0 |
---|---|
multiple | false |
enableHeadingAttributes | false |
enableSorting | false |
class | m5-configuration-tree |
enableHighlighting | false |
...
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: 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:
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:
...