Decorator 简介

Decorator 是一些静态或者半静态的标记,通常是 Velocity 或者 JSP 的模版。本文将重点介绍使用 Velocity 建立 Decorator 模版。大部分使用的是标准描述,同样可以使用其它的脚本语言来建立Decorator。

在页面上有两种不同的 Decorator : layout-decorator(或者称作page-decorator) 和 portlet-decorator 。

Portlet decorator 在 Jetspeed 中是窗口主题。它区分开每个 Portlet 实例。 Portlet decorator 可以获取当前 portlet 实例的标题以及所拥有的状态。

每一个 .psml 的 Portal 页面(可参考PSML 页面设计指南), Layout 或者 page decorator 会返回一个 “页头” 和 一个“页尾”。

Decoration 文件结构

所有的 decorations 都存放在 Web 应用程序根目录下的 decrorations 目录里。 在此文件夹下有两个主要的目录,一个是 layout ,存放layout decorator的, 一个是 portlet , 存放 portlet decorator。每个 decorator 拥有自己的目录,存放在这两个目录中。 Jetspeed 通过每个 decorator 的目录名来调用这个 decorator。 具体的介绍,不久将会出现在这篇文档中。

Layout(Page) Decorator 的结构

主要的4个文件

基本的Layout Decorator需要下面4个文件:

  • decorator.properties
  • styles.css
  • header.vm
  • footer.vm
其中的3个文件 decorator.properties, header.vm, 和 footer.vm 在 decorator 的根目录下。 而 styles.css 需要放到 css/ 目录下。

Layout Decorator 的基本配置文件: decorator.properties

decorator.properties 文件包含了 layout decorator 的基本信息。实际上, 这个文件可以是不包括任何信息的,但是由于系统寻找可用的 decorator 的时候要用到此文件, 所以不能没有此文件。在此文件里可以不用指定以下信息。

属性 描述 默认值
base.css.class 这个值将替换掉你的 header 模版里的 css 主伪类。在设计 header 模版的时候,你就知道怎么使用它了。 decorator 的名字
stylesheet 指定调用的 css 文件 css/styles.css
header 指定页头模版文件 header.vm
footer 指定页尾模版文件 footer.vm

页头模版: header.vm

header.vm 将显示为你 portal 页面的前半部分。下面将一步一步的教你写一个基本页头模版。



注意: 这是基于会使用 HTML 和 CSS 的读者写的。 了解一些 Velocity 模版的知识会对开发有帮助的,但不是必须的。

        
<html>
   <head>
     #defineLayoutObjects()
头两行是很显而易见的。如果你还不明白,那么这份文档对你没有多大帮助。;-)

我们的第一个宏 (Macro) : #defineLayoutObjects()

现在这一行的内容 #defineLayoutObjects() 比起前面两行就不那么显而易见了。 #defineLayoutObjects() 在 Velocity 常用语里叫“宏”(Macro)。 一个“宏”将在 Velocity 文件或者 Velocity 模版里定义。 所有我们将用到的(包括刚才说的这个)全局宏都定义在 WEB-INF/jetspeed_macros.vm 里。 后面我们将介绍在 Decorator 开发时怎样写一个自己的宏。现在回到正题。 #defineLayoutObjects() 添加了一些可以在 header.vm 、 footer.vm 、其他的宏、以及所有的 portlet decorator 模版里使用的值。 我们adds values to Velocity that will be accessible within header.vm, footer.vm, other macros and all of your portlet decoration templates. We could easily stop here regarding #defineLayoutObjects(), however, I feel it can be helpful to have some insights into the inner workings of Velocity for the uninitiated. With out further ado, the code:

        
  #macro (defineLayoutObjects)
    #set($preferedLocale = $JS2RequestContext.locale)
    #set($rootFragment = $jetspeed.currentFragment)
    #set($site = $request.getAttribute("org.apache.jetspeed.portalsite.PortalSiteRequestContext"))
    #set($theme = $request.getAttribute("org.apache.jetspeed.theme"))
    #set($layoutDecoration = $theme.getDecoration($rootFragment))
  #end        
Hmm. What is actually happening here. Okay first off we have, #set(), this is what is known as a directive in Velocity. A directive is built-in functionallity and not a macro. #set() is pretty straight forward in that it takes the value on the right of the = and assigns it to the left. Cool, that seems fairly straight forward. But how does one work with these values and where the heck did $JS2RequestContext.locale come from? I guess i should take a quick step back and describe how we work with objects in Velocity. All objects available to a Velocity template can be referenced via the $someObject notation. Knowing that much invoking a method , let's getFoo(), can be done just like this $someObject.getFoo(). Even cooler is the fact we can short-hand getter methods that don't take any arguments like this, $someObject.foo. As for this $JS2RequestContext this is actually an instance of the org.apache.jetspeed.RequestContext that has been availble to Velocity by Jetspeed itself. So, by looking the javadoc for org.apache.jetspeed.RequestContext we see $JS2RequestContext.locale will give us an instance of java.util.Locale that reperesnts the locale of the current user. Couldn't be much simpler than that could it?



Next up we have this line #set($rootFragment = $jetspeed.currentFragment) another set() statement, this time creating an object called $rootFragment which is an instance of org.apache.jetspeed.om.page.ContentFragment. It is really not relevant to this guide to describe what $jetspeed.currentFragment is doing so I am going to skip that and move on.



#set($site = $request.getAttribute("org.apache.jetspeed.portalsite.PortalSiteRequestContext"))

#set($theme = $request.getAttribute("org.apache.jetspeed.theme"))


Ah $request, now that looks familiar, this is actually an instance of javax.servlet.http.HttpServletRequest from which we are retreiving objects that were been placed into Velocity by Jetspeed. The actual objects are: org.apache.jetspeed.portalsite.PortalSiteRequestContext and org.apache.jetspeed.decoration.Theme respectively. We will put all of these objects to good use in just a little while.

Feed Your HEAD: How to Properly Code Your Head Tag.

This section provides you with all the information to prperly code the <HEAD> of your Layout decroation. So, straight to the code.

  
<html>
    <head>
     #defineLayoutObjects()
     
     <base href="#BaseHref()">
     <meta http-equiv="Content-type" content="#ContentType()" />
     <meta http-equiv="Content-style-type" content="text/css" />   
     #includeJavaScriptForHead()
     #IncludeStylesheets()    
     <title>#PageTitle()</title>
     <meta name="description" content="#PageDescription()" />

The <base> Tag

First off we have <base href="#BaseHref()"> which allows us to define the base path for resolution of web resources, for an in depth discussion of the <base> see: W3C Schools Reference. If you have spent any time playing with Jetspeed, you will have noticed it does all sorts of crazy URL rewriting that will totally hose any attempts to consistently path you html and styles sheets. By defining the BASE tag, this probelms will all but disappear. As for the #BaseHref() macro, it simply generates a fully qualified path to your web application's root. The actual code, interms of the servlet api is synonimous with this:

HttpServletRequest request;
StingBuffer baseHref = new StringBuffer(request.getScheme())
     .append("://").append(request.getServerName())
	 .append(":").append(request.getServerPort())
	 .append(request.getContextPath()).append("/");
return baseHref.toString();		 
The actual Velocity macro code is a bit more terse ;)
${request.scheme}://${request.serverName}:${request.serverPort}${request.contextPath}/

Meta Tag: <meta http-equiv="Content-type" content="#ContentType()" />

Will return text/html plus the proper encoding, such as UTF.

#includeJavaScriptForHead()

At the time of the writing of this guide there is really very little javascript required to for the base Jetspeed 2 server to run. However this may change in near future as we try to employ the use of AJAX in things such as configuration and administration.