Introduction to Decorators

Decorators are defined as any static or semi-static markup surrounding a dynamically generated Fragment. Decorators are usually written with either Velocity or JSP templates. This guide will focus primarily on using the Velocity scripting language to build decoration. However, most of the standards and approaches described here can be applied to writing decroations in other scripting languages.

There are two different types of decorations that are used when building a page; Portlet and Layout (or page).

Portlet decorations are the "window dressings" of Jetspeed. They wrap each indivual portlet fragment's rendered content with HTML (XHTML, VXML, etc). Portlet decoration's are responsible for displaying the appropriate title and any buttons associated with changing window states or portlet modes.

Layout or Page decorations responsible for providind a "header" area and "footer" area for a single portal page which is represented by a .psml document (see: Documentation for Designers working with PSML for more information on psml). They also provide general style information for the page and portlets. However, portlet level style settings can be overidden at the portlet decoration level.

Decoration File Structure

All decorations are stored in a directory directly off of the web applications root called decorations. The two primary directories under the here are layout for layout decorations and portlet for portlet decorations. Individual decoartions are housed in their own directories underneath these two directories. The name of the directory you create for under either layout or portlet is how Jetspeed will locate your decoration. We will go into further detail on how this works later on in this guide.

Anatomy of a Layout(Page) Decoration

Four Files in a Nutshell

In it's most basic form a Layout Decroation only requires you to define four files:

  • decorator.properties
  • styles.css
  • header.vm
  • footer.vm
Three of these files; decorator.properties, header.vm, and footer.vm go directly into the directory you create under /decorations/layout. The styles.css needs to be put into a subdirectory of your decoration names css/.

Basic Layout Decoration Configuration: decorator.properties

The decorator.properties file holds basic information about your layout decoration. In all actuallity, this file can be blank, but we still require that it be present as it is used by other APIs to "discover" available decorations. With that being said, it is safe to assume that all the properties defined below are optional.

Property Name Description Default
base.css.class This value is generally placed in the top most element tag of your header template. You will see how it is used when we go into development of a header template. Defaults to the name of your decoration
stylesheet Relative path to your decoration's stylesheet css/styles.css
header Relative path to your decoration's header template header.vm
footer Relative path to your decoration's footer template footer.vm

Top o' the Page to ya: header.vm

The header.vm represnts the top portion of your portal page. Below is a section by section walkthrough of the basics required to write a functional header template.

NOTICE: It is assumed that the reader is proficient in both the use of HTML and CSS. A rudimentary knowledge of Velocity helps but is not required to develop a decoration.

        
<html>
   <head>
     #defineLayoutObjects()
The first two lines should be obvious, if they are not, this guide from here on out will not be much help to you ;-)

Our First Macro: #defineLayoutObjects()

Now the line containing #defineLayoutObjects() will not be as obvious in its purpose as the previous two. #defineLayoutObjects() is what is known, in Velocity vernacular, as a macro. A macro is a predefined snippet of Velocity code that can be reused within any Velocity template. All of the global macros we will be using (including this one) are defined within the WEB-INF/jetspeed_macros.vm. Later in this guide we will discuss supplying your own, custom macros for assisting you in your decoration development, if you choose to. Now, back to the #defineLayoutObjects(). #defineLayoutObjects() 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 represents 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 properly 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.

JSP Decorators

The JSP decorators are only currently supported in the tigris decorator. The JSP decorators are not enabled by default. JSP decorators can be enabled by modifying (properly commented) settings in:

  • decorators/layout/tigris/decorator.properties
  • decorators/portlet/tigris/decorator.properties
  • WEB-INF/templates/layout/html/columns/layout.properties
  • WEB-INF/templates/layout/html/maximized/layout.properties
  • WEB-INF/templates/layout/html/solo/layout.properties

Note: you need to change the settings in *all* of the above properties files, and then restart jetspeed.

Also note: for the tcolumns template there isn't a JSP replacement (yet), but that isn't much used anyway AFAIK. Just to be sure: don't switch your page layout portlet(s) from columns to tcolumns, otherwise your page will be be broken.