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

在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。这就能够让您使用您熟悉的模板语言了。

绘制器配置

FreemarkerRendererJspRendererConfiguration应用的/modules/rendering/renderers/freemarker/jsp里配置。

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

...

modules

...

rendering

...

renderers

...

freemarker

...

contextAttributes

...

jsp

...

contextAttributes

...

STK定义它自己的绘制器STKRenderer,在Configuration应用的/modules/standard-templating kit/rendering/renderers/stk里配置。

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

...

modules

...

standard-templating-kit

...

renderers

...

stk

...

contextAttributes

...

页面区域组件定义里的renderType属性定义使用的模板绘制器。对STK模板来说,类型是stk

加载脚本

系统可以从三个地方加载FreeMarker脚本,以下列顺序:

  1. 网络应用程序的文件系统。您可以将脚本文件放在/<CATALINA_HOME>/webapps/<contextPath>/templates文件夹。
  2. templates 工作区。模板需被使能才能被考虑进去。
  3. 网络应用程序的类路径(classpath)。这是STK模板默认存放的地方。最佳实践是在模块JAR里打包脚本。模块JAR在类路径里好让系统找得到它。

模板加载器

FreeMarker模板加载器在Configuration应用的/server/rendering/freemarker/jcr/webapp里配置:

需要的话您也可以写下您自己定制的模板加载器。

...

heading0
multiplefalse
enableHeadingAttributesfalse
enableSortingfalse
classm5-configuration-tree
enableHighlightingfalse

...

server

...

filters

...

IPConfig

...

i18n

...

security

...

rendering

...

freemarker

...

templateLoaders

...

jcr

...

webapp

...

Tip

JSP脚本只能存在于文件系统中,因为它们在被绘制前需要服务器预编译。JSP脚本在模块安装或更新时被解压到文件系统。

编辑脚本

Panel
bgColor#ffffff
titleBGColor#ffffff
title最佳实践
Multiexcerpt
MultiExcerptNamestoring scripts

最佳实践是将脚本存储在您的项目模块里,这样脚本可以被存储在一个版本控制系统,并且自动成为一个可控的软件开发周期的一部分。您可以在文件系统里用您喜爱的支持语法高亮及其他特性的编辑器来编辑。

开发时,在内容库里编辑脚本有时候很有用。这种方法只对FreeMarker脚本有效。如果您将脚本导入templates工作区,它们就会在STK > Templates里可见可用。所有的STK脚本都遵循这个模式。您可以从以上的加载顺序看出,在templates工作区里的脚本会覆写从类路径里加载的脚本。一旦被添加或修改,则需要选中Enable template复选框来强迫Magnolia CMS从内容库中加载模板。

Image Removed

 

Warning

在内容库里编辑模板脚本对评估,制造原型,或较小的项目比较理想。然后,长期来说这不利于维护。不要在生产环境中这样做。我们强烈建议您在一个版本控制系统中存储脚本,并将它们打包进一个项目模块。

 

参考模板样例 > 编辑模板脚本里更多信息。

STK脚本

STK模板脚本在网络应用程序的类路径中。其副本在STK > Templates里就可以编辑。STK脚本是用FreeMarker写的。

脚本存储在/templating-kit文件夹的子文件夹里:

  • components包含按照组件类型分类的组件模板。
  • pages包含main页面脚本,区域脚本在子文件夹里。

此结构遵循模板原型结构。在模板原型里引用的区域模板都被存在/global文件夹里。

Image Removed

main脚本

main是主页面脚本(Git)。它是理解这个系统的一个好起点。它在编写实例上使用@cms.init指令初始化页面编辑器,也包含了设置bodyClassbodyID的逻辑。

main脚本使用@cms.area指令来调用区域绘制它们自身。它使用#include指令绘制导航main脚本用三个DIV元件包装区域,建立了一个可调的页面网格

区域脚本

参考STK区域里关于STK区域脚本的描述。

指令

关键的模板特性以指令(directives形式在Magnolia CMS标签库里可用。指令输入很快,但会绘制复杂的输出。

标准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代码片段。参考Templating Kit > Templates /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提供一下定制指令:

  • cms:init:嵌入编辑页面所必需的JavaScript和CSS。
  • cms:area绘制区域
  • cms:component绘制组件

对FreeMarker来说,这些指令都是由Directives类来完成的。此类可在Configuration应用的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"/]

该指令通过区域名字引用区域。区域名为包含区域定义的内容节点,如mainfooterstage

页面上的结果是一个区域栏,一个开始标记和一个结束标记。区域定义里title属性值在此区域栏里绘制。当编辑者点击New Component框的添加图标时,组件就添加到区域里了。

Image Removed

如果区域定义包含一个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指令里来依次通过地图里的组件。

要绘制的内容,或对于可编辑组件可能的编辑,都是通过content属性(attribute)来传递。在编写实例上,指令绘制一个组件工具栏。组件定义里的title属性值在工具栏中绘制。

Image Removed

属性(Attribute)描述缺省值
editable定义编辑图标是否应被显示,主要对继承的内容有用。cmsfn.isFromCurrentPage()
template要用到的组件定义名。节点里定义的模板。

示例:

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

常用指令属性

下列属性(attributes)可用任何指令传递,它们定义指令创建的元件应该工作在什么内容上。

属性(Attribute)描述缺省值
content一个节点或是内容地图。 
workspace指定路径时用到的工作区。与当前内容相同
path工作区中的路径。 

content属性

content属性(attribute)告诉脚本它应该在哪个内容节点上操作。脚本通常在“当前”内容节点上执行。对于一个页面级脚本,当前节点为该页面,而对于一个区域级脚本,当前节点就是该区域,同样,对于一个组件级脚本,当前节点就是该组件。然后,也有一些您想要脚本在一个不同的内容节点上执行的情况,这就是content属性变得方便实用的时候。

例如,intro区域没有它自己的内容,它也不包含任何组件,因为它是noComponent类型的。这个区域在页面内容上操作,编辑和绘制页面标题及摘要。我们通过使用内容属性来实现这些。

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。

指令绘制示例

这是一个关于指令在页面上如何绘制的例子。

  1. main脚本包含一个cms.init指令,嵌入了编写实例上所需的CSS和JavaScript。
  2. cms.area指令调用即将绘制的区域。该指令通过名字来辨别区域,在此例中为extras。如果该区域有子区域,您需要一个分开的脚本来调用要绘制的子区域。然后,如果该区域只包含组件,那么您不需要区域脚本。
  3. 页面的脚注同样使用cms.area指令来绘制。该区域不含子区域,只有组件。

Image Removed

添加您自己的指令

您可以添加自己的指令。以下步骤使您类中的Java方法和功能对模板脚本可用:

  1. 像通常一样写作和编译您的Java类。
  2. 拷贝这个类文件到您的Magnolia网络应用的WEB-INF/classes文件夹。
  3. 转到Configuration应用/modules/rendering/renderers/freemarker/contextAttributes
  4. /contextAttributes下,创建一个内容节点,如myClass。用您类的用途命名它。
  5. myClass里,创建两个数据节点:
    • componentClass,设置其值为您放在WEB-INF/classes里的类的完全相称类名。
    • name,设置其值为myClass

这就让您能够使用${myClass.myMethod()} FreeMarker句法,从模板获得myClass里所有的静态方法。

模板功能

TemplatingFunctions包含了您可以在模板中使用的有用方法。这些方法表现为cmsfndecode方法从属性(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)]

每个绘制器的模板功能类在Configuration应用的/<module>/rendering/renderers/<renderer>/contextAttributes/<tag library>下配置。注意<tag library>内容节点和name数据节点的值与脚本里呈现给方法的句法相匹配。

...

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内建函数

FreeMarker提供一套强大的内建函数。这些被用作基本数据操作,不需要任何Java代码。内建函数在使用时前面加一个“?”字符。例如, ?exists用于检查一个值或对象是否存在,?has_content检查一个值或对象是否存在且为空。

字符串

大多数Java字符串在FreeMarker里都能实现并直接使用。例如:substringuncap_firstcapitalizedate,time,datetimeends_withhtmlindex_oflast_index_oflengthlower_caseupper_casecontainsreplacestarts_withtrim

布尔值

字符串(使用了布尔值的)将一个布尔值转化为一个字符串。您可以采用以下两种方法使用它:

  • foo?string使用缺省字符来代表truefalse值来将布尔值转化为字符串。
  • 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样例:

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}

参考

对FreeMarker初学者有用的链接:

...