Skip to content

Instantly share code, notes, and snippets.

@optilude
Created June 24, 2012 22:57
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save optilude/2985375 to your computer and use it in GitHub Desktop.
Save optilude/2985375 to your computer and use it in GitHub Desktop.

Diazo theming

This guide provides an overview of Diazo theming in Plone.

Contents

What is a Diazo theme?

A "theme" makes a website (in this case, one powered by Plone) take on a particular look and feel.

Diazo (formerly known as XDV) is a technology that can be used to theme websites. It is not specific to Plone per se, but has been created by the Plone community and, as of Plone 4.3, provides the default way to apply a theme to a Plone site. You can learn more about Diazo at http://diazo.org.

Diazo themes may be a little different to themes you have created in other systems, and indeed to themes you may have created for earlier versions of Plone. A Diazo theme is really about transforming some content - in this case the output from "vanilla" Plone - into a different set of HTML markup by applying a set of rules to combine a static mock-up of the end result you want with the dynamic content coming from Plone.

In comparison, the previous way to theme a Plone site (like the way many other content management systems are themed) relies on selectively overriding the templates and scripts that Plone uses to build a page with different versions that produce different HTML markup. The latter approach can be more powerful, certainly, but also requires much deeper knowledge of Plone's internals and command of server-side technologies such as Zope Page Templates and even Python. Diazo themes, in contrast, are easy to understand for web designers and non-developers alike.

A Diazo theme consists of three elements:

  1. One or more HTML mockups, also referred to as theme files, that represent the desired look and feel.

    These will contain placeholders for content that is to be provided by the Plone content management system. Mockups usually reference CSS, JavaScript and image files by relative path. The most common way to create a theme is to usedesktop software like Dreamweaver or a text editor to create the relevant markup, styles and scripts, and test the theme locally in a web browser.

  2. The content that is being themed. In this case, that is the output from Plone.
  3. A rules file, which defines how the placeholders in the theme (i.e. the HTML mockup) should be replaced by relevant markup in the content.

    The rules file uses XML syntax (similar to HTML). Here is a very simple example:

    <?xml version="1.0" encoding="UTF-8"?>
    <rules
        xmlns="http://namespaces.plone.org/diazo"
        xmlns:css="http://namespaces.plone.org/diazo/css"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">`
    
        <theme href="theme.html" />
    
        <replace css:content-children="#content" css:theme-children="#main" />
    
    </rules>

    Here, we are replacing the contents (child nodes) of placeholder element with HTML id main in the theme file (theme.html, found in the same directory as the rules.xml file, as referneced by the <theme /> rule) with the contents (children) of the element with the HTML id content in the markup generated by Plone.

    When this theme is applied, the result will look very much like the static HTML file theme.html (and its referenced CSS, JavaScript and image files), except the placeholder that is identified by the node in the theme with id main will be filled by Plone's main content area.

Plone ships with an example theme called, appropriately, Example theme, which uses the venerable Twitter Bootstrap to build a simple yet functional theme exposing most of Plone's core functionality. You are advised to study it - in particular the rules.xml file - to learn more about how Diazo themes work.

Using the control panel

TODO

Selecting a theme

TODO: Describe how to enable a theme in the control panel

Creating a new theme

TODO: Describe actions: cloning and creating anew

Uploading an existing theme

TODO: Describe uploading a theme - with or without rules.xml file to start with

The file manager

TODO: Describe purpose and UI

The rule builder

TODO: Describe purpose and UI - filesystem and ZODB themes

Advanced settings

TODO: Describe the available settings at a high level

Reference

The remainder of this guide contains reference materials useful for theme builders.

Deploying and testing themes

To build and test a theme, you must first create a static HTML mockup of the look and feel you want, and then build a rules file to describe how Plone's content mapes to the placeholders in this mockup.

The mockup can be created anywhere using whatever tool you feel most comfortable building web pages in. To simplify integration with Plone, you are recommended to make sure it uses relative links for resources like CSS, JavaScript and image files, so that it can be opened in a web browser from a local file. Plone will convert these relative links to the appropriate absolute paths automatically, ensuring the theme works no matter which URL the user is viewing.

There are several ways to get the theme into Plone:

  1. On the filesystem

If you used an installer or a standard "buildout" to set up your Plone site, you should have a directory called resources in the root of your Plone installation (this is created using the resources option to the buildout recipe plone.recipe.zope2instance. See http://pypi.python.org/pypi/plone.recipe.zope2instance for more details.)

You can find (or create) a theme directory inside this directory, which is used to contain themes. Each theme needs its own directory with a unique name. Create one (e.g. resources/theme/mytheme) and put your HTML files and any references resources inside this directory. You can use subdirectories if you wish, but you are recommended to keep the basic theme HTML files at the top of the theme directory.

You will also need a rules file called rules.xml. If you haven't got one yet, start with an empty one:

<?xml version="1.0" encoding="UTF-8"?>
<rules
    xmlns="http://namespaces.plone.org/diazo"
    xmlns:css="http://namespaces.plone.org/diazo/css"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">`

    <theme href="theme.html" />
    <replace css:content-children="#content" css:theme-children="#main" />

</rules>

Provided you are running Zope in debug mode (e.g. you start it up with bin/instance fg), changes to the theme and rules should take effect immediately. You can preview or enable the theme through the Themes control panel, and then iteratively modify the rules.xml file or the theme mockup as you wish.

  1. Through the web

If you prefer (or do not have filesystem access), you can create themes entirely through the Plone control panel, either by duplicating an existing theme, or starting from scratch with a near-empty theme.

TODO: Describe specific UI to do this

Once a theme has been created, you can modify it through the in-Plone theme file manager or rule builder. See below for more details.

  1. As a zip file

Themes can be downloaded from Plone as Zip files, which can then be uploaded into other sites.

TODO: Describe specific UI to do this

In fact, you can create valid theme zip archives by compressing a theme directory on the filesystem using a standard compression tool such as 7-Zip or Winzip (for Windows) or the built-in Compress action in the Mac OS X Finder. Just make sure you compress exactly one folder that contains all the theme files and the rules.xml file. (Do not compress the contents of the folder directly: when unpacked, the zip file should produce exactly one folder which in turn contains all the relevant files).

  1. In a Python package (programmers only)

If you are creating a Python package containing Plone customisations that you intend to install into your site, you can let it register a theme for installation into the site.

To do this, place a directory called e.g. theme at the top of the package, next to the Zope configure.zcml file, and add a <plone:static /> declaration to the configure.zcml file:

<configure
    xmlns:plone="http://namespaces.plone.org/plone"
    xmlns="http://namespaces.zope.org/zope">

    ...

    <plone:static name="mytheme" directory="theme" type="theme" />

    ...

</configure>

Notice the declaration of the plone namespace at the top element. Place the theme files and the rules.xml file into the theme directory.

If your package has a GenericSetup profile, you can automatically enable the theme upon installation of this profile by adding a theme.xml file in the profiles/default directory, containing e.g.:

<theme>
    <name>mytheme</name>
    <enabled>true</enabled>
</theme>

The manifest file

It is possible to give additional information about a theme by placing a file called manifest.cfg next to the rules.xml file at the top of a theme directory.

This file may look like this:

[theme]
title = My theme
description = A test theme

As shown here, the manifest file can be used to provide a more user friendly title and a longer description for the theme, for use in the control panel. Only the [theme] header is required - all other keys are optional.

You can also set:

rules = myrules.xml

to use a different rule file name than rules.xml, and:

prefix = /some/prefix

to change the absolute path prefix (see Advanced settings).

Extensions to the Diazo theming engine can add support for additional blocks of configurable parameters.

Rules syntax

The following is a short summary of the Diazo rules syntax. See http://diazo.org for more details and further examples.

Selectors

Each rule is represented by an XML tag that operates on one or more HTML elements in the content and/or theme. The elements to operate on are indicated using attributes of the rules known as selectors.

The easiest way to select elements is to use a CSS expression selector, such as css:content="#content" or css:theme="#main .content". Any valid CSS 3 expression (including pseudo-selectors like :first-child may be used.

The standard selectors operate on the element(s) that are matched. If you want to operate on the children of the matched element instead, use css:theme- children="..." or css:content-children="..." instead.

If you cannot construct a suitable CSS 3 expression, you can use XPath expressions such as. content="/head/link" or theme="//div[@id='main']" (note the lack of a css: prefix when using XPath expressions). The two approaches are equivalent, and you can mix and match freely, but you cannot have e.g. both a css:theme and a theme attribute on a single rule.

Conditions

By default, every rule is executed, though rules that do not match any elements in the theme are ignored. You can make a rule, set of rules or theme reference (see below) conditional upon an element appearing in the content by adding an attribute to the rule like css:if-content="#some-element" (to use an XPath expression instead, drop the css: prefix). If no elements match the expression, the rule is ignored.

Tip: if a <replace /> rule matches an element in the theme but not in the content, the theme node will be dropped (replaced with nothing). If you do not want this behavior and you are unsure if the content will contain the relevant element(s), you can use css:if-content conditional rule. Since this is a common scenario, there is a shortcut: css:if-content="" means "use the expression from the css:content attribute".

Similarly, you can construct a condition based on the path of the current request by using an attribute like if-path="/news" (note that there is no css:if-path ). If the path starts with a slash, it will match from the root of the Plone site. If it ends with a slash, it will match to the end of the URL. You can set an absolute path by using a leading and a trailing slash.

Finally, you can use arbitrary XPath expressions against any defined variable using an attribute like if="$host = 'localhost'" . By default, the variables url , scheme , host and base are available, representing the current URL. Themes may define additional variables in their manifests.

Available rules

The various rule types are summarized below.

rules
<rules>
    ...
</rules>

Wraps a set of rules. Must be used as the root element of the rules file. Nested <rules /> can be used with a condition to apply a single condition to a set of rules.

When used as the root element of the rules file, the various XML namespaces must be declared:

<rules
    xmlns="http://namespaces.plone.org/diazo"
    xmlns:css="http://namespaces.plone.org/diazo/css"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    ...
</rules>
theme and notheme
<theme href="theme.html" />
<theme href="news.html" if-path="/news" />
<notheme if="$host = 'admin.example.org'" />

Choose the theme file to be used. The href is a path relative to the rules file. If multiple <theme /> elements are present, at most one may be given without a condition. The first theme with a condition that is true will be used, with the unconditional theme, if any, used as a fallback.

<notheme /> can be used to specify a condition under which no theme <should be used.notheme /> takes precedence overtheme />. **Tip:** To ensure you do not accidentally style non-Plone pages, add a condition likecss:if-condition="#visual-portal-wrapper"`` to the last theme listed, and do not have any unconditional themes.

replace
<replace
    css:content="#content"
    css:theme="#main"
    />

Replaces the matched element(s) in the theme with the matched element(s) from the content.

before and after
<before
    css:content-children="#portal-column-one"
    css:theme-children="#portlets"
    />

<after
    css:content-children="#portal-column-two"
    css:theme-children="#portlets"
    />

Inserts the matched element(s) from the content before or after the matched element(s) in the theme. By using theme-children , you can insert the matched content element(s) as the first (prepend) or last (append) element(s) inside the matched theme element(s).

drop and strip
<drop css:content=".documentByLine" />
<drop theme="/head/link" />
<drop css:theme="#content *" attributes="onclick onmouseup" />

<strip css:content="#parent-fieldname-text" />

Remove element(s) from the theme or content. Note that unlike most other rules, a <drop /> or <strip /> rule can operate on the theme or content , but not both. <drop /> removes the matched element(s) and any children, whereas <strip /> removes the matched element(s), but leaves any children in place.

<drop /> may be given a whitespace-separated list of attributes to drop. In this case, the matched element(s) themselves will not be removed. Use attributes="*" to drop all attributes.

merge and copy
<merge
    attributes="class"
    css:content="body"
    css:theme="body"
    />

<copy
    attributes="class"
    css:content="#content"
    css:theme="#main"
    />

These rules operate on attributes. <merge /> will add the contents of the named attribute(s) in the theme to the value(s) of any existing attributes with the same name(s) in the content, separated by whitespace. It is mainly used to merge CSS classes.

<copy /> will copy attributes from the matched element(s) in the content <to the matched element(s) in the theme, fully replacing any attributes with <the same name that may already be in the theme.

The attributes attribute can contain a whitespace-separated list of attributes, or the special value * to operate on all attributes of the matched theme element.

Advanced modification

Instead of selecting markup to insert into the theme from the content, you can place markup directly into the rules file, as child nodes of the relevant rule element:

<after css:theme="head">
    <style type="text/css">
        body > h1 { color: red; }
    </style>
</after>

This also works on the content, allowing you to modify it on the fly before any rules are applied:

<replace css:content="#portal-searchbox input.searchButton">
    <button type="submit">
        <img src="images/search.png" alt="Search" />
    </button>
</replace>

In addition to including static HTML in this manner, you can use XSLT instructions that operate on the content. You can even use css: selectors directly in the XSLT.:

<replace css:theme="#details">
    <dl id="details">
        <xsl:for-each css:select="table#details > tr">
            <dt><xsl:copy-of select="td[1]/text()"/></dt>
            <dd><xsl:copy-of select="td[2]/node()"/></dd>
        </xsl:for-each>
    </dl>
</replace>

Rules may operate on content that is fetched from somewhere other than the current page being rendered by Plone, by using the href attribute to specify a path of a resource relative to the root of the Plone site:

<after
    css:theme-children="#leftnav"
    css:content=".navitem"
    href="/@@extra-nav"
    />

Theme parameters

It is possible to pass arbitrary parameters to your theme, which can be referenced as variables in XPath expressions.

For example, you could have a parameter mode that could be set to the string live or test. In your rules, you could do something like this to insert a warning when you are on the test server:

<before css:theme-children="body" if="$mode = 'test'">
    <span class="warning">Warning: This is the test server</span>
</before>

You could even use the parameter value directly, e.g.:

<before css:theme-children="body">
    <span class="info">This is the <xsl:value-of select="$mode" /> server</span>
</before>

See the Diazo documentation for more details about rules that support if parameters and inline HTML and XSL.

The following parameters are always available when using plone.app.theming:

scheme

The scheme portion of the inbound URL, usually http or https.

host

The hostname in the inbound URL.

path

The path segment of the inbound URL. This will not include any virtual hosting tokens, i.e. it is the path the end user sees.

base

The Zope base url (the BASE1 request variable).

You can add additional parameters through the control panel, using TALES expressions. Parameters are listed on the Advanced tab, one per line, in the form <name> = <expression>.

For example, if you want to avoid theming any pages that are loaded by Plone's overlays, you can make use of the ajax_load request parameter that they set. Your rules file might include:

<notheme if="$ajax_load" />

To add this parameter as well as the mode parameter outlined earlier, you could add the following in the control panel:

ajax_load = python: 'ajax_load' in request.form
mode = string: test

The right hand side is a TALES expression. It must evaluate to a string, integer, float, boolean or None: lists, dicts and objects are not supported. python:, string: and path expressions work as they do in page templates.

The following variables are available:

context

The context of the current request, usually a content object.

request

The current request.

portal

The portal root object.

context_state

The @@plone_context_state view, from which you can look up additional values such as the context's URL or default view.

portal_state

The @@plone_portal_state view, form which you can look up additional values such as the navigation root URL or whether or not the current user is logged in.

See plone.app.layout for details about the @@plone_context_state and @@plone_portal_state views.

Theme parameters are really integral to a theme, and will therefore be set based on a theme's manifest when a theme is imported from a Zip file or enabled from a resource directory. This is done using the [theme:parameters] section in the manifest file. For example:

[theme]
title = My theme
description = A test theme

[theme:parameters]
ajax_load = python: 'ajax_load' in request.form
mode = string: test

Temporarily disabling the theme

Note that when Zope is in development mode (e.g. running in the foreground in a console with bin/instance fg), the theme will be re-compiled on each request. In non-development mode, it is compiled once when first accessed, and then only re-compiled the control panel values are changed.

Also, in development mode, it is possible to temporarily disable the theme by appending a query string parameter diazo.off=1. For example:

http://localhost:8080/Plone/some-page?diazo.off=1

The parameter is ignored in non-development mode.

Commonly used rules

The following recipes illustrate rules commonly used in building Plone themes:

To copy the page title:

<replace css:theme="title" css:content="title" />

To copy the <base /> tag (necessary for Plone's links to work):

<replace css:theme="base" css:content="base" />

If there is no <base /> tag in the theme, you can do:

<before css:theme-children="head" css:content="base" />

To drop all styles and JavaScript resources from the theme and copy them from Plone's portal_css tool instead:

<!-- Drop styles in the head - these are added back by including them from Plone -->
<drop theme="/html/head/link" />
<drop theme="/html/head/style" />

<!-- Pull in Plone CSS -->
<after theme-children="/html/head" content="/html/head/link | /html/head/style" />

To copy Plone's JavaScript resources:

<!-- Pull in Plone CSS -->
<after theme-children="/html/head" content="/html/head/script" />

To copy the class of the <body /> tag (necessary for certain Plone JavaScript functions and styles to work properly):

<!-- Body -->
<merge attributes="class" css:theme="body" css:content="body" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment