JBoss.orgCommunity Documentation

Chapter 25. Layouts and Themes

25.1. Overview
25.2. Header
25.2.1. Overview
25.3. Layouts
25.3.1. How to define a Layout
25.3.2. How to use a Layout
25.3.3. Where to place the Descriptor files
25.3.4. Layout JSP™ tags
25.4. RenderSets
25.4.1. What is a RenderSet
25.4.2. How is a RenderSet defined
25.4.3. How to specify what RenderSet to use
25.5. Themes
25.5.1. What is a Theme
25.5.2. How to define a Theme
25.5.3. How to use a Theme
25.5.4. How to write your own Theme
25.6. Other Theme Functionalities and Features
25.6.1. Content Rewriting and Header Content Injection
25.6.2. Declarative CSS Style injection
25.6.3. Disabling Portlet Decoration
25.7. Theme Style Guide (based on the Industrial theme)
25.7.1. Overview
25.7.2. Main Screen Shot
25.7.3. List of CSS Selectors
25.8. Additional Ajax selectors

Portals usually render the markup fragments of several portlets, and aggregate these fragments into one page that ultimately gets sent back as response. Each portlet on that page will be decorated by the portal to limit the real estate the portlet has on the page, but also to allow the portal to inject extra functionality on a per portlet basis. Classic examples of this injection are the maximize, minimize and mode change links that will appear in the portlet window , together with the title.

Layouts and themes allow to manipulate the look and feel of the portal. Layouts are responsible to render markup that will wrap the markup fragments produced by the individual portlets. Themes, on the other hand, are responsible to style and enhance this markup.

In JBoss Portal, layouts are implemented as a JSP or a Servlet. Themes are implemented using CSS Style sheets, JavaScript™ and images. The binding element between layouts and themes are the class and id attributes of the rendered markup.

JBoss Portal has the concept of regions on a page. When a page is defined, and portlet windows are assigned to the page, the region, and order inside the region, has to be specified as well. For portal layouts this has significant meaning. It defines the top most markup container that can wrap portlet content (other then the static markup in the JSP itself). In other words: from a layout perspective all portlets of a page are assigned to one or more regions. Each region can contain one or more portlets. To render the page content to return from a portal request, the portal has to render the layout JSP, and for each region, all the portlets in the region.

Since the markup around each region, and around each portlet inside that region, is effectively the same for all the pages of a portal, it makes sense to encapsulate it in its own entity.

In JBoss Portal you can currently see two out of these approaches, namely the first and the last. Examples for the first can be found in the portal-core.war, implemented by the nodesk and phalanx layouts. Examples for the third approach can be found in the same war, implemented by the industrial and Nphalanx layout. What encapsulates the markup generation for each region, window, and portlet decoration in this last approach is what's called the RenderSet.

While we want to leave it open to you to decide which way to implement your layouts and themes, we strongly believe that the last approach is superior, and allows for far more flexibility, and clearer separation of duties between portal developers and web designers.

The last topic to introduce in this overview is the one of portal themes. A theme is a collection of web design artifacts. It defines a set of CSS, JavaScript and image files that together decide about the look and feel of the portal page. The theme can take a wide spectrum of control over the look and feel. It can limit itself to decide fonts and colors, or it can take over a lot more and decide the placement (location) of portlets and much more.

The default header is divided into two parts, links to pages displayed as tabs and links to navigate between portals and dahsboards as well as loggin in and out. Those two parts are included into the template thanks to the layout as defined in Section 25.3, “Layouts”. In fact, the region named, dashboardnav will include the navigation links, while the region named navigation will include the navigation tabs. It is then easy to hide one and/or the other by removing the corresponding inclusion in the layout.

Screenshot of the header with the 'renaissance' theme

Note

Here, we use split content from rendering by using a CSS style sheet, it allow us to change the display by switching the CSS without affecting the content. THe Maple theme will display the links on the left side with a different font for example. THis is up to you to choose or not this approach

To customize the header there are several options detailed after.

  • The first option would simply require to modify the theme CSS, by doing this you could change the fonts, the way tabs are rendered, colors and many other things but not change the content.
  • The second option is to modify the provided JSP files, header.jsp and tabs.jsp. It gives you more flexibility than the previous solution on modifying the content. Links to legacy application could easily be added, URLs could be arranged differently, the CSS approach could be replaced by good old HTML, CSS style names could be changed... The drawback of this method compare to the next one is the limitation in what is accessible from the JSP.

The content of those two parts are displayed thanks to two different JSP™ pages. By default you would find those pages in the directory portal-core.war/WEB-INF/jsp/header/. The file header.jsp is used to display the links that are displayed on the upper right of the default theme. The file tabs.jsp is used to display the pages tabs appearing on the left.

Again, you have several choices, either to edit the included JSP files directly or create your own, store them in a web application then edit the following file: jboss-portal.sar/META-INF/jboss-service.xml. The interesting part in that file is the following:

<mbean
   code="org.jboss.portal.core.aspects.controller.PageCustomizerInterceptor"
   name="portal:service=Interceptor,type=Command,name=PageCustomizer"
   xmbean-dd=""
   xmbean-code="org.jboss.portal.jems.as.system.JBossServiceModelMBean">
   <xmbean/>
   <attribute name="TargetContextPath">/portal-core</attribute>
   <attribute name="HeaderPath">/WEB-INF/jsp/header/header.jsp</attribute>
   <attribute name="TabsPath">/WEB-INF/jsp/header/tabs.jsp</attribute>
   <depends
      optional-attribute-name="PortalAuthorizationManagerFactory"
      proxy-type="attribute">portal:service=PortalAuthorizationManagerFactory</depends>
</mbean>

The three attributes are:

Writing the header JSP

A couple of request attributes are set so that they can be used by the JSP, here is the list of attributes and their meaning:

Every attribute that is an URL attribute is an object implementing the org.jboss.portal.api.PortalURL interface. Therefore it is possible to generate the URL using the toString() method and change various things related to the URL. With that in hand, if someone just wanted to display the logged-in username and a link to log out, he could write:

<%@ page import="org.jboss.portal.identity.User" %>

<%
   User user = (User) request.getAttribute("org.jboss.portal.header.USER");
   PortalURL signOutURL = (PortalURL)request.getAttribute("org.jboss.portal.header.SIGN_OUT_URL");
   PortalURL loginURL = (PortalURL)request.getAttribute("org.jboss.portal.header.LOGIN_URL");
   

   if (user == null)
   {
%>
   <a href="<%= loginURL %>">Login</a>
<%
   }
   else
   {
%>
Logged in as: <%= user.getUserName() %>
<br/>
<a href="<%= signOutURL %>">Logout</a>
<%
   }
%>

Writing the tabs JSP

A couple of request attributes are set so that they can be used by the JSP, here is the list of attributes and their meaning:

The default file in charge of displaying the tabs can be found in: portal-core.war/WEB-INF/jsp/header/

The portal comes with a set of JSP™ tags that allow the layout developer faster development.

The theme-basic-lib.tld contains a list of tags that allow a JSP writer to access the state of the rendered page content. It is built on the assumption that regions, portlet windows and portlet decoration is managed inside the JSP.

The portal-layout.tld contains tags that work under the assumption that the RenderSet will take care of how regions, portlet windows and the portlet decoration will be rendered. The advantage of this approach is that the resulting JSP is much simpler and easier to read and maintain.

Here is an example layout JSP that uses tags from the latter:

               <%@ taglib uri="/WEB-INF/theme/portal-layout.tld" prefix="p" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
      <title><p:title default="My Great Portal"/></title>
      <meta http-equiv="Content-Type" content="text/html;" />
      <p:theme themeName='renaissance' />
      <p:headerContent />
   </head>
   <body id="body">
      <div id="portal-container">
         <div id="sizer">
            <div id="expander">
               <div id="logoName"></div>
               <table border="0" cellpadding="0" cellspacing="0" id="header-container">
                  <tr>
                     <td align="center" valign="top" id="header">
                        <div id="spacer"></div>
                     </td>
                  </tr>
               </table>
               <div id="content-container">
                  <p:region regionName='This-Is-The-Page-Region-To-Query-The-Page'
                     regionID='This-Is-The-Tag-ID-Attribute-To-Match-The-CSS-Selector'/>
                  <p:region regionName='left' regionID='regionA'/>
                  <p:region regionName='center' regionID='regionB'/>
                  <hr class="cleaner" />
                  <div id="footer-container" class="portal-copyright">Powered by
                     <a class="portal-copyright"
                        href="http://www.jboss.com/products/jbossportal">
                        JBoss Portal
                     </a>
                  </div>
               </div>
            </div>
         </div>
      </div>
   </body>
</html>

A RenderSet can be used to produce the markup containers around portlets and portlet regions. The markup for each region, and each portlet window in a region is identical. Further more, it is most likely identical across several layouts. The way portlets are arranged and decorated will most likely not change across layouts. What will change is the look and feel of the decoration, the images, fonts, and colors used to render each portlet window on the page. This is clearly a task for the web designer, and hence should be realized via the portal theme. The layout only needs to provide enough information to the theme so that it can do its job. The RenderSet is exactly that link between the layout and the theme that takes the information available in the portal and renders markup containing the current state of the page and each portlet on it. It makes sure that the markup around each region and portlet contains the selectors that the theme CSS needs to style the page content appropriately.

A RenderSet consists of the implementations of four interfaces. Each of those interfaces corresponds to a markup container on the page.

All the renderer interfaces are specified in the org.jboss.portal.theme.render package.

The four markup containers are hierarchical. The region contains one or more windows. A window contains the portlet decoration and the portlet content.

The region is responsible for arranging the positioning and order of each portlet window. Should they be arranged in a row or a column? If there are more then one portlet window in a region, in what order should they appear?

The window is responsible for placing the window decoration, including the portlet title, over the portlet content, or under, or next to it.

The decoration is responsible for inserting the correct markup with the links to the portlet modes and window states currently available for each portlet.

The portlet content is responsible for inserting the actually rendered markup fragment that was produced by the portlet itself.

Analogous to how a strategy is specified, the RenderSet can be specified as a portal or page property, or a particular layout can specify an anonymous RenderSet to use. Here is an example of a portal descriptor:

               <?xml version="1.0" encoding="UTF-8"?>
<portal>
  <portal-name>default</portal-name>
    <properties>
      <!-- use the divRenderer for this portal -->
      <property>
        <name>theme.renderSetId</name>
        <value>divRenderer</value>
      </property>
    </properties>
    <pages>
      <default-page>default</default-page>
      <page>
        <page-name>default</page-name>
        <properties>
          <!-- overwrite the portal's renderset for this page -->
          <property>
            <name>theme.renderSetId</name>
            <value>emptyRenderer</value>
          </property>
        </properties>
      <window>
        <window-name>TestPortletWindow</window-name>
        <instance-ref>TestPortletInstance</instance-ref>
        <region>center</region>
        <height>0</height>
      </window>
    </page>
  </pages>
</portal>

Here is an example of a layout descriptor with an anonymous RenderSet:

               <?xml version="1.0" encoding="UTF-8"?>
<layouts>
<renderSet>
<set content-type="text/html">
<region-renderer>org.foo.theme.render.MyRegionRenderer</region-renderer>
<window-renderer>org.foo.theme.render.MyWindowRenderer</window-renderer>
<portlet-renderer>org.foo.theme.render.MyPortletRenderer</portlet-renderer>
<decoration-renderer>org.foo.theme.render.MyDecorationRenderer</decoration-renderer>
</set>
</renderSet>
<layout>
<name>generic</name>
<uri>/generic/index.jsp</uri>
<uri state="maximized">/generic/maximized.jsp</uri>
</layout>
</layouts>

Again, analogous to layout strategies, the anonymous RenderSet overwrites the one specified for the page, and that overwrites the one specified for the portal. In other words: all pages that use the layout that defines an anonymous RenderSet will use that RenderSet, and ignore what is defined as RenderSet for the portal or the page.

In addition to specifying the renderSet for a portal or a page, each individual portlet window can define what renderSet to use for the one of the three aspects of a window, the window renderer, the decoration renderer, and the portlet renderer. This feature allow you to use the the window renderer implementation from one renderSet, and the decoration renderer from another. Here is an example for a window that uses the implementations of the emptyRenderer renderSet for all three aspects:

               <window>
   <window-name>NavigationPortletWindow</window-name>
   <instance-ref>NavigationPortletInstance</instance-ref>
   <region>navigation</region>
   <height>0</height>
   <!-- overwrite portal and page properties set for the renderSet for this window -->
   <properties>
      <!-- use the window renderer from the emptyRenderer renderSet -->
      <property>
         <name>theme.windowRendererId</name>
         <value>emptyRenderer</value>
      </property>
      <!-- use the decoration renderer from the emptyRenderer renderSet -->
      <property>
         <name>theme.decorationRendererId</name>
         <value>emptyRenderer</value>
      </property>
      <!-- use the portlet renderer from the emptyRenderer renderSet -->
      <property>
         <name>theme.portletRendererId</name>
         <value>emptyRenderer</value>
      </property>
   </properties>
</window>

Themes can be added as part of any web application that is deployed to the portal server. All what is needed is a theme descriptor file that is part of the deployed archive. This descriptor indicates to the portal what themes and theme resources are becoming available to the portal. The theme deployer scans the descriptor and adds the theme(s) to the ThemeService, which in turn makes the themes available for consumption by the portal. Here is an example of a theme descriptor:

               <themes>
<theme>
<name>nodesk</name>
<link href="/nodesk/css/portal_style.css" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="/images/favicon.ico" />
</theme>
<theme>
<name>phalanx</name>
<link href="/phalanx/css/portal_style.css" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="/images/favicon.ico" />
</theme>

<theme>
<name>industrial-CSSSelect</name>
<link rel="stylesheet" id="main_css" href="/industrial/portal_style.css" type="text/css" />
<link rel="shortcut icon" href="/industrial/images/favicon.ico" />

<script language="JavaScript" type="text/javascript">
// MAF - script to switch current tab and css in layout...
function switchCss(currentTab,colNum) {
var obj = currentTab;
var objParent = obj.parentNode;

if (document.getElementById("current") != null) {
var o = document.getElementById("current");
o.setAttribute("id","");
o.className = 'hoverOff';
objParent.setAttribute("id","current");
}

var css = document.getElementById("main_css");
source = css.href;
if (colNum == "3Col") {
if (source.indexOf("portal_style.css" != -1)) {
source = source.replace("portal_style.css","portal_style_3Col.css");
}
if (source.indexOf("portal_style_1Col.css" != -1)) {
source = source.replace("portal_style_1Col.css","portal_style_3Col.css");
}
}
if (colNum == "2Col") {
if (source.indexOf("portal_style_3Col.css" != -1)) {
source = source.replace("portal_style_3Col.css","portal_style.css");
}
if (source.indexOf("portal_style_1Col.css" != -1)) {
source = source.replace("portal_style_1Col.css","portal_style.css");
}
}
if (colNum == "1Col") {
if (source.indexOf("portal_style_3Col.css" != -1)) {
source = source.replace("portal_style_3Col.css","portal_style_1Col.css");
}
if (source.indexOf("portal_style.css" != -1)) {
source = source.replace("portal_style.css","portal_style_1Col.css");
}
}

css.href = source;
}
</script>
</theme>
</themes>

Themes are defined in the portal-themes.xml theme descriptor, which is located in the WEB-INF/ folder of the web application.

Again, analogous to the way it is done for layouts, themes are specified in the portal descriptor as a portal or page property. The page property overwrites the portal property. In addition to these two options, themes can also be specified as part of the theme JSP tag , that is placed on the layout JSP. Here is an example portal descriptor that specifies the phalanx theme as the theme for the entire portal, and the industrial theme for the theme test page:

               <portal>
  <portal-name>default</portal-name>
  <properties>
    <!-- Set the theme for the default portal -->
    <property>
      <name>layout.id</name>
      <value>phalanx</value>
    </property>
  </properties>
  <pages>
    <page>
      <page-name>theme test</page-name>
      <properties>
        <!-- set a difference layout for this page -->
        <property>
          <name>layout.id</name>
          <value>industrial</value>
        </property>
      </properties>
      <window>
        <window-name>CatalogPortletWindow</window-name>
        <instance-ref>CatalogPortletInstance</instance-ref>
        <region>left</region>
        <height>0</height>
      </window>
    </page>
  </pages>
</portal>

And here is an example of a layout JSP that defines a default theme to use if no other theme was defined for the portal or page:

               <%@ taglib uri="/WEB-INF/theme/portal-layout.tld" prefix="p" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title><%= "JBoss Portal :: 2.2 early (Industrial)" %></title>
    <meta http-equiv="Content-Type" content="text/html;" />
    <p:theme themeName='industrial' />
    <p:headerContent />
  </head>
  <body id="body">
    <div id="portal-container">
      <div id="sizer">
        <div id="expander">
          <div id="logoName"></div>
          <table border="0" cellpadding="0" cellspacing="0"
            id="header-container">
            <tr>
              <td align="center" valign="top" id="header">
                <div id="spacer"></div>
              </td>
            </tr>
          </table>
          <div id="content-container">
            <p:region
              regionName='This-Is-The-Page-Region-To-Query-The-Page'
              regionID='This-Is-The-Tag-ID-Attribute-To-Match-The-CSS-Selector' />
            <p:region regionName='left' regionID='regionA' />
            <p:region regionName='center' regionID='regionB' />
            <hr class="cleaner" />
            <div id="footer-container" class="portal-copyright">
              Powered by
              <a class="portal-copyright"
                href="http://www.jboss.com/products/jbossportal">
                JBoss Portal
              </a>
              <br />
              Theme by
              <a class="portal-copyright"
                href="http://www.novell.com">
                Novell
              </a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

For the function of the individual tags in this example, please refer to the layout section of this document.

This section contains all the functionalities that don't fit with any of the other topics. Bits and pieces of useful functions that are related to the theme and layout functionality.

Portlets can have their content rewritten by the portal. This is useful if you want to uniquely namespace markup (JavaScript functions for example) in the scope of a page. The rewrite functionality can be applied to the portlet content (the markup fragment) and to content a portlet wants to inject into the header. The rewrite is implemented as specified in the WSRP (OASIS: Web Services for Remote Portlets; producer write). As a result of this, the token to use for rewrite is the WSRP specified "wsrp_rewrite_". If the portlet sets the following response property

res.setProperty("WSRP_REWRITE","true");

all occurrences of the wsrp_rewrite_ token in the portlet fragment will be replaced with a unique token (the window id). If the portlet also specifies content to be injected into the header of the page, that content is also subject to this rewrite.

res.setProperty("HEADER_CONTENT", "
               <script>function wsrp_rewrite_OnFocus(){alert('hello button');}</script>
               ");
            

Note that in order for the header content injection to work, the layout needs to make use of the headerContent JSP tag, like:

               <%@ taglib uri="/WEB-INF/theme/portal-layout.tld" prefix="p" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title><JBoss Portal 2.2 early</title>
    <meta http-equiv="Content-Type" content="text/html;" />

    <p:headerContent />
  </head>
  <body id="body">
    <p>...</p>
  </body>
</html>

This document outlines the different selectors used to handle the layout and look/feel of the Industrial theme included in the JBoss portal.

A couple of things to know about the theming approach discussed below:

The following is a list of the selectors used in the theme stylesheet, including a brief explanation of how each selector is used in the portal:

Since 2.6 JBoss Portal has ajax features. Those features introduce a couple of CSS selectors that enables further customization of the visual look and feel. Indeed by default those CSS styles are provided by ajaxified layouts but it may not fit with some themes. It is possible to redefine them in the stylesheet of the themes.