home | products | contact

Python and open source software consulting

Croutons

Croutons is a presentation templating library for HTML. Croutons allows look and feel to be reused without any tedious mucking about with server-side includes or equivalents.

Croutons makes all this as easy as possible without the need to modify existing pages or to create specially coded templates.

Contents

Introduction

What is croutons?

Croutons is a presentation templating library for HTML, written in Python. Croutons allows HTML pages' look-and-feel to be reused without any tedious mucking about with server-side includes or equivalent technology.

Croutons provides mechanisms for:

Croutons makes this as easy as possible without the need to modify any existing pages or to create specially coded templates or include files.

Croutons can be installed as an apache handler so that HTML files containing croutons markup can be processed automatically by the server. Croutons also contains a WSGI middleware component so that it may be combined with other server-side applications.

Why "croutons"?

Croutons is based on Beautiful Soup, a library for parsing HTML. Croutons allows you to sprinkle new content into your tag soup – hence the name.

Licensing

Croutons is available under a BSD Licence. The croutons software distribution contains Beautiful Soup. Beautiful Soup is licensed under the same terms as Python itself.

Download

Download croutons v0.2.2

Older versions

Download croutons v0.2.0

Download croutons v0.1.1

Download croutons v0.1

Installation

Pre-requisites

Installation procedure

Webserver configuration

To configure the Apache web server to display crouton pages, use the following steps. If you do not use Apache, you will have to find the equivalent instructions for your web server

User guide

Using croutons with a web server

Let's start with an example. For this we need two files: one ordinary static HTML page and a croutons template page. For the purposes of this example, we're going to create a new and rather simplistic HTML page, although you could equally well use an existing page of your choice.

Creating the static page

We will use this as the template, providing a look and feel that we can reuse in our croutons page later on. In your website's document root, create a new HTML page with the following content, and save it with the filename template.html:

<html>
    <head>
        <title>A static html page</title>
        <style type="text/css">
            body { font-family: sans-serif; }
            #Header { background-color: #eee; }
            #Footer { background-color: #eee; }
            #Content { padding: 2em; }
        </style>
    </head>

    <body>
    <div id="Header">
        <h1>This is the header section</h1>
    </div>
    <div id="Content">
        <h1>This is the main content area.</h1>
        <p>
        The content in this area will change from page to page, while the
        header and footer should remain static.
        </p>
    </div>
    <div id="Footer">
        <h1>This is the footer section</h1>
    </div>
    </body>
</html>
    

Creating the croutons page

We can now create a croutons page based on the template.html page you created above. The aim will be to reuse the style, header and footer but to replace the content and page title with new content. Create a second file in the same directory, newpage.crouton, with the following content:

<html crouton:based-on="virtual('template.html')">
    <head>
        <title crouton:replace="title">A croutons page</title>
    </head>

    <body>
    <div crouton:replace="#Content">
        <h1>This is the main content area, but this time with croutons.</h1>
        <p>
        The "crouton:replace" attribute above means the contents of the div
        with id "Content" in the original template will be replaced by
        whatever's put here.
        </p>
    </div>
    </body>
</html>
    

If you now view newpage.crouton in a web browser, you should see that the header, footer and style have been pulled from the original template page, but the page title and content area have been replaced. Here's a tag-by-tag explanation of what's going on:

<html crouton:based-on="virtual('template.crouton')">

The "crouton:based-on" attribute instructs the crouton parser to fetch the content from the file 'template.crouton' and use this as the basis for the page being rendered. The contents of the crouton:based-on attribute may be any valid python expression, and the functions path, virtual and url have been provided to fetch content from any file path, virtual path (ie relative to the website's documentroot) and url respectively.

<title crouton:replace="title">A croutons page</title>

The "crouton:replace" attribute must always contain a CSS2 selector expression. This attribute instructs the crouton parser to locate the element from the template page identified by the CSS selector expression, with the contents of the element in the element. In this case the upshot is that the content of the <title> tag is replaced by "A croutons page".

Reference

Croutons attributes

crouton:based-on="expr"

expr is parsed and evaluated as a python expression. The resulting value (which must be either a Crouton object or a BeautifulSoup object, see the API documentation for details) will be used to replace the element containing the crouton:based-on attribute in its entirety.

Examples
<!-- Base page on another HTML page, referenced by file path.  -->
<html crouton:based-on="path('/home/oliver/www/index.html')">
<html crouton:based-on="path('../index.html')">

<!-- Base page on another HTML page, referenced by virtual paths.  -->
<html crouton:based-on="virtual('/index.html')">

<!-- Base page on another HTML page, referenced by url. -->
<html crouton:based-on="url('http://www.example.org/index.html')">

<!-- Base part of a page on another -->
<html crouton:based-on="virtual('/index.html')">
    <div crouton:replace="#photo">
        <img crouton:based-on="virtual('/products/sprocket.html').select('table#productinfo img')"/>
    ></div>
</html>

            
Functions

Although expr may be any valid python expression, three functions are exposed that allow content to be loaded from a variety of sources:

path('filesystem_path')

The path function loads content from any filesystem path. This may be specified relatively or absolutely, and is not restricted to being within the document root.

virtual('virtual_path')

The virtual function loads content from any virtual path (a path relative to the website's document root). This may be specified relatively or absolutely, and is restricted to being within the document root.

url('url')

The url function loads content from any valid and accessible URL.

The result of the path, virtual or url functions will be python object with a select method that may be used to limit the portion of the returned document by specifying a CSS-2 selector, for example: crouton:based-on="virtual('/products/sprocket.html').select('table#productinfo img')".

crouton:rewrite-links=""

Any tag containing a crouton:based-on attribute may also contain a crouton:rewrite-links attribute. This attribute, if present, instructs the crouton parser to search for links within the based-on code (images, linked stylesheets, anchors etc) and rewrite the targets of those links as absolute URLs. The links will then work if the crouton page is in a different directory or server from the original.

Examples
<!--
  Base this page on http://www.example.org/index.html, rewriting links. 
  For example, a link to "./photo.jpg" in the original page would become
  "http://www.example.org/photo.jpg" when used in this page.
-->
<html crouton:based-on="url('http://www.example.org/index.html')" crouton:rewrite-links="">
            
crouton:replace-inner="expr"
crouton:replace="expr"

crouton:replace is an alias for crouton:replace-inner.
expr must be a CSS-2 selector expression, which will select elements in the based-on document. The first matching element found will have its contents replaced with the contents of the element containing the crouton:replace attribute. Unlike replace-outer, only the contents of the selected tag is replaced.

Examples

Given a document soup.html, with the following content:

<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Mushroom</li>
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
        

The following results may be had:

Crouton document
<html crouton:based-on="path('soup.html')">
  <li crouton:replace="ul#Flavours li">Vegetable</li>
</html>
            
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Vegetable</li>
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
            
Crouton document
<html crouton:based-on="path('soup.html')">
  <li crouton:replace="ul#Flavours li+li">Cream of asparagus</li>
</html>
            
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Mushroom</li>
        <li>Cream of asparagus</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
            
crouton:replace-outer="expr"

expr must be a CSS-2 selector expression, which will select elements in the based-on document. The first matching element found will be replaced by the element containing the crouton:replace-outerattribute. Unlike replace-inner, the replacement includes the containing tags.

For example

Crouton document
<html crouton:based-on="path('soup.html')">
  <li crouton:replace-outer="ul#Flavours li">Vegetable</li>
</html>
            
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Vegetable</li> <!-- note that entire <li> is replaced – not just the contents -->
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
            
crouton:append="expr"

expr must be a CSS-2 selector expression, which will select elements in the based-on document. The first matching element found will have the contents of the element containing the crouton:append attribute appended to its contents.

Using the soup menu example above:

Crouton document
<html crouton:based-on="path('soup.html')">
  <ul crouton:append="ul#Flavours"><li>Broccoli and stilton</li></ul>
</html>
        
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Mushroom</li>
        <li>Tomato</li>
        <li>Minestrone</li>
        <li>Broccoli and stilton</li>
    </ul>
</body>
</html>
        
crouton:prepend="expr"

expr must be CSS-2 selector expression, which will select elements in the based-on document. The first matching element found will have the contents of the element containing the crouton:replace attribute prepended.

Using the soup menu example above:

Crouton document
<html crouton:based-on="path('soup.html')">
  <ul crouton:prepend="ul#Flavours"><li>Broccoli and stilton</li></ul>
</html>
            
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Broccoli and stilton</li>
        <li>Mushroom</li>
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
            
crouton:insert-before="expr"

expr must be a CSS-2 selector expression, which will select elements in the based-on document. The element containing the crouton:insert-before attribute will be inserted, in its entirety, directly before the first element that matches expr.

For example:

Crouton document
<html crouton:based-on="path('soup.html')">
  <img crouton:insert-before="ul#Flavours" src="soup.png" />
</html>
        
Result
<html>
<body>
    <h1>Soup menu</h1>
    <img src="soup.png" />
    <ul id="Flavours">
        <li>Mushroom</li>
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
        
crouton:insert-after="expr"

expr must be a CSS-2 selector expression, which will select elements in the based-on document. The element containing the crouton:insert-before attribute will be inserted, in its entirety, directly after the first element that matches expr.

For example:

Crouton document
<html crouton:based-on="path('soup.html')">
  <img crouton:insert-after="ul#Flavours" src="soup.png" />
</html>
        
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Mushroom</li>
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
    <img src="soup.png" />
</body>
</html>
        
crouton:remove="expr"

expr must be a CSS-2 selector expression, which will select elements in the based-on document. The first matching element found will be removed from the based-on document. Nothing will be inserted.

Using the soup menu example above:

Crouton document
<html crouton:based-on="path('soup.html')">
  <span crouton:remove="ul#Flavours li"/>
</html>
        
Result
<html>
<body>
    <h1>Soup menu</h1>
    <ul id="Flavours">
        <li>Mushroom</li>
        <li>Tomato</li>
        <li>Minestrone</li>
    </ul>
</body>
</html>
        

Known issues and support

Work is still ongoing to support the full CSS-2 selector syntax as described in the CSS2 specification.

If you find croutons useful or have a question about it, please email the author, oliver@thelettero.co.uk.