dominoGuru.com

Your Development & Design Resource

Installing Custom Themes on a Domino Server for XPages

Installing Custom Themes on a Domino Server for XPages For those of you that have designed Themes for applications, you know that a Theme is more than just simple CSS that changes the look of the rendered XPage application. A Theme can be used to change the XPage itself, dynamically include resources, and can write values to anything it can get a handle on.

Creating a corporate branded Theme for your organization can be a herculean chore, but once it's complete you have an amazingly reusable development tool that can quickly speed up your XPage application development efforts.

As is often the case with Notes Developers who store all Design Elements in the NotesDatabase, most of the Themes that I've seen are created in-NSF. While this is perfectly fine for a single-use application, the eventual goal for an XPages application developer should be to create a standard Theme for their XPage applications that establishes not only a visual look and feel, but also consistent functionality.

Let's first take a look at the end-result Theme architecture and then review how it is created. Here's the Theme architecture for Acme Anvil Corporation:

IBM Lotus Domino XPages Custom Theme Architecture IBM Lotus Domino XPages Custom Theme Architecture

The "acme.theme" (set to extend the "webstandard.theme" is installed on the IBM Lotus Domino server, and uses "acme"-specific Domino server-based CSS and images as well as sets various XPage control properties.

For example, if a pageTitle is not defined for an XPage, the "acme.theme" uses the application's @DbTitle().

As each application is different however, the NotesDatabase uses an in-NotesDatabase Theme. This "acme.extended.theme" extends "acme.theme", and does more application-specific functions.

Domino Server Theme Installation

First, let's take a look at the "acme.theme":

<theme extends="webstandard">

    <resources>
        <bundle target="xsp" src="preferences.properties" var="preferences" loaded="true" rendered="true" />
        <styleSheet href="/.ibmxspres/domino/acme/acme.css" />
    </resources>

    <control override="false">
        <name>ViewRoot</name>
        <property>
            <name>pageTitle</name>
            <value>#{javascript:@DbTitle()}</value>
        </property>
    </control>

    <control>
        <name>container_main</name>
        <property>
            <name>styleClass</name>
            <value>container_main</value>
        </property>
    </control>
   
    <control>
        <name>container_header</name>
        <property>
            <name>styleClass</name>
            <value>container_header</value>
        </property>
    </control>
   
    <control>
        <name>container_body</name>
        <property>
            <name>styleClass</name>
            <value>container_body</value>
        </property>
    </control>
   
    <control>
        <name>container_footer</name>
        <property>
            <name>styleClass</name>
            <value>container_footer</value>
        </property>
    </control>
   
    <control>
        <name>header_title</name>
        <property>
            <name>tagName</name>
            <value>h1</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:@DbTitle()}</value>
        </property>
    </control>
   
    <control>
        <name>header_subtitle</name>
        <property>
            <name>tagName</name>
            <value>h2</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['header_subtitle']}</value>
        </property>
    </control>
   
    <control>
        <name>footer_disclaimer</name>
        <property>
            <name>tagName</name>
            <value>p</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['footer_disclaimer']}</value>
        </property>
    </control>
   
    <control>
        <name>container_left</name>
        <property>
            <name>styleClass</name>
            <value>container_left column</value>
        </property>
    </control>
   
    <control>
        <name>container_middle</name>
        <property>
            <name>styleClass</name>
            <value>container_middle</value>
        </property>
    </control>
   
    <control>
        <name>container_right</name>
        <property>
            <name>styleClass</name>
            <value>container_right column</value>
        </property>
    </control>
   
    <control>
        <name>label_name</name>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['label_name']}</value>
        </property>
    </control>
   
    <control>
        <name>label_mailaddress</name>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['label_mailaddress']}</value>
        </property>
    </control>
   
    <control>
        <name>label_type</name>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['label_type']}</value>
        </property>
    </control>
   
    <control>
        <name>label_product</name>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['label_product']}</value>
        </property>
    </control>
   
    <control>
        <name>label_complaint</name>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['label_complaint']}</value>
        </property>
    </control>

</theme>

Pretty basic stuff really, and very non-application specific (which is how a corporate theme should be), but there are a few things to point out here.

First, the Resource Bundle ("preferences.properties") is an in-NotesDatabase Design Element. Second, and more to the point of installing custom Themes on a Domino Server, the "acme.css" Stylesheet is installing on the IBM Lotus Domino Server and is thus referenced in the Theme.

So, to install a Custom Theme on an IBM Lotus Domino Server, you'll first need to do some Server OS-level file management and make sure that you point to the target resources using the proper relative paths.

As my Domino Server is installed on a Windows server, here's the target installation paths for the various contents that make up the "acme.theme":

 

Resource Target Directory
acme.theme C:\IBM\Lotus\Domino\xsp\nsf\themes\
acme.css
chicken_basket.ttf
chicken_basket.eot
"images" Directory
C:\IBM\Lotus\Domino\data\domino\html\acme\

 

Take special note of the "images" Directory above, as that's key and is more Web Developer than most Notes Developers honestly get involved in. See, the "acme.css" Stylesheet uses the TrueType Font (and EOT, for Internet Explorer) files for @Font-Face custom fonts in the CSS, and uses several images which we'll put in the aforementioned "images" Directory. The catch is, these are relative path resources to our CSS, meaning that it can access those files and images relative to it's current directory.

Showing you the contents of the "acme.css" might help explain this better...

@font-face {
    font-family: chicken_basket;
    src: url(chicken_basket.eot);
}
@font-face {
    font-family: chicken_basket;
    src: url(chicken_basket.ttf);
}


body {
    margin: 0px;
    padding: 0px;
    text-align: left;
    background-color: #B02B2C;
}

div.container_main {
    margin-top: 25px;
}

div.container_header, div.container_body {
    width: 975px;
    margin: 0px auto 0px auto;
    text-align: left;
}
div.container_header {
    background-color: #3e658c;
    background-image: url(images/acme.png);
    background-position: left center;
    background-repeat: no-repeat;
    height: 200px;
}
div.container_header h1 {
    display: none;
}
div.container_header h2 {
    position: relative;
    left: 510px;
    top: 65px;
    color: #fff;
    font-family: chicken_basket;
    font-size: 24pt;
    width: 450px;
    font-weight: normal;
}

div.container_middle {
    position: relative;
    top: -37px;
    background-color: #3e658c;
}

.xspTabTabbedPanel {
    background-color: #fff;
}

.xspPagerContainer, .xspPagerContainer * {
    background-color: transparent;
}

div.container_footer {
    position: fixed;
    clear: both;
    height: 25px;
    bottom: 0px;
    width: 100%;
    background-color: #333;
}

div.container_footer p {
    display: block;
    width: 975px;
    margin: 0px auto 0px auto;
    padding: 4px;
    color: #eee;
    text-align: center;
}

The url values for the fonts and the images are all relative to the current directory position of the stylesheet... meaning that to get a handle on the header banner image, I just have to point to "images/acme.png".

As seen in the "acme.theme" however, the path to the stylesheet needs to be exact, but additionally relative to the Themes current directory. To do this -- and you've already seen in the Theme XML -- you use the "/.ibmxspres/domino/{themename}/" syntax.

Once this is installed on the IBM Lotus Domino Server, you can start working on your "acme.extended.theme".

"acme.extended.theme"/in-NotesDatabase Theme Setup

All you really need to do is create a new Theme -- we'll name it "acme.extended.theme" -- make sure that it extends the "acme.theme", and set it as the Theme for our NotesDatabase.

We'll go a little more detailed than that however...

<theme extends="acme">

    <resources>
        <styleSheet href="/app.css" />
    </resources>

    <control>
        <name>container_left</name>
        <property>
            <name>rendered</name>
            <value>#{javascript:return false}</value>
        </property>
    </control>
   
    <control>
        <name>container_right</name>
        <property>
            <name>rendered</name>
            <value>#{javascript:return false}</value>
        </property>
    </control>
   
    <control>
        <name>table_complaint</name>
        <property>
            <name>styleClass</name>
            <value>table_complaint</value>
        </property>
    </control>
   
    <control>
        <name>table_complaint_col0</name>
        <property>
            <name>styleClass</name>
            <value>table_complaint_col0</value>
        </property>
    </control>
   
    <control>
        <name>table_complaint_col1</name>
        <property>
            <name>styleClass</name>
            <value>table_complaint_col1</value>
        </property>
    </control>

    <control>
        <name>header_complaintform</name>
        <property>
            <name>tagName</name>
            <value>h3</value>
        </property>
        <property>
            <name>styleClass</name>
            <value>header_complaintform heading</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['header_complaintform']}</value>
        </property>
    </control>
   
    <control>
        <name>header_complaints</name>
        <property>
            <name>tagName</name>
            <value>h3</value>
        </property>
        <property>
            <name>styleClass</name>
            <value>header_complaints heading</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['header_complaints']}</value>
        </property>
    </control>
   
    <control>
        <name>content_complaint_intro</name>
        <property>
            <name>tagName</name>
            <value>p</value>
        </property>
        <property>
            <name>styleClass</name>
            <value>content_complaint_intro</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['content_complaint_intro']}</value>
        </property>
    </control>
   
    <control>
        <name>complaint_submit</name>
        <property>
            <name>styleClass</name>
            <value>xspButtonSubmit complaint_submit</value>
        </property>
        <property>
            <name>value</name>
            <value>#{javascript:preferences['label_submitcomplaint']}</value>
        </property>
    </control>
   
    <control>
        <name>viewPanel_complaints</name>
        <property>
            <name>viewStyle</name>
            <value>width:950px;</value>
        </property>
    </control>
   
    <control>
        <name>card</name>
        <property>
            <name>styleClass</name>
            <value>card</value>
        </property>
    </control>
   
    <control>
        <name>complaint_name</name>
        <property>
            <name>styleClass</name>
            <value>complaint_name</value>
        </property>
        <property>
            <name>tagName</name>
            <value>h4</value>
        </property>
    </control>
   
    <control>
        <name>complaint_type</name>
        <property>
            <name>styleClass</name>
            <value>complaint_type</value>
        </property>
        <property>
            <name>tagName</name>
            <value>h5</value>
        </property>
    </control>
   
    <control>
        <name>complaint_product</name>
        <property>
            <name>styleClass</name>
            <value>complaint_product</value>
        </property>
        <property>
            <name>tagName</name>
            <value>h5</value>
        </property>
    </control>
   
    <control>
        <name>complaint_complaint</name>
        <property>
            <name>styleClass</name>
            <value>complaint_complaint</value>
        </property>
        <property>
            <name>tagName</name>
            <value>p</value>
        </property>
    </control>
   
       
    <control>
        <name>form</name>
        <property>
            <name>styleClass</name>
            <value>table_form</value>
        </property>
    </control>
   
    <control>
        <name>td_label</name>
        <property>
            <name>styleClass</name>
            <value>label</value>
        </property>
    </control>
   
    <control>
        <name>td_field</name>
        <property>
            <name>styleClass</name>
            <value>field</value>
        </property>
    </control>
   
    <control>
        <name>image_help</name>
        <property>
            <name>url</name>
            <value>help.jpg</value>
        </property>
    </control>

</theme>

A few things to point out here. Of course, this extends "acme" which resides on the server. Then there's the inclusion of a new application-specific Stylesheet. This app-specific Stylesheet will allow you to customize the look and feel of this application. There is also extensive use of the Resource Bundle which was injected as a resource in the "acme.theme", and a few tricks such as setting the tagName for a given control or the url value of an image control.

I also force certain controls to not render. This is more inline with the concept of having a corporate application framework and using Themes to disable sections of the application that you quite frankly won't use. In this example, I'm not rendering a left or right column in the "body" of my application layout and thus using the entire "body" screen real estate for my application (where the left and right columns could contain corporate-standard navigation in an application that required such a thing).

Let's take a look at our themed application: Acme Anvil Corporation - Complaints.

Acme Anvil Corporation - Complaints Screencap Acme Anvil Corporation - Complaints Screencap

And let's take a look at the Home.xsp (the default launch object for this application) XPage markup:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

 <xp:div themeId="container_main">
  <xp:div themeId="container_header">
   <xp:text id="header_title" themeId="header_title" />
   <xp:text id="header_subtitle" themeId="header_subtitle" />
  </xp:div>
  <xp:div themeId="container_body">
   <xp:div themeId="container_left" />
   <xp:div themeId="container_middle">
    <xp:tabbedPanel id="tabbedPanel1">
     <xp:tabPanel label="Home" id="tabPanel1">
      <xp:include id="include1" pageName="/Complaint.xsp" />
     </xp:tabPanel>
    </xp:tabbedPanel>
   </xp:div>
   <xp:div themeId="container_right" />
  </xp:div>
  <xp:div themeId="container_footer">
   <xp:text id="footer_disclaimer" themeId="footer_disclaimer" />
  </xp:div>
 </xp:div>

</xp:view>

Pretty simple stuff. Notice the left and right columns which do not appear (read: render) as the result of our application-specific Theme. The rest of the application is full of little tweaks and interesting techniques to create a rather simple yet amazingly functional application.

Here's the Complaint.xsp which is included in Home.xsp:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

 <xp:table themeId="table_complaint">
  <xp:tr>
   <xp:td themeId="table_complaint_col0">
    <xp:panel>
     <xp:this.data>
      <xp:dominoDocument var="complaint" formName="complaint" />
     </xp:this.data>
     <xp:text id="header_complaintform" themeId="header_complaintform" />
     <xp:text id="content_complaint_intro" themeId="content_complaint_intro" />
     <xp:table themeId="form">
      <xp:tr>
       <xp:td themeId="td_label">
        <xp:label themeId="label_name" />
       </xp:td>
       <xp:td themeId="td_field">
        <xp:inputText themeId="field_name" value="#{complaint.name}" />
       </xp:td>
      </xp:tr>
      <xp:tr>
       <xp:td themeId="td_label">
        <xp:label themeId="label_mailaddress" />
       </xp:td>
       <xp:td themeId="td_field">
        <xp:inputText themeId="field_name" value="#{complaint.mailaddress}" />
       </xp:td>
      </xp:tr>
      <xp:tr>
       <xp:td themeId="td_label">
        <xp:label themeId="label_type" />
       </xp:td>
       <xp:td themeId="td_field">
        <xp:comboBox id="field_type"
        themeId="field_type" value="#{complaint.type}">
         <xp:selectItems value="#{javascript:preferences['value_type'].split(',')}" />
        </xp:comboBox>
       </xp:td>
      </xp:tr>
      <xp:tr>
       <xp:td themeId="td_label">
        <xp:label themeId="label_product" />
       </xp:td>
       <xp:td themeId="td_field">
        <xp:comboBox id="field_product"
        themeId="field_product" value="#{complaint.product}">
         <xp:selectItems
         value="#{javascript:preferences['value_product'].split(',')}" />
        </xp:comboBox>
       </xp:td>
      </xp:tr>
      <xp:tr>
       <xp:td themeId="td_label">
        <xp:label themeId="label_complaint" />
       </xp:td>
       <xp:td themeId="td_field">
        <xp:inputTextarea id="field_complaint" themeId="field_complaint" value="#{complaint.complaint}" />
       </xp:td>
      </xp:tr>
     </xp:table>
     <xp:button id="complaint_submit" themeId="complaint_submit">
      <xp:eventHandler
      themeId="complaint_submit_onclick" event="onclick" submit="true"
      refreshMode="complete" immediate="false" save="true">
       <xp:this.action><![CDATA[#{javascript:complaint.save();
context.redirectToHome()}]]></xp:this.action>
      </xp:eventHandler>
     </xp:button>
    </xp:panel>
   </xp:td>
   <xp:td themeId="table_complaint_col1">
    <xp:image id="image1" themeId="image_help" />
   </xp:td>
  </xp:tr>
 </xp:table>

 <xp:text id="header_complaints" themeId="header_complaints" />

 <xp:viewPanel id="viewPanel_complaints"
 themeId="viewPanel_complaints" showColumnHeader="false"
 var="thisentry" rows="1">
  <xp:this.facets>
   <xp:pager partialRefresh="true"
   layout="Previous Group Next" xp:key="headerPager" id="pager1" />
  </xp:this.facets>
  <xp:this.data>
   <xp:dominoView var="complaints"
   viewName="complaints" />
  </xp:this.data>
  <xp:viewColumn columnName="name"
  id="viewColumn1" displayAs="hidden">
   <xp:this.value><![CDATA[#{javascript:return ""}]]></xp:this.value>
   <xp:panel>
    <xp:this.data>
     <xp:dominoDocument
     var="thiscomplaint" formName="complaint"
     action="openDocument"
     documentId="#{javascript:thisentry.getDocument().getUniversalID()}" />
    </xp:this.data>
    <xp:div themeId="card">
     <xp:text id="complaint_name" themeId="complaint_name" value="#{thiscomplaint.name}" />
     <xp:text id="complaint_type" themeId="complaint_type" value="#{thiscomplaint.type}" />
     <xp:text id="complaint_product" themeId="complaint_product" value="#{thiscomplaint.product}" />
     <xp:text id="complaint_complaint" themeId="complaint_complaint" value="#{thiscomplaint.complaint}" />
    </xp:div>
   </xp:panel>
   <xp:viewColumnHeader value="name"
   id="viewColumnHeader1" />
  </xp:viewColumn>
 </xp:viewPanel>

</xp:view>

Again, simple.

And for those of you curious, here's the "preferences.properties" Resource Bundle:

header_subtitle=For fifty years, the leader in creative mayhem!
footer_disclaimer=This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. ('xcept all Warner Bros. stuff -- please don't sue me!)
header_complaintform=File a Complaint
header_complaints=Filed Complaints
content_complaint_intro=While Acme strives for success with all of our products, we understand that nobody's perfect. Please help us improve by filing any complaints you may have or issues you've encountered using one of our amazing products!
label_name=Your Name
label_mailaddress=Email
label_type=Type of Issue
label_product=Acme Product
label_complaint=Complaint
label_submitcomplaint=Submit Complaint
value_type=,Damaged Product,General Malfunction,Blew up my face
value_product=,Anvil,Aspirin,Axle Grease (guaranteed slippery),Balloon,Basket,Bat-man's Outfit,Bed Springs,Bird Seed,Bomb,Bumble Bees (One-fifth),Catcus Costume,Christmas Package Machine,Dehydrated Boulders (just add water),Detonator,Do It Yourself Tornado Kit,Dog Sled,Dynamite,Earth Quake Pills,Electronic Fan,Electronic Motor,Explosive Tennis Balls,Female Road Runner Costume,Frisbee,Giant Mouse Trap,Giant Kite Kit,Giant Rubber Band,Glue,Grease,Handlebars,High Speed Tonic (contains Vitamins R-P + M),Instant Icicle Maker,Instant Road,Invisible Paint,Iron Bird Seed,Iron Pellets,Jet Motor,Jet Propelled Pogo Stick,Jet Propelled Skis,Jet Propelled Unicycle,Jim-Dandy Wagon,Lightning Bolts (rubber gloves included),Little Giant Do-It-Yourself Rocket-Sled Kit,Little Giant Snow Cloud Feeder (makes instant snow),Matches,Mouse Snare,Nitroglyercin,Outboard Motor,Road Runner Lasso,Rocket Powered Roller Skates,Rocking Horse,Roller Skates,Roller Skis (no snow necessary),Speed Skates (ice),Street Cleaners Wagon,Super Outfit,TNT,Triple Strength Battleship,Wash Tub,Weather Ballon Trucking,Water Pistol

Figure you might as well load comboBox (et al) listItem values this way. Makes taking the application multi-lingual that much easier!

Conclusion

Corporate-branding your XPage applications via a shared, on-IBM Lotus Domino Server Theme is a simple and ultimately time-saving approach to developing XPage apps. It will allow you to rather easily update all of the applications within your environment (should Acme Anvil Corporation become Acme, Inc. in the near future...). It also helps ramp-up your application development when used in tandem with an XPages application framework. From there, it's a matter of picking what type of NotesData will be used within the application and Google Image Searching for funny pictures of cartoon characters.

... ok, that last one might not really apply in most cases.


About the author: Chris Toohey

Thought Leadership, Web & Mobile Application Development, Solutions Integration, Technical Writing & Mentoring

A published developer and webmaster of dominoGuru.com, Chris Toohey specializes in platform application development, solutions integration, and evangelism of platform capabilities and best practices.



More from dominoGuru.com


dominoGuru.com is powered by IBM Notes Domino XPages & hosted by Prominic.NET

Contact Us

Use our Contact / Feedback form or one of these email addresses:

Creative Commons License

Except where otherwise noted, dominoGuru.com by Chris Toohey is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.