What and Why?

This article is about a piece of software called Deliverance. The purpose of Deliverance is to make it easier to theme a Plone web site, and to make it possible to integrate other web applications using the same theme. Theming a Plone site has historically been a pain. You had to know a lot about Plone and how to build add-on Products. Deliverance separates the theme from the Plone site. It allows someone who knows about HTML and CSS to build a theme and apply that theme to a Plone site without tinkering with the Plone site itself. Hurray!

I'll assume that readers are already familiar with running a Plone site on a Linux server with Apache as your front-end proxy. I'll explain how to apply a theme by using Deliverance as a proxy server between Plone and Apache. If you're new to Plone you should read the docs on plone.org

My example site is for a bed and breakfast in Walla Walla, Washington called Stone Creek Manor. The previous Stone Creek Manor site was flash-based, so the owner couldn't make changes herself. With her new Plone site, she's able to log in and make changes any time.

How?

Here's how I got it installed and working. I already have Plone installed and running on localhost:8080. My Plone site is called "scm". Apache is running on port 80. The Deliverance  proxy server will be running on port 8000.

Install Deliverance

I tried the instructions on the Deliverance web site but I couldn't get it installed that way. Instead I used easy_install to install Deliverance in /usr/local/deliverance. This may not be "best practices" but it worked for me.

Install Supervisor

Supervisor is used to make sure that Deliverance is running. I installed it with easy_install as well. Here are the configuration lines I added to /etc/supvisord.conf to make Deliverance start:

[program:deliverance]
command = /usr/local/deliverance/bin/deliverance-proxy /usr/local/deliverance/etc/deliverance.xml

After Supervisor is installed and configured just start it using the "supervisord" command. To start the Deliverance server, use the command "supervisorctl start all".

Configure Apache

I'm running Apache in front of Deliverance, so I need to configure it.

Inside my <VirtualHost> section I have these lines to allow access to my static theme files which are stored at /var/www/static/scm.  Remember not to create an object in the root of your Plone site called "static"!

  Alias /static /var/www/static/scm
  <Directory /static>
      AllowOverride None
      Order allow,deny
      Allow from all
  </Directory>

I had to add this line to my Apache config so that I could test for the domain in my Deliverance rules. I'm going to have more than one site served by this Deliverance instance, so I need to be able to set a page class based on the domain. I found this tip at: http://macadames.wordpress.com/2009/05/23/some-deliverance-tips.

# This is for Deliverance. 
# We need to know what host this request is really for.
# Otherwise the domain is just localhost.
ProxyPreserveHost On

Here's my Apache rewrite rule that makes my Plone site appear to be at the root of www.example.com. This is the same rule that I use without Deliverance, except for the RewriteCond statement and the port number.

RewriteEngine On
RewriteCond  %{REQUEST_URI} !/static
RewriteRule ^/(.*) \
      http://localhost:8000/VirtualHostBase/http/%{SERVER_NAME}:80/scm/VirtualHostRoot/$1 [L,P]

Create a static theme

I created my static theme with help from the example I found in the Deliverance demo here: http://svn.plone.org/svn/collective/deliverancedemo/trunk/static/. I put my static files in /var/www/static/scm.

My theme is fairly simple. It has a fixed width container with a header section, a two-column content area, and a footer section. The right column of the content area is the sidebar, where I have some links and widgets. Since it's a small site, the sidebar is the same for every page and I hard-coded it in my theme file instead of using Plone portlets.

Here are some code snippets from my theme's index.html file:

Header

   <div id="header"><!-- Header logo + top nav buttons. -->
      <div id="logo">
        <a href="/">Home</a>
      </div>
      <div id="links">
        <ul>
          <li><a href="#">Test</a></li>
          <li><a href="#">Test 2</a></li>
          <li><a href="#">Test 3</a></li>
          <li><a href="#">Test 4</a></li>
        </ul>
      </div>
      <div id="pathbar"></div>
    </div>

 This is the header area, as you can see. The div called "logo" is filled (via css) with my site's logo banner image and it's a link to the root of my site. The text "Home" in the link is moved off of the page with css tricks. The div called "links" is where the top navigation is shown. I'll replace these with my Plone site's global_nav bar. There's a place for the pathbar (breadcrumbs), but I don't actually put anything in there on this site.

Main Body

    <div id="leftbar"> <!-- Main content + Plone editing bars -->
      <div id="edit-bar">
        <div id="view-menu"></div>
        <div id="action-menu"></div>
      </div>
      <br />
      <div id="bodytext">
        <p>Nulla sit amet magna non enim posuere porttitor. Vestibulum ante. </p>
      </div>
    </div>

 

This is just a two-column layout and my main column is on the left, so my main content area is called "leftbar". On a Plone "page" type of object I only want to display the edit bars and the main content (the parent-fieldname-text div) of the page, not the Title, Description, etc. so I have divs to put these items into.

On other types of Plone objects, like forms, etc. I'll put the whole portal-column-content div in the leftbar div.

Sidebar

    <div id="rightbar"><!-- Sidebar content -->
      <h2>Phone number here</h2>
      <h3><a href="http://www.stonecreekmanor.blog.com/" title="Read our blog">Read our Blog</a>
      </h3>
    </div>

My sidebar is on the right side of the page. I have some static elements that go on every page, like the phone number and a link to the blog. I put these in my theme, although they could just as well go in a Plone static HTML portlet. The portlets from the Plone site will be added in below these static elements.

Footer

  <div id="bottom"><!-- Footer area -->
    <div id="email">
      <a href="mailto:info@stonecreekmanor.com">info@stonecreekmanor.com</a>
    </div>
    <div id="developer">
      <p>Site by: <a href="http://www.catapultsolutions.net/">Catapult Solutions</a>.</p>
    </div>
  </div>    
  <div id="personal-bar"><!-- Login/logout buttons -->
    <ul id="personal-bar-tools">
      <li>
        <a href="/sitemap" title="View our sitemap">Site Map</a>
      </li>
    </ul>
  </div>

 In the footer area I hard-code the business email address and a link to my own site in the theme in one row, then I have another row with an unordered list that contains a link to the standard Plone sitemap page. I'll be adding the personal_tools items to this unordered list, so the "login" link, etc. will show up here.

That's the end of the theme's HTML. Of course there's a style sheet and some images as well.

Configure Your Plone Site

There are a couple of changes that need to be made in your Plone web site for Deliverance to work properly. You'll need to get into your Site Setup > Zope Management Interface > portal_css. Find the style sheet called "authoring.css" and uncheck the box marked "Merging allowed?", then change the "Render type" from "import" to "link". Do the same for the style sheet called "portlets.css".

As usual you'll need to configure you Plone site's email settings under Site Setup > Mail.

Your deliverance.xml Rules File

The deliverancedemo files that I exported from svn has an example deliverance.xml file included. I started with this and modified it to my taste. I had to change things a bit so that I can server more than one Plone site, each with it's own theme, with the same Deliverance instance. For more information about these rules, see the official documentation in the deliverancedemo files from svn.

The Server Settings

<?xml version="1.0" encoding="UTF-8"?>
<ruleset>

  <server-settings>
    <server>localhost:8000</server>
    <execute-pyref>true</execute-pyref>
    <dev-allow>localhost</dev-allow>
    <dev-htpasswd>deliv-users.htpasswd</dev-htpasswd>
  </server-settings>

This is the beginning of your deliverance.xml file. The server settings section tells the proxy server what port to start on, etc.

The Proxy Rule

  <proxy path="/">
    <dest href="http://localhost:8080" />
  </proxy>

This tells Deliverance that any page should be proxied to the local machine on port 8080. This is the port that my Plone site is running on.

Special Rules for the Kupu Editor

  <!-- RULES FOR MAKING KUPU WORK -->
  <match  path="contains:emptypage" class="emptypage">
  </match>
  <rule class="emptypage">
      <replace content="element:/html/head"  theme="element:/html/head" />
      <replace content="element:/html/body"  theme="element:/html/body"  />
  </rule>

I found some helpful advice in this blog post. Apparently, the Kupu edit box is an iframe with a page called emptypage inside. We need this page to come through as-is, so we'll take the whole <head> and the whole <body> of this special page, not using any elements from the theme.

The 'path="contains:emptypage"' part tells Deliverance that any page with the text "emptypage" anywhere in its path will be given the page class "emptypage".

The <rule class="emptypage"> part tells Deliverance that any pages with the page class "emptypage" should use these rules.

Special Cases

  <!-- Rules for special cases in Plone sites -->
  <match path="contains:image_view_fullscreen" abort="1" />
  <match path="contains:referencebrowser_popup" abort="1" />

We use <match> rules to "abort," or skip the skinning process and proxy the page as is if we're dealing with viewing a Plone image object at full size. We do the same for the little pop-up window for the Plone Reference Browser.

I'm sure there are more special cases that will crop up over time.

Different Domains Get Different Themes

  <!-- Rules to assign special page classes to pages from different domains -->
  <match domain="contains-insensitive:stonecreekmanor" class="scm">
  </match>
  
  <match domain="sbp.*" path="/" class="sbp">   
  </match>

So far I have two Plone sites being served through this Deliverance instance.

For the first one I'm going to search for the word "stonecreekmanor" in part of the URL that Deliverance calls the "domain." In this example, the domain is "www.stonecreekmanor.com, but I can just search for "stonecreekmanor". By using the search type "contains-insensitive" I'm telling Deliverance to match any domain that contains the specified text, and that the match is not case sensitive.

My second site is reached by the domain sbp.catapultsolutions.net, so I'll search for "sbp.*". This will match any domain that begins with "sbp.".

Rules for www.stonecreekmanor.com

  <!-- These rules are for Stone Creek Manor Bed and Breakfast -->
  <rule class="scm" suppress-standard="1">    
    
    <!-- Theme -->
    <theme href="http://www.stonecreekmanor.com/static/" />

We start off the section for my first site with a <rule>. This section will include lots of other statements and at the end there is a </rule> of course.

This set of rules will be used on every page request that has a page class of "scm," which was set by the <match> we saw above.

I'm suppressing the standard rules that Deliverance uses by default. I'll declare all of my rules explicitly.

The <theme> line here tells Deliverance where to find the theme files. We've already looked at my Apache configuration that allows us to find the theme files on the file system using the /static/ directory.

The Base Tag

    <!-- Add in the Plone-generated base tag -->
    <prepend content='/html/head/base' theme='children:/html/head' />

 

There's a special <base> tag in Plone sites that is needed if all is to go according to plans. This rule prepends the <base> tag at the beginning of the <head> of our page.

Use Plone's CSS, JavaScript, etc.

    <!-- Add in the Plone-created CSS, JS, and other links
      in addition to the static ones -->
    <prepend content='/html/head/script' theme='/html/head' />
    <prepend content='/html/head/link' theme='/html/head' />
    <prepend content='/html/head/style' theme='/html/head' />
    <prepend content='/html/head/meta' theme='/html/head' />    
    <replace content='/html/head/title' theme='/html/head/title' />

Here we suck in all of the scripts, links, meta tags and the title tag. I have my own style sheet in my theme, but I pull in Plone's as well in case there's a page that I haven't thought about styling, like the login form or the contact form. These forms will have the default Plone styling until I decide to override the styles in my custom stylesheet. If you wanted to do all this styling in your theme you could leave out some of the Plone stylesheets to save time on page loads.

Note that I've prepended the style sheets at the top of the <head> section of the page. This allows my custom style sheet to be loaded last, giving it precedence.

Add Plone's id and class Attributes to Our <body> tag

    <!-- Append the id/class attributes from the body tag, 
      this is important for Kupu and per-section styling -->
    <append content="attributes(id,class):/html/body" theme="attributes:/html/body" />

Here we append the "id" and "class" attributes of our Plone page's <body> to the <body> tag from our theme.

Copy the Plone portal_tabs

Well, they used to be called portal_tabs ;-)

    <!-- Copy the main navigation -->
    <replace content='children:#portal-globalnav' theme='children:#links ul' /> 

This line keeps the "links" div from my theme, but replaces everything inside of it with all of the things inside of the Plone site's "portal-globalnav" div.

The Plone Personal Bar

    <!-- Copy the personal bar -->
    <append content='children:#portal-personaltools' 
            theme='children:#personal-bar-tools' />

Here we append everything in the Plone site's "portal-personaltools" unordered list to my theme's "personal-bar-tools" unordered list. I have a hard link to the standard Plone sitemap page in my themes "personal-bar-tools" unordered list, so the portal_personaltools stuff gets added after that link.

Pages That Are Not Plone "Page" Objects

    <!-- Copy over the contents of the page body if no 
      parent-fieldname-text.
      This is needed for login_form, contact-info, etc. -->
    <replace content='children:#portal-column-content' 
             theme='children:#leftbar'
             if-content='not:#parent-fieldname-text' />

Most of the pages we are dealing with are Plone "Page" objects, but some are not. We have things like the login form, the contact form, etc.

For "Page" objects we mainly want to take that object's "parent-fieldname-text" div, which is the cooked body of the page, and plunk it down in the content well of our theme. When we're dealing with something that's not a "Page" we don't have a "parent-fieldname-text" div, so we need to make other arrangements.

The way to make these forms work is to include the whole "portal-column-content" div in our theme's content well, known as the "leftbar" div since this is a two-column layout and our sidebar is on the right.

We only want to do this if the Plone page's content doesn't include a CSS id of "parent-fieldname-text", so we use the "if-content" line with the "not:" in front of the CSS selector we want to test for.

The Plone Edit Bar

    <!-- Copy the edit bar if we're on a normal page-->
    <replace content='#content-views' theme='children:#view-menu' 
              if-content='#parent-fieldname-text'/>
    <replace content='div.contentActions' theme='children:#action-menu'  
              if-content='#parent-fieldname-text'/>

I want my client be able to edit her pages in the themed version of the site, not the standard Plone look, so if this is a Plone "Page" object and it has the "parent-fieldname-text" div I'm going to pull in the editing bar that allows users to edit, delete, add, etc. Of course this will only show up for users who are logged in and have permission to edit.

Pull in the Main Content Text

    <replace content='children:#parent-fieldname-text' 
             theme='children:#bodytext'  
             if-content='#parent-fieldname-text'/>

If this is a Plone "Page" object and it has a "parent-fieldname-text" div, put the contents of that div inside of the "bodytext" div in the theme.

Plone Portlets

    <!-- stuff to remove from portlet -->
    <drop content='dt.portletHeader' />
    <drop content='dd.portletFooter' />

    <!-- Bring the portlet columns inside the sidebar -->
    <append content='children:#portal-column-one'  theme='children:#rightbar' />
    <append content='children:#portal-column-two'  theme='children:#rightbar' />

I've got some widgets in TAL Portlets in my Plone site, so I'm going to pull them into my sidebar, called the "rightbar" div in my theme.

First I need to drop the portlet headers and footers, since I don't want them in the finished product. I use the <drop> lines to do that.

Then I append the stuff in the right and left portlet columns from my Plone site to the "rightbar" div of my theme.

Google Analytics Code

    <!-- Add in any inline scripts, like Google Analytics -->    
    <append content='/html/body/div/script' theme='children:/html/body' />

  </rule>

Here I append the Google Analytics JavaScript code at the end of the <body>. You'll notice that this is my last rule and the </rule> tag comes next.

Notes

While working on this project my main tool was the Firebug add-in for Firefox. I used it to identify the divs in the theme and the Plone site, and to debug issues with CSS.

I use Notepad++ as my text editor. I like it because it can be configured to my liking, highlights opening and closing tags, is fast, etc.

I also use the Firefox Web Developer Toolbar add-in. It allows me to turn off caching, validate HTML and CSS, and lots more.

Please let me know if you like this article. It turned out to be longer than I expected, but I hope it will help someone else get started with Deliverance.

Document Actions