It looks like you're offline.
Open Library logo
additional options menu
title Infogami Developer Tutorial
body <u><i>This document is not maintained. Last updated December 2008.</i></u> <p>The Open Library interface is powered by infogami -- a cleaner, simpler alternative to other wiki applications. But unlike other wikis, Infogami has the flexibility to handle different classes of data. Most wikis let you store unstructured pages -- big blocks of text. Infogami lets you store structured data. <a name="origin_footnote1">&nbsp;</a><a href="#footnote1" class="footnote">[1]</a> </p> <p>In addition to this, infogami facilitates the creation of dynamic HTML templates and macros. This flexible environment enables users to create, share and build collaborative interfaces. With Open Library in particular, we are focused on building a productive and vital community focused on the discovery of books.</p> <p>In this document, you'll learn how to develop for infogami, including building new templates for displaying your own data, running your own copy, and developing new features and plugins.</p> <p>To discuss this document, join the <a href="http://mail.archive.org/cgi-bin/mailman/listinfo/ol-discuss">General Discussion</a> mailing list.</p> <p><a name="toc">&nbsp;</a> </p><div id="toc-header" style="clear: both;"><b>Table of Contents</b></div> <table border="0" cellpadding="8" cellspacing="0" width="100%"> <tbody><tr><td valign="top" width="50%"> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tbody><tr> <td class="toc" colspan="3" width="100%"><b><a href="#summary">Summary (Audience Statement)</a></b></td> </tr><tr> <td class="toc" colspan="3" width="100%"><b><a href="#intro">Introduction</a></b></td> </tr><tr> <td class="toc" colspan="3"><b>Architecture</b></td> </tr><tr> <td class="toc-number"><a href="#anchor1">1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Web Server</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-number"><a href="#anchor2">2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Templates</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor2.1">2.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">web.py</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor2.2">2.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">infogami</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor2.3">2.3</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">OL application</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-number"><a href="#anchor3">3</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Search</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-number"><a href="#anchor4">4</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Data</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc" colspan="3"><b>Template Basics</b></td> </tr><tr> <td class="toc-number"><a href="#anchor5">5.</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Types</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor5.1">5.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Primitive Types</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor5.2">5.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Compound Types</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-number"><a href="#anchor6">6.</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Templates</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor6.1">6.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Type-specific Templates</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor6.2">6.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Default Templates</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor6.3">6.3</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Custom Templates</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor6.4">6.4</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Useful Functions</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-number"><a href="#anchor7">7.</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Macros</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor7.1">7.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Best Practices</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor7.2">7.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Embedding</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> </tr></tbody></table> <p></p></td><td valign="top" width="50%"> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tbody><tr> <td class="toc" colspan="3"><b>WikiLanguage</b></td> </tr><tr> <td class="toc-number"><a href="#anchor8">8</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Formatting Conventions</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor8.1">8.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Page Headers</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor8.2">8.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Format Commands</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor8.3">8.3</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Working with Types</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-number"><a href="#anchor9">9</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Template Syntax</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor9.1">9.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Conditionals</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor9.2">9.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Loops and Iterations</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor9.3">9.3</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Regular Expressions</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc" colspan="3"><b>The Infogami API</b></td> </tr><tr> <td class="toc-number"><a href="#anchor10">10.</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Plugins</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor10.1">10.1</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Special Pages and Modes</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor10.2">10.2</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Actions</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor10.3">10.3</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Classes</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor10.4">10.4</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Decorators</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> </tr><tr> <td class="toc-link-end"><a href="#anchor10.5">10.5</a>&nbsp;&nbsp;</td> <td class="toc" width="100%">Security Considerations</td> <td class="toc-link-end" align="center">&nbsp;&nbsp;</td> <!-- </tr><tr> <td class="toc" colspan="3"><a href="#anchor11"><b>Acknowledgements</b></a></td> --> </tr><tr> <td class="toc" colspan="3"><a href="#anchor12"><b>Authors</b></a></td> </tr><tr> <td class="toc" colspan="3"><a href="#anchor13"><b>References</b></a></td> </tr><tr> <td class="toc" colspan="3"><a href="#anchor14"><b>Template and Plugin Examples</b></a></td> </tr><tr> <td class="toc" colspan="3"><a href="#anchor16"><b>Code Repository</b></a></td> </tr><tr> <td class="toc" colspan="3"><a href="#anchor17"><b>Installing the Software</b></a></td> <!-- </tr><tr> <td class="toc" colspan="3"><a href="#anchorA"><b>A.</b></a>&nbsp;&nbsp;<b>Differences From Python</b></td> --> </tr><tr> <td class="toc" colspan="3"><a href="#anchor18"><b>Troubleshooting</b></a></td> </tr></tbody></table> <p></p></td></tr></tbody></table> <p><a name="summary">&nbsp;</a></p><h2>Summary (Audience Statement)</h2> <p> This document describes the internal workings of the Open Library software, from a developers' point of view. You should read it if you are:</p> <p><b>1)</b> a software developer wanting to add new features to Open Library. To do this, you will also have to be a good Python programmer experienced in writing web server applications (not necessarily in Python). The document will explain the Open Library's software architecture and its internal interfaces, and will explain how to write extensions (plugins), but the sections about plugin writing will assume that you are familiar with Python and web programming in general.</p> If you do not yet know Python, you should first study the [Python documentation](http://www.python.org/doc/) or the free book [Dive into Python](http://diveintopython.org). Python is an easy language to learn, but the OL codebase is probably not understandable by complete beginners. For web server principles, the somewhat dated [Philip and Alex's Guide to Web Publishing](http://philip.greenspun.com/panda) is still an informative read, though maybe someone can suggest something newer. You should also understand the principles of software security -- see [David A Wheeler's page](http://www.dwheeler.com) for many documents.</p> <p><b>2)</b> A user or web designer wanting to improve or customize the Open Library's user interface, either for yourself or for our whole community. You will mainly want to study the section about template programming. You will need to know how to write HTML and it will help if you've done some server-side template programming (such as in PHP). It will also help if you've had some exposure to Python, but the programming skills you'll need for template writing are generally less intense than they'd be for extension writing.<p> <p><b>3)</b> A general user just wanting to know how the software works behind the scenes. You might not understand all the details, but reading the doc should give you a general impression of how sites like this are put together.</p> <p><b>4)</b> A librarian or metadata maintainer wanting to process large volumes of metadata for import into the Open Library. If you only want to import a few books, it's probably easiest to use the web interface (or the Z39.50 interface once we have one). To import bulk data, you'll have to process it into a format that Open Library can understand, which may require programming, but you can use your own choices of languages and platforms for that purpose since you only have to create uploadable files, rather than use language-specific interfaces. You'll mainly want to look at the section about data formats and schemas. <p>If you just want to be an OL user accessing or editing book data, you do NOT need to read this doc. The doc is about how to customize and extend the software, not how to use it. As developers and designers, our goal is to make the site self-explanatory for users and not need much separate documentation, but we do have some user docs at <a href="http://www.openlibrary.org/help">/help</a>. </p> <p><a name="intro">&nbsp;</a></p><h2>Introduction</h2> <p>Infogami is a wiki application framework built on <a href="http://webpy.org/">web.py</a>. Actual applications (like <a href="http://www.openlibrary.org/">Open Library</a>) are written by extending Infogami through two layers: plugins and templates. Plugins are <a href="http://python.org">Python</a> modules that get loaded into Infogami through a special API. They are invoked by submitting HTTP requests to the application, either HTML form posts or direct GET requests. Plugins can use any library or application code that they wish, and they create Python objects to represent results, that then get expanded to HTML by templates. Templates are a mixture of HTML text and user-written code, approximately in the spirit of PHP templates<!--, but a bit less powerful -->. The user-written code is in a special-purpose scripting language that is approximately a Python subset, which runs in a hopefully-secure server-side interpreter (embedded in the Python app) that has limited access to system functions and resources.</p> <p><a name="basics">&nbsp;</a></p><h2>&nbsp;&nbsp;Architecture</h2> <p><a name="anchor1">&nbsp;</a><h3><a href="#toc" class="toc-link">1.</a>&nbsp;&nbsp;Web Server</h3> <p>The <a href="http://lighttpd.net/">lighttpd</a> HTTP server runs <a href="http://infogami.org/">Infogami</a> through a FastCGI interface using <a href="http://trac.saddi.com/flup">Flup</a>. (There can be multiple concurrent infogami instances that the lighttpd server distributes requests between, although Open Library currently just runs one.) Infogami is written in <a href="http://python.org/">Python</a> (Open Library currently requires 2.5 or greater) and uses <a href="http://webpy.org/">web.py</a> and <a href="http://www.openlibrary.org/about/tech"><em>ThingDB</em></a>. ThingDB uses <a href="http://postgresql.org/">PostgreSQL</a> as its data store. <a href="http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo">Psycopg2</a> is the Python driver for PostgreSQL. Open Library uses <a href="http://cr.yp.to/daemontools/supervise.html">supervise</a> (see also <a href="http://cr.yp.to/daemontools.html">daemontools</a>) to make sure everything keeps running. <a name="origin_footnote2"></a><a href="#footnote2" class="footnote">[2]</a> </p> <p><a name="anchor2">&nbsp;</a><h3><a href="#toc" class="toc-link">2.</a>&nbsp;&nbsp;Templates</h3> <p>The infogami application relies on various <em>Web templates</em> (these are code+HTML snippets). The initial templates are static files but they get edited through the wiki interface, and new ones get added through the wiki, so the real versions live entirely in the database. </p> <p><a name="anchor2.1">&nbsp;</a><h4><a href="#toc" class="toc-link">2.1</a>&nbsp;&nbsp;web.py</h4> <p>The goal of web.py is to build the ideal way to make web apps. web.py allows you to build HTTP responses, makes the database easier to use, and creates a templating evironment that tries to bring Python into HTML. [<a href="http://groups.google.com/group/webpy/msg/f266701d97e7ceb1">*</a>]</p> <p><a name="anchor2.2">&nbsp;</a><h4><a href="#toc" class="toc-link">2.2</a>&nbsp;&nbsp;infogami</h4> <p>Each infogami page (i.e. something with a URL) has an associated type. Each type contains a schema that states what fields can be used with it and what format those fields are in. Those are used to generate view and edit templates which can then be further customized as a particular type requires. </p> <p><a name="anchor2.3">&nbsp;</a><h4><a href="#toc" class="toc-link">2.3</a>&nbsp;&nbsp;OL application</h4> <p> <i>[ See <a href="http://www.openlibrary.org/dev/docs/ui">State of the UI</a> ]</i> ... <a name="origin_footnote3">&nbsp;</a><a href="#footnote3" class="footnote">[3]</a> </p> <p><a name="anchor3">&nbsp;</a><h3><a href="#toc" class="toc-link">3.</a>&nbsp;&nbsp;Search</h3> <p>Infogami also accepts plug-ins and Open Library uses one for the <a href="http://lucene.apache.org/solr/">Solr</a> search engine. Solr is a JSP currently sitting in a <a href="http://www.mortbay.org/">Jetty</a> http server, so it communicates with Infogami through a local http socket. Solr itself wraps the <a href="http://lucene.apache.org/">Lucene</a> search library. These run under <a href="http://java.sun.com/">Java</a> (Open Library currently uses Java 1.5). Solr is built under <a href="http://ant.apache.org/">Apache Ant</a> and has a few config and schema files, plus a startup script (solr.sh) that has to be manually edited to set the port number. I think we currently use Lucene as a downloaded .jar file so we don't build it. </p> <p>The solr-infogami plugin also calls out to <em>an archive.org PHP script</em> that expands basic search queries to advanced queries. It may also start using <em>the openlibrary.org flipbook</em> (with some possible customizations) to display OCA scans for pages containing fulltext search results. </p> <p><a name="anchor4">&nbsp;</a><h3><a href="#toc" class="toc-link">4.</a>&nbsp;&nbsp;Data</h3> <p>Open Library has a bunch of catalog data and fulltext acquired from various sources, either sitting in the Archive or to be uploaded to there. I think the acquisition processes (including web crawling scripts for some of the data) is outside the scope of an Open Library software install. There are a bunch of additional scripts to make the stuff usable in openlibrary and these need to be documented. These include <em>TDB Conversion Scripts</em> written by dbg, and (for OCA fulltext) <em>Archive Spidering</em> and <em>Solr Importing</em> scripts written by phr. </p> <p><a name="basics">&nbsp;</a></p><h2>&nbsp;&nbsp;Template Basics</h2> <p><a name="anchor5">&nbsp;</a><h3><a href="#toc" class="toc-link">5.</a>&nbsp;&nbsp;Types</h3> <p>There are two kinds of types in infogami. <a href="#anchor5.1">Primitive types</a> and <a href="#anchor5.2">compound types.</a> Primitive types are for representing integers, strings etc.</p> <p>Other primitive and compound types can be added by creating a page in the wiki with type type/type. </p> <p>Sometimes you might want to link two different types. For example, book type has an author property and you want to add a books field to the author, which contains all books where author property of the book is this author. This is achieved by adding a backreference to the author type. </p> <p><a name="anchor5.1">&nbsp;</a><h4><a href="#toc" class="toc-link">5.1</a>&nbsp;&nbsp;Primitive Types</h4> <p>Available primitive types are:</p> <p> <b>type/int</b><br /> <b>type/boolean</b><br /> <b>type/string</b> - <i>for single line text</i><br /> <b>type/text</b> - <i>for multiline text</i><br /> </p> <p><a name="anchor5.2">&nbsp;</a><h4><a href="#toc" class="toc-link">5.2</a>&nbsp;&nbsp;Compound Types</h4> <p>Compound types specify what properties a thing of that type must have. Available compound types are:</p> <p><b>type/type</b><br /> <i>Type for representing all types. It defines the following properties ... </i> </p> <ul> <li>description of type type/text - description of type </li> <li>is_multiple of type type/boolean - to distinguish between primitive and compound types</li> <li>properties of type type/property - set of properties a thing of this type can have. This property can have multiple values.</li> <li>backreferenes of type type/backreference - set of backreferences a thing of this type can have. This property can have multiple values. Back-references are explained later in this document.</li> </ul> <p><b>type/property</b><br /> <i>Type for representing properties in a type. It defines the following properties ...</i> </p> <ul> <li>name of type type/string - name of the property</li> <li>type of type type/type - expected type of the value</li> <li>is_unique of type type/boolean - can this type have multiple values?</li> <li>description of type type/string - readable description of this property</li> </ul> <p><b>type/backreference</b><br /> <i>later ...</i> </p> <p><b>type/page</b><br /> <i>Type for wiki pages. It defines the following properties ...</i> </p> <ul> <li>title of type type/string - title of the page</li> <li>body of type type/text - body of the page</li> </ul> <p><b>type/template</b><br /> <i>Type for template pages. It defines the following properties ...</i> </p> <ul> <li>title of type type/string - title of the template</li> <li>body of type type/text - body of the template</li> </ul> <p><a name="anchor6">&nbsp;</a><h3><a href="#toc" class="toc-link">6.</a>&nbsp;&nbsp;Templates</h3> <p>Infogami uses templates to control the look and feel of the site. Custom templates can be provided in the wiki to enhance functionality or customize the look and feel of the site.</p> <p>There are two kinds of templates. Regular templates and type-specific templates. Type-specific templates are used to render a wiki page based on its type.</p> <p>Regular templates must be placed at path <a href="http://www.openlibrary.org/templates/">templates/</a> and end with .tmpl extension. type-specific templates must be placed at <a href="http://www.openlibrary.org/type/">type/</a> and end with .tmpl extension.</p> <p>With Open Library, only site administrators can modify the templates that reside in the <a href="http://www.openlibrary.org/templates/">templates/</a> and <a href="http://www.openlibrary.org/type/">type/</a> directories. If users want to override these templates, they must create their own set of templates in their user directory, and change their template root in their user preferences. For more information, see <a href="#anchor6.3">Custom Templates.</a> <p><a name="anchor6.1">&nbsp;</a><h4><a href="#toc" class="toc-link">6.1</a>&nbsp;&nbsp;Type-specific Templates</h4> <p>There can be 4 type-specific templates for every type. </p> <ul> <li><b>view.tmpl</b> - Template to specify how to view a page</li> <li><b>edit.tmpl</b> - Template to specify how to edit a page</li> <li><b>repr.tmpl</b> - Template to specify how objects of this type appear when viewing pages of other types which has a property of this type.</li> <li><b>input.tmpl</b> - Template to specify how objects of this type appear when editing pages of other types which has a property of this type.</li> </ul> <p>For more information on how the repr and input templates are used, please see <a href="#anchor3.4">Useful Functions.</a></p> <p><a name="anchor6.2">&nbsp;</a><h4><a href="#toc" class="toc-link">6.2</a>&nbsp;&nbsp;Default Templates</h4> <p>It is not compulsory to specify all these templates for every type. When a type-specific template is not specified for a type, a default template is used. The following are the default templates: </p> <ul> <li><b>templates/default_view.tmpl</b> - <a href="http://www.openlibrary.org:8080/templates/default_view.tmpl">View Code</a> or <a href="http://www.openlibrary.org:8080/type/page">View Page Using Template</a></li> <li><b>templates/default_edit.tmpl</b> - <a href="http://www.openlibrary.org:8080/templates/default_edit.tmpl">View Code</a> or <a href="http://www.openlibrary.org:8080/type/page?m=edit">View Page Using Template</a></li> <li><b>templates/default_repr.tmpl</b> - <a href="http://www.openlibrary.org:8080/templates/default_repr.tmpl">View Code</a> </li> <li><b>templates/default_input.tmpl</b> - <a href="http://www.openlibrary.org:8080/templates/default_input.tmpl">View Code</a> </li> <li><i>Note to Anand: what about http://www.openlibrary.org:8080/templates/default_ref.tmpl ?</i></li> </ul> <p>There are many other interesting regular templates. Some of them are: </p> <ul> <li><b>templates/site.tmpl</b> - to specify the overall look and feel of a web site - <a href="http://www.openlibrary.org:8080/templates/site.tmpl">View Code</a> or <a href="http://www.openlibrary.org:8080/">View Page Using Template</a></li> <li><b>templates/diff.tmpl</b> - to display a diff between two versions of a page - <a href="http://www.openlibrary.org:8080/templates/diff.tmpl">View Code</a> or <a href="http://www.openlibrary.org:8080/dev/docs?b=13&a=12&m=diff">View Page Using Template</a></li> <li>...</li> </ul> <p><a name="anchor6.3">&nbsp;</a><h4><a href="#toc" class="toc-link">6.3</a>&nbsp;&nbsp;Custom Templates</h4> <a href="http://www.openlibrary.org/preferences/"><img src="http://invisible.net/openlibrary/ui_files//login.png" align="right" border="0" /></a> <p>A user can create their own set of templates by creating a subdirectory in the 'user' directory ('user/[username]'). A user can create a new template for each set of templates they want to override. If a template does not exist in a user's subdirectory, then the default set of templates will be used. Templates are created in the same manner as any new page is created in a wiki. <!-- Please note that the default templates that exist in the 'templates' directory are only editable by Open Library site administrators. --></p> <p>For purposes of illustration, we will view the contents of ('user/brewster/*') <ul> <li><a href="http://www.openlibrary.org/user/brewster/type">/user/brewster/type/</a></li> <li><a href="http://www.openlibrary.org/user/brewster/templates">/user/brewster/templates/</a></li> </ul> <p>To view the Open Library using a set of custom templates, you must change your template preferences: </p> <ul> <li>Log in with your username and password</li> <li>Click on <a href="http://www.openlibrary.org/preferences">"Preferences"</a> in the upper-right corner of the screen</li> <li>Change your template root to "/user/[username]"</li> </ul> <p><i>NOTE: Changing the template root to 'user/brewster' will load the templates used <a href="http://invisible.net/openlibrary/ui_files//brewster.skin.png?keepThis=true&amp;TB_iframe=true&amp;height=599&amp;width=800" title="User-defined Template Set (click on image to return to document)" class="thickbox">in this example.</a></i></p> <p>It is also possible to create custom Cascading Style Sheet (CSS) files and JavaScript files in the wiki. <p>To create a custom CSS file in the wiki, create a page in your user directory entitled 'user/[username]/[filename].css' and select 'rawtext' for the pagetype. Once the CSS file has been successfully created, it can be linked to the template using the '$add_stylesheet' command:</p> <div class="blue"> <code class="normal"> $add_stylesheet('/static/css/search.css') </code></div> <p>JavaScript files can be created in the same manner, using a '.js' extension and the '$add_javascript' command:</p> <div class="blue"> <code class="normal"> $add_javascript('/static/js/randomsearch.js') </code></div> <p>It is important to note that when a page with the type 'rawtext' is added to the wiki, the editing function is not readily apparent. To edit a rawtext page, simply add '?m=edit' to the url.</p> <p><i>Example:</i> <ul> <li><a href="http://www.openlibrary.org/user/anand/master.css">http://www.openlibrary.org/user/anand/master.css</a></li> <li><a href="http://www.openlibrary.org/user/anand/master.css?m=edit">http://www.openlibrary.org/user/anand/master.css?m=edit</a></li> </ul> <p><a name="anchor6.4"></a><h4><a href="#toc" class="toc-link">6.4</a>&nbsp;&nbsp;Useful Functions</h4> <p>There are some useful functions, which are very handy in writing view and edit templates. </p> <ul> <li><b>thingrepr(value, type) - </b>This calls repr template of the specified type with value as argument.</li> <li><b>thinginput(type, name, value) - </b>This calls input template of the specified type with value, name as arguments.</li> </ul> <p>Lets take an example. Suppose there are many types, which need to display an image. Probably they will do that by defining a type type/image for representing images. Instead of worrying how to display and edit (upload) images, these types can just use <code>thingrepr</code> and <code>thinginput</code> macros and define <code>repr</code> and <code>input</code> templates for <code>type/image</code>. Later in future, if you want to change how to display/upload image, you don't have to change in multiple places.</p> <p>There are some global functions, which are accessible by every template. For more information see <a href="#anchor10.4">Infogami API &gt; Decorators</a>. </p> <p><a name="anchor7">&nbsp;</a><h3><a href="#toc" class="toc-link">7.</a>&nbsp;&nbsp;Macros</h3> <p>Macros are like functions in any programming language. Macros look very much like templates, but they can be called from wikipages and other templates and macros. Macros can be written in the wiki just like templates. Macros must be placed in <a href="http://www.openlibrary.org/macros/">macros/</a> path and type must be type/macro. As a convention, they are named in <code>CamelCase</code>. </p> <p><a name="anchor7.1">&nbsp;</a><h4><a href="#toc" class="toc-link">7.1</a>&nbsp;&nbsp;Best Practices</h4> <p>Macros introduce an element of modularity when designing web templates in infogami. A common practice is to use macros for any repeatable design elements. This can be as simple as an HTML callout box, or a script. For instance, the "SAVE", "PREVIEW", and "DELETE" buttons on every edit form on the Open Library site are generated by the "EditButtons" Macro code below: <div id="form-border"> <div id="metadata-body"> {{EditButtons()}} <br /><br /> <div class="blue"> <code class="normal"> &lt;p&gt;&lt;strong&gt;Edit summary&lt;/strong&gt; (briefly describe the changes you have made):<br /> &lt;input type="text" name="_comment" style="width: 100%" value="" /&gt;<br /> &lt;/p&gt;<br /> &lt;p&gt;<br /> &lt;button class="button" type="submit" value="$_.SAVE" name="_save" title="Save"&gt;&lt;img src="/static/images/save.png" alt="Save" /&gt;&lt;/button&gt;<br /> &lt;button class="button" type="submit" value="$_.PREVIEW" name="_preview" title="Preview"&gt;&lt;img src="/static/images/preview.png" alt="Preview" /&gt;&lt;/button&gt;<br /> &lt;button class="button" type="submit" value="$_.DELETE" name="_delete" title="Delete"&gt;&lt;img src="/static/images/delete.png" alt="delete" /&gt;&lt;/button&gt;<br /> </p> <br /></code></div> </div> </div> <p>Intelligent use of macros results in clean, readable code that can be easily understood and repurposed by other users. For instance, the <a href="http://www.openlibrary.org/type/author/view.tmpl">author view</a> template is a good example of a template with a highly modular and streamlined design. <a href="http://www.openlibrary.org:8080/a/Mark_Twain">View Page Using Template</a> <p><a name="anchor7.2">&nbsp;</a><h4><a href="#toc" class="toc-link">7.2</a>&nbsp;&nbsp;Embedding</h4> <p>Macros can be invoked either through the wiki interface when editing a page in Markdown, or as a script when building web templates. <p>The syntax to call a macro from a wiki page is: </p> <div class="blue"> <code class="normal">{{HelloWorld()}} or {{MyMacro(&quot;arg1&quot;, &quot;arg2&quot;)}}<br /> </code></div><p>The syntax to call a macro from templates and macros is: </p> <div class="blue"> <code class="normal">$:macros.HelloWorld() and $:macros.MyMacro(&quot;arg1&quot;, &quot;arg2&quot;)<br /> </code></div> <p><a name="wiki">&nbsp;</a></p><h2>&nbsp;&nbsp;WikiLanguage</h2> <p><a name="anchor8">&nbsp;</a></p><h3><a href="#toc" class="toc-link">8</a>&nbsp;&nbsp;Formatting Conventions</h3> <p><a name="anchor8.1">&nbsp;</a></p><h4><a href="#toc" class="toc-link">8.1</a>&nbsp;&nbsp;Page Headers</h4> <p>The edit and view templates are defined by two lines of code that must be at the top of the template file in order for them to function properly (The absence of either of these two lines will generate an error). For instance, this example is from the default edition template:</p> <div class="blue"> <code class="normal"> $def with (page)<br /> <br /> $var title: $page.title </code></div> <p>Additional variables can be controlled in these commands. For instance, in an edit template, the preview function can be disabled as follows:</p> <div class="blue"> <code class="normal"> $def with (page, preview=False ) </code></div> <p>Display of the &lt;TITLE&gt; of the page can be constructed by making a call to a universal language file. For instance:</p> <div class="blue"> <code class="normal"> $var title: $_.EDIT $page.title </code></div> <p>would display "edit Tom Sawyer" in the edit view of an edition page by accessing the verbiage in the language file for '$_.EDIT' (edit) and the 'title' key in the 'edition' type [ See <a href="http://www.openlibrary.org/type/edition/">http://www.openlibrary.org/type/edition/</a>]. <p>and:</p> <div class="blue"> <code class="normal"> $var title: $_.VIEW $page.d.get('name') </code></div> <p>would display "View Mark Twain" when viewing an author page by accessing the 'name' attribute in the 'author' type [See <a href="http://www.openlibrary.org/type/author/">http://www.openlibrary.org/type/author/</a>]. <p><a name="anchor8.2">&nbsp;</a><h4><a href="#toc" class="toc-link">8.2</a>&nbsp;&nbsp;Format Commands</h4> <p><a name="anchor8.3">&nbsp;</a><h4><a href="#toc" class="toc-link">8.3</a>&nbsp;&nbsp;Working with Types</h4> <p>The page 'get' statements make a call to the document type to pull the corresponding information for display on the page. For instance the following table illustrates our current author type:</p> <div id="form-border"> <div id="metadata-body"> <table cellpadding="0" cellspacing="0" border="0" width="100%"> <tbody> <tr> <td class="historyheader"><img src="/static/images/name.schema.png" alt="Name" border="0" /></td> <td class="historyheader"><img src="/static/images/type.schema.png" alt="Type" border="0" /></td> </tr> <tr> <td class="history">description</td> <td class="history"> </td> </tr> <tr> <td class="history">is_primitive</td> <td class="history"> </td> </tr> <tr> <td class="history">properties</td> <td class="history">website of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">bio of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">photograph of type <a href="/type/image">type/image</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">name of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">death_date of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">alt_names of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">location of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">properties</td> <td class="history">birth_date of type <a href="/type/string">type/string</a> </td> </tr> <tr> <td class="history">backreferences</td> <td class="history">Back-reference to authors property of <a href="/type/edition">type/edition</a> </td> </tr> </tbody> </table> </div> </div> <p>Therefore using this command:</p> <div class="blue"> <code class="normal"> $page.d.get('name') </code></div> <p>on the view template will display the author name wherever used on an author template.</p> <p><a name="anchor9">&nbsp;</a><h3><a href="#toc" class="toc-link">9</a>&nbsp;&nbsp;Template Syntax</h3> <p><a name="anchor9.1">&nbsp;</a><h4><a href="#toc" class="toc-link">9.1</a>&nbsp;&nbsp;Conditionals</h4> <p>'If' statements are used in much the same manner as in other scripting languages, invoking an action if a certain state exists. For instance, if page preview is activated in an edit template as follows:</p> <div class="blue"> <code class="normal"> if $def with (page, preview=True ), </code></div> <p>the following command would format the HTML contents of the page wherever it is inserted in the edit template:</p> <div class="blue"> <code class="normal"> $if preview:<br /> <br /> &lt;p&gt;$:format(page.body)&lt;/p&gt; </code></div> <p>The 'if' statement can also be used in conjunction with the 'get' statement to hide empty data fields on a page. For instance:</p> <div class="blue"> <code class="normal"> $if page.d.get('language'): <br /><b>Language:</b> $page.language </code></div> <p>would display the language a book was published in if that data is available for that book, or:</p> <div class="blue"> <code class="normal"> $if page.d.get('birth_date'): $page.d.get('birth_date')&nbsp;-&nbsp;<br /> $if page.d.get('death_date'): $page.d.get('death_date') </code></div> <p>would display the date of birth and date of death for a given author on the author view template if they exist.</p> <p><a name="anchor9.2">&nbsp;</a><h4><a href="#toc" class="toc-link">9.2</a>&nbsp;&nbsp;Loops and Iterations</h4> <p><a name="anchor9.3">&nbsp;</a><h4><a href="#toc" class="toc-link">9.3</a>&nbsp;&nbsp;Regular Expressions</h4> <p><a name="api">&nbsp;</a></p><h2>&nbsp;&nbsp;The Infogami API</h2> <p><a name="anchor10">&nbsp;</a><h3><a href="#toc" class="toc-link">10.</a>&nbsp;&nbsp;Plugins</h3> <p>Functionality of infogami can be extended by adding new plugins to it. Plugin in infogami is a python package with the following directory structure. </p> <ul> <li><b>foo/</b></li> <li><b>foo/__init__.py</b> - required to make foo a python package</li> <li><b>foo/code.py</b> - this is imported when infogami starts</li> <li><b>foo/templates/</b> - place to keep templates. This is optional.</li> </ul> <p>see <code>sample_run.py</code> in infogami repository to find out how to add a plugin to infogami. </p> <p><a name="anchor10.1">&nbsp;</a><h4><a href="#toc" class="toc-link">10.1</a>&nbsp;&nbsp;Special Pages and Modes</h4> <p>A plugin can add special pages to infogami and add new modes to wiki pages. Special pages are paths which are treated differently than regular wiki pages. login, logout, sitepreferences etc. are some examples of special pages. Modes are actions that be done on a wiki page. view, edit, history, diff etc. are some examples of wiki modes. </p> <p><code>infogami.utils.delegate</code> module provides two special classes <code>page</code> and <code>mode</code>. Any class that extends from <code>page</code> becomes a special page with path same as the class name and any class that extends from <code>mode</code> becomes a wiki mode with name of the mode same as the class name. </p> An example: <div class="blue"> <pre><code class="normal"> # hello/code.py class hellopage(delegate.page) def GET(self, site) return &quot;hello page!&quot class hello(delegate.mode) def GET(self, site, path) return &quot;hello mode for &quot; + path </code></pre></div> If this `hello` plugin is added to infogami, `/hellopage` page displays `hello page!` and `/foo?m=hello` displays `hello mode for foo`. <p><a name="anchor10.2">&nbsp;</a><h4><a href="#toc" class="toc-link">10.2</a>&nbsp;&nbsp;Actions</h4> <p>A plugin can also add new <code>actions</code> to the system. Action is a utility function, which can be invoked from the command line as shown in the following example. </p> <div class="blue"> <code class="normal">$ python run.py action_name arg1 arg2<br /> <br /> $ python run.py startserver 9000<br /> $ python run.py help<br /> $ python run.py movetemplates<br /> </code></div><p>The default action is <code>startserver</code> and that is invoked when no action is specified. </p> <p>To convert a function in to an action, it must decorated with <code>infogami.action</code> decorator. The following is an example of action. </p> <div class="blue"> <code class="normal">@infogami.action<br /> def hello(name=&quot;world&quot;):<br /> print &quot;hello, &quot; + name + &quot;!&quot;<br /> </code></div><p>Once this plugin is added to infogami, this action can be used. </p> <div class="blue"> <code class="normal">$ python run.py hello<br /> hello, world!<br /> $ python run.py infogami<br /> hello, infogami!<br /> </code></div><p>The <code>help</code> action prints the list of all available actions. </p> <p>There are some important actions, that every plugin author must know. </p> <p><strong>install</strong>: The <code>install</code> action runs all the install hooks. Install hook is a function without any arguments decorated by <code>infogami.install_hook</code>. Any plugin can add a new install hook to do some actions to initialize an infogami instance. Some of the actions are install hooks too. </p> <p><strong>movefiles</strong>: The <code>movefiles</code> action moves all the files in <code>$plugin/files/</code> from every plugin to <code>static/</code> directory. This is also an install hook. If a plugin needs to access any static resources, they must be put in <code>files/</code> directory of that plugin. Keeping resources directly in <code>static/</code> is not a good idea and should be discouraged. </p> <p><strong>movetemplates</strong>: The <code>movetemplates</code> action moves templates from every plugin to wiki. Optional prefix can be specified to limit the templates that are moved into the wiki. This is also an install hook. </p> <p><a name="anchor10.3">&nbsp;</a><h4><a href="#toc" class="toc-link">10.3</a>&nbsp;&nbsp;Classes</h4> <p><a name="anchor10.4">&nbsp;</a><h4><a href="#toc" class="toc-link">10.4</a>&nbsp;&nbsp;Decorators</h4> <p>To make a function accessible in templates, that function must be decorated with <code>infogami.utils.view.public</code> decorator. Since templates can be written by untrusted users, utmost care must be taken when exposing a function in templates. </p> <p><a name="anchor10.5">&nbsp;</a><h4><a href="#toc" class="toc-link">10.5</a>&nbsp;&nbsp;Security Considerations</h4> <p>The basic flow is: the user submits a url or HTML form; the FCGI handler matches the URL patch against a series of regexps to dispatch the request to the proper plugin; the plugin receives the GET or POST data and creates results as Python objects (e.g. a list of strings or ints); the plugin then loads a template (designated in the plugin code) which receives the result objects as arguments. The template code then expands the result to HTML which gets presented to the user's browser. Thus, a user can "re-skin" the application by modifying the static HTML or the script code in the templates that he or she uses. A few initial templates (such as the application's top-level HTML page) are included in the application source tree, and the rest are created and edited through the wiki interface.</p> <p>Plugins are written by the application developers and are part of the source code tree for the application. Templates are entered and updated through the wiki HTTP interface, are stored in the database just like other wiki pages, and can be written by arbitary users. Thus, templates are potentially hostile and therefore require security precautions during execution. Templates give a flexible means of creating presentation HTML, but for security reasons, operations requiring access to system functions must be implemented in plugins, not templates. Templates can be application-wide or can be specific to a given user account.</p> <!-- <p><a name="anchor11">&nbsp;</a></p><h2>&nbsp;&nbsp;Acknowledgements</h2> --? <p><a name="anchor12">&nbsp;</a></p><h2>&nbsp;&nbsp;Authors</h2> <p><a href="http://www.openlibrary.org/user/aaronsw/">aaronsw</a>, <a href="http://www.openlibrary.org/user/anand/">anand</a>, <a href="http://www.openlibrary.org/user/phr/">solrize</a> and <a href="http://www.openlibrary.org/user/webchick/">webchick</a>.</p> <p><a name="anchor13">&nbsp;</a></p><h2>&nbsp;&nbsp;References</h2> <table border="0" width="99%"> <tbody> <tr><td class="author-text" valign="top"><b><a name="footnote1">&nbsp;</a><a href="#origin_footnote1" class="footnote">[1]</a></b></td> <td class="author-text">"<a href="http://www.openlibrary.org/about/tech">About the technology</a>", last edited <a href="http://www.openlibrary.org/about/tech?m=history">August 2007.</a><br> <span class="hyperlink">http://www.openlibrary.org/about/tech</span></td> </tr> <tr><td class="author-text" valign="top"><b><a name="footnote2">&nbsp;</a><a href="#origin_footnote2" class="footnote">[2]</a></b></td> <td class="author-text">"<a href="http://www.openlibrary.org/about/architecture">About the architecture</a>", last edited <a href="http://www.openlibrary.org/about/architecture?m=history">August 2007.</a><br> <span class="hyperlink">http://www.openlibrary.org/about/architecture</span></td> </tr> <tr><td class="author-text" valign="top"><b><a name="footnote3">&nbsp;</a><a href="#origin_footnote3" class="footnote">[3]</a></b></td> <td class="author-text">"<a href="http://www.openlibrary.org/dev/docs/ui/">State of the UI</a>", last edited <a href="http://www.openlibrary.org/dev/docs/ui?m=history">September 2007.</a><br> <span class="hyperlink">http://www.openlibrary.org/dev/docs/ui/</span></td> </tr> <tr><td class="author-text" valign="top"><b><a name="footnote4">&nbsp;</a><a href="#origin_footnote4" class="footnote">[4]</a></b></td> <td class="author-text">"<a href="http://infogami.org/dev/quicklook">Quick Look at Infogami</a>", last edited <a href="http://infogami.org/dev/quicklook?m=history">October 2007.</a><br> <span class="hyperlink">http://infogami.org/dev/quicklook</span></td> </tr> </tbody></table> <p><a name="anchor14">&nbsp;</a></p><h2>&nbsp;&nbsp;Template and Plugin Examples</h2> <p>The following examples illustrate what is possible with the Infogami template language.</p> <p><img src="http://invisible.net/openlibrary/ui_files//arrow.png" alt="Disclosure Arrow"> <a href="http://www.openlibrary.org/static/docs/results.png?keepThis=true&amp;TB_iframe=true&amp;height=599&amp;width=800" title="Faceted Search example (click on image to return to document)" class="thickbox">Search Template</a><br /> <img src="http://invisible.net/openlibrary/ui_files//arrow.png" alt="Disclosure Arrow"> <a href=".png?keepThis=true&amp;TB_iframe=true&amp;height=599&amp;width=800" title="Price Check (click on image to return to document)" class="thickbox">Price Check</a> <br /> <img src="http://invisible.net/openlibrary/ui_files//arrow.png" alt="Disclosure Arrow"> <a href=".png?keepThis=true&amp;TB_iframe=true&amp;height=599&amp;width=800" title="Price Check (click on image to return to document)" class="thickbox">Data Visualization</a> </p> <p><a name="anchor16">&nbsp;</a></p><h2>&nbsp;&nbsp;Code Repository</h2> <p>The infogami code repository can be found <a href="http://infogami.org/hg/">here.</a></p> <p><a name="anchor17">&nbsp;</a></p><h2>&nbsp;&nbsp;Installing the Software</h2> <p>Installation instructions can be found <a href="http://www.openlibrary.org/dev/docs/setup">here.</a></p> <!-- <p><a name="anchorA">&nbsp;</a></p><h2>&nbsp;&nbsp;Differences From Python</h2> <p><font color="green"><i>[ phr to write ]</i></font></p> --> <p><a name="anchor18">&nbsp;</a></p><h2>&nbsp;&nbsp;Troubleshooting</h2> <p>Q. I can't change my page type when I create a new page in the wiki using my new template set. Whenever I change the page type (to 'edition' instead of 'page', for instance), it reverts back to the original type. </p> <p>A. Make sure your edit template contains the box for 'page type'. It is possible that it was removed when the form was designed. The absence of this box means that the template loses the type information whenever it is saved. </p> <p>Q. I am trying to use custom graphics using the type for my particular page template set, however it generates this error: error in processing template: AttributeError: 'str' object has no attribute 'png' (falling back to default template). What gives? </p> <p>A. Aha. You have to say ${key}.png because otherwise it thinks you want the png field of the key variable (as in $page.doi). </p> <p>Q. Why doesn't the HTML formatting I enter in my edit template show up correctly on my view template? I added carraige returns and page breaks in all the appropriate places, but it runs together in one long paragraph. </p> <p>A. You need to use the '$:format' command to support HTML formatting. For instance, to properly format the appearance of a biography on the author view page you would need to add the following the script in the appropriate place on the page: </p> <p>$:format(page.bio) </p> <p>Q. My HTML code for any images I add via a page edit form displays on the view page. For example, instead of a photo of Dave Eggers, I see &quot;&lt;img src=&quot;http://ia350629.us.archive.org/3/items/ol-images/daveeggers.gif&quot; align=&quot;left&quot;&gt;&quot; </p> <p>A. Again, you need to use: </p> <p>$:format(page.bio) instead of $page.bio </p> <p>if you want to support HTML formatting. </p> <p>Q. Help! My edit template for [this page] is royally hosed. I was trying to do something fancy with [form layout | javascript | etc. ] and broke the edit template so badly that I can't get back in and correct it. </p> <p>A. Use the emergency override to fall back to a very simplified template version and see if you can make your corrections there: </p> <div class="blue"> <code class="normal">[url]?m=edit&amp;rescue=true </code></div> <p><br><br><br> </p>