Page History
模板脚本绘制通常为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]
。 - 有大量的内建函数。
- 可以调用任何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
在配置应用的/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
,在配置应用的/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脚本,加载顺序如下:
- Web应用程序的文件系统。您可以将脚本文件放在
/<CATALINA_HOME>/webapps/<contextPath>/templates
文件夹。 templates
工作区。模板需先被使能才可以使用。- Web应用程序的类路径(classpath),这也是默认存放STK模板的地方。一个最佳实践是,在模块JAR里打包脚本(为方便系统找到,模块JAR在类路径里)。
模板加载器
FreeMarker模板加载器在配置应用的/server/rendering/freemarker/jcr
和/webapp
里配置:
LazyWebappTemplateLoader
默认从webapp文件夹里加载模板。如果没找到匹配的模板,则将会从类路径中加载。JcrRepoTemplateLoader
允许从内容库中加载模板。这个加载器在原位模板工具模块里。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 > 模板里可供使用。所有的STK脚本都遵循这个模式。您可以从以上的加载顺序看出,templates
工作区里的脚本会覆写从类路径里加载的脚本。所以脚本一旦被添加或修改,就需要选中使能模板复选框来强迫Magnolia CMS从内容库中加载模板。
Warning |
---|
在内容库里编辑模板脚本对评估、制造原型或较小的项目比较理想。然后,长期来说这不利于维护。不要在生产环境中这样做。我们强烈建议您在一个版本控制系统中存储脚本,并将它们打包进一个项目模块。 |
更多信息请参考模板样例 > 编辑模板脚本。
STK脚本
STK模板脚本在Web应用程序的类路径中,脚本副本在STK > 模板里就可以编辑。STK脚本是用FreeMarker写的。
脚本存储在/templating-kit
文件夹的子文件夹里:
components
包含按照组件类型分类的组件模板。pages
的子文件夹里有main
页面脚本和区域脚本。
此结构遵循模板原型结构。模板原型里引用的区域模板都存在/global
文件夹。
main脚本
main
是主页面脚本(Git),是理解这个系统的一个好起点。它在编写实例上使用@cms.init
指令初始化页面编辑器,同时也包含了设置bodyClass
和bodyID
的逻辑。
main脚本使用@cms.area
指令来调用区域绘制,并使用#include
指令绘制导航。main
脚本用三个DIV元件包装区域,建立了一个可调的页面网格。
区域脚本
参考STK区域里关于STK区域脚本的描述。
指令
关键的模板工具功能在Magnolia CMS标签库里,以指令(directives)形式供使用。指令输入起来很快,却可以绘制复杂的输出。
标准FreeMarker指令
这里有一些最有用的FreeMarker指令及其样例代码:
if, else和elseif
支持常用运算符(&&, ||, !, ==, !=, >, <, >=, <=)。
布尔测试:
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
可在任何Java集的扩展集合里进行迭代。
Code Block |
---|
[#list model.getSomeList() as elem]
<li>${elem.title!}</li>
[/#list] |
assign
assign允许您定义变量。任何非空对象都能传递给变量。
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
include指令包含一个FreeMarker模板脚本。
Code Block |
---|
[#include "/templating-kit/templates/content/myScript.ftl"] |
宏
宏指令让您可以复用FreeMarker代码片段。参考STK > 模板 /templating-kit/components/macros
里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"/] |
定制Magnolia指令
Magnolia CMS提供以下定制指令:
对FreeMarker来说,这些指令都是由Directives类来完成的。这个类在配置应用的modules/rendering/renderers/freemarker/contextAttributes/cms/componentClass
里配置。
对JSP,指令是由Templating JSP模块提供。
指令的语法随模板语言而略有不同。FreeMarker指令中,标准指令以“#”字符开头,而定制指令则以“@”符号开头。所有Magnolia cms
标签库中的指令都以“@”开头,紧接着是标签库的名字,如cms
,其次是一个点符号、宏的名字以及参数。在JSP里限制的字符则不同。
语法:
Code Block |
---|
[@<tag library>.<macro> <parameter>=<value> /] |
FreeMarker示例:绘制一个组件
Code Block |
---|
[@cms.component content=component /] |
JSP示例:绘制一个组件
Code Block |
---|
<cms:component content="${component}"/> |
cms:init
InitElement Java类嵌入了在编写实例上编辑页面所需的JavaScript和CSS。输出在HTML页面的head
元件里。
示例:
Code Block |
---|
[@cms.init /] |
cms:area
cms:area指令
(AreaDirective)绘制区域以及里面的组件。编辑者可在区域内添加组件。可用的组件在区域定义里配置。
示例:
Code Block |
---|
[@cms.area name="main"/] |
该指令通过区域名称来引用区域。区域名称为包含区域定义的内容节点,如main
,footer
或stage
。
页面上的结果是一个区域栏、一个开始标记和一个结束标记。区域定义里title
属性值在此区域栏里绘制。编辑者点击新建组件框的添加图标时,组件就添加到区域里了。
如果区域定义包含一个templateScript
属性,那么由被引用的脚本绘制区域。如果脚本没有给出,则使用以下默认脚本:
区域类型为single
Code Block |
---|
[@cms.component content=component /] |
区域类型为list
Code Block |
---|
[#list components as component]
[@cms.component content=component /]
[/#list] |
cms:component
cms:component
指令(ComponentDirective)绘制组件。content
属性(attribute)定义组件编辑的内容,通常用在list
指令里,在同一个map的组件里循环。
content
属性传递要绘制的内容,或可能要编辑的内容(对于可编辑组件来说)。在编写实例上,指令绘制一个组件工具栏。组件定义里的title
属性(property)值在工具栏中绘制。
属性(Attribute) | 描述 | 缺省值 |
---|---|---|
editable | 定义编辑图标是否应被显示,主要对继承的内容有用。 | cmsfn.isFromCurrentPage() |
template | 要用到的组件定义名。 | 节点里定义的模板。 |
示例:
Code Block |
---|
[#list components as component ]
[@cms.component content=component /]
[/#list] |
常用指令属性
下列属性(attributes)可用任何指令传递,它们定义指令创建的元件应该工作在什么内容上。
属性 | 描述 | 缺省值 |
---|---|---|
content | 一个节点或是内容地图。 | |
workspace | 指定路径时用到的工作区。 | 与当前内容相同 |
path | 工作区中的路径。 |
content属性
content
属性告诉脚本它应该在哪个内容节点上操作。脚本通常在“当前”内容节点上操作。对于一个页面级脚本,当前节点为该页面;而对于一个区域级脚本,当前节点就是该区域。同样,对于一个组件级脚本,当前节点就是该组件。然后,也有一些情况,如您想要脚本在一个不同的内容节点上操作,这时content
属性就变得方便实用。
例如,intro区域没有它自己的内容,也不包含任何组件,因为它是noComponent
类型的。我们将通过使用content
属性来实现让这个区域在页面内容上操作,编辑和绘制页面标题及摘要。
在main.ftl
脚本里,我们告诉main区域:“你应该在当前内容节点上操作,因为它是一个页面,而我是一个页面级脚本。”
Code Block |
---|
<div id="wrapper-3">
[@cms.area name="platform"/]
[@cms.area name="main" content=content/]
[@cms.area name="extras"/]
</div> |
在mainArea
区域脚本里,我们再次传递相同的信息给intro区域:“你应该在当前内容节点上执行,它(仍然)是一个页面。”
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 --> |
现在由intro区域编辑页面内容。尽管intro区域在页面上位于main区域的DIV元件里,标题和摘要实际上是属于页面的。它们是页面的而非区域的属性,所以应当将它们存放在内容结构里的页面节点下。
workspace属性
workspace
属性(attribute)告知指令,内容在magnolia
JCR库的哪个工作区。实际上,几乎总是website
工作区;如果当前内容在website
工作区里,则自动默认指向website。
指令绘制示例
这是一个关于指令在页面上如何绘制的例子。
main
脚本包含一个cms.init
指令,嵌入了编写实例上所需的CSS和JavaScript。cms.area
指令调用即将绘制的区域。该指令通过名字来辨认区域,在此例中为extras
。如果该区域有子区域,您需要一个单独的脚本来调用要绘制的子区域。然而,如果该区域只包含组件,那么您不需要区域脚本。- 页面的页脚同样使用
cms.area
指令来绘制。该区域不含子区域,只有组件。
添加您自己的指令
您可以添加自己的指令。执行以下步骤,可以使您自己的类中的Java方法和功能为模板脚本所用:
- 像通常一样写作和编译您的Java类。
- 拷贝这个类文件到您的Magnolia Web应用程序的
WEB-INF/classes
文件夹。 - 转到配置应用
/modules/rendering/renderers/freemarker/contextAttributes
。 - 在
/contextAttributes
下,创建一个内容节点,如myClass
。可以用类的用途命名。 - 在
myClass
里,创建两个数据节点: componentClass
,设置值为您放在WEB-INF/classes
里的类的完全相称类名。name
,设置值为myClass
。
这样,您就能够使用${myClass.myMethod()
} FreeMarker句法从模板获得myClass
里所有的静态方法了。
模板功能
TemplatingFunctions
包含了您可以在模板中可用的有用方法,这些方法以cmsfn
出现。例如,decode
方法可以从属性(properties)上移除HTML换码,下列代码片段展示了这个方法在stkTextImage
组件脚本中的使用。
Code Block |
---|
[#if content.text?has_content]
${cmsfn.decode(content).text}
[/#if] |
DamTemplatingFunctions
提供到Assets
的直接权限以及定义有用的方法,这些方法以damfn
出现。更多信息参考DAM模板。例如:
Code Block |
---|
[#assign asset = damfn.getAssetForId(content.link)][#assign assetMap = damfn.getAssetMapForAssetId(content.link)] |
STKTemplatingFunctions
使附加的方法在STK模板里都可以使用,这些方法以stkfn
出现。以下promos
组件脚本片段包含了abbreviateString
示例。
Code Block |
---|
[#assign text = stkfn.abbreviateString(text, 80)]
|
每个绘制器的模板功能类在配置应用的/<module>/rendering/renderers/<renderer>/contextAttributes/<tag library>
下配置。注意<tag library>
内容节点和name
数据的节点值与脚本里呈现方法的句法相匹配。
...
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内建函数
FreeMarker提供一套强大的内建函数。它们可用作基本数据操作,不需要任何Java代码。内建函数在使用时前面加一个“?
”字符。例如, ?exists
用于检查一个值或对象是否存在,?has_content
检查一个值或对象是否存在且为空。
字符串
大多数Java字符串在FreeMarker里都能实现并直接使用。例如: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
。
布尔值
使用了布尔值的字符串将一个布尔值转化为一个字符串。您可以通过以下两种方法使用它:
foo?string
使用默认字符来代表true和false值,将布尔值转化为字符串。foo?string(
“yes
”,“no
”)
在布尔值为真时返回第一个参数“yes”,否则,返回第二个参数“no”。
日期
日期内建函数有多种格式,例如:
Code Block |
---|
[#assign microFormatDate = content.date?string("yyyy-MM-dd") + "T" + ontent.date?string("hh:mm:ss")] |
专家级内建函数
还有一些专家级内建函数。最常用的有:
has_content
决定HTML是否被绘制,以避免HTML标签。
Code Block |
---|
[#if content.author?has_content]
<p>
<cite>${content.author}</cite>
</p>
[/#if] |
eval
估算传递的FreeMarker代码。
Code Block |
---|
[#assign indexString = ('"'+(ctx.indexString!)+'"')?eval] |
Java对象
这些绘制上下文对象在AbstractRenderer
及它的子类中设置:
content
:当前的content
节点。
Code Block |
---|
${content.header!} |
model
:下面的样例代码对应模型类的getNavigation()
方法。
Code Block |
---|
${model.navigation!} |
def
:当前页面,区域或组件定义对象。
Code Block |
---|
${def.headingLevel!} |
ctx
:参看WebContext。
Code Block |
---|
${ctx.user.name!} |
state
:参看AggregationState
。
Code Block |
---|
${state.locale!} |
检查null值
检查null值使您的模板更加稳定,因为FreeMarker遇到null值使会报告例外。检查有两种方法:
第一种方法是,使用“!
”字符来提供缺省值,“!
”后的内容被执行。
例如,下列代码想要从内容里分配title
,如果不成功,它将返回到该内容节点名(name
)。
Code Block |
---|
<meta name="keywords" content="${content.keywords!content.title!content.@name}" /> |
您也可以指定值:
Code Block |
---|
[#if content.keywordsEnabled!false]
<meta name="keywords" content="${content.keywords!"These are some keywords"}" />
[/#if] |
第二种方法是使用?has_content
内建函数。下例在有值的情况下在h1
标签里绘制header
。
Code Block |
---|
[#if content.header?has_content]
<h1>${content.header}</h1>
[/#if] |
模板样例
这是最常用在您的模板脚本里的FreeMarker样例:
...
Children Display |
---|
...
参考
以下链接里的内容对FreeMarker初学者有帮助:
...