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:
- Using existing pages as templates for new pages
- Including content from existing pages into new pages
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
Older versions
Installation
Pre-requisites
- Python 2.4 or higher is required
- The Apache web server is recommended for developing websites using croutons
Installation procedure
- Download the latest version of croutons, using the link above.
- Run the setup.py installer script. From the command line, type:
% python setup.py installNote that you may need to have administrator or root access to your system for this command to complete successfully.
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
- Edit your httpd.conf, and insert the following lines:
# Enable croutons support AddHandler cgi-script .cgi ScriptAlias /crouton-handler /path/to/crouton.cgi Action crouton-page /crouton-handler AddHandler crouton-page .croutonThe ScriptAlias directive will need to change depending on your system configuration. "/path/to/crouton-cgi.py" will usually be
/usr/local/bin/crouton-cgi.pyor/usr/bin/crouton-cgi.pyon unix-like systems. On other systems examine the output of thepython setup.py installcommand to find where the script has been installed.You can also add or change the file extensions associated with crouton files by modifying the AddHandler line.
If you have installed mod_fastcgi, you can substitute the following configuration for improved performance.
# Enable croutons support AddHandler fastcgi-script .fcgi ScriptAlias /crouton-handler /path/to/crouton.fcgi Action crouton-page /crouton-handler AddHandler crouton-page .crouton
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,virtualandurlhave 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
Croutonobject or aBeautifulSoupobject, see the API documentation for details) will be used to replace the element containing thecrouton:based-onattribute 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
pathfunction 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
virtualfunction 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
urlfunction loads content from any valid and accessible URL.
The result of the
path,virtualorurlfunctions will be python object with aselectmethod 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-onattribute may also contain acrouton:rewrite-linksattribute. 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:replaceis an alias forcrouton: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 thecrouton:replaceattribute. Unlikereplace-outer, only the contents of the selected tag is replaced. - 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. Unlikereplace-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:appendattribute 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:replaceattribute 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-beforeattribute 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-beforeattribute 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>
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>
Known issues and support
Only a subset of the full CSS-2 selector syntax is supported. Otherwise the library is known to be stable on older versions of python (2.3), there are no plans to update it and no support is offered.