Skip to content

Developer's Starter Guide

Audience

The intended audience of this guide are first time contributors who are trying to understand the Open Library codebase.

Prerequisites

Before starting this guide, it is recommended that you:

  1. Clone or fork and pull an up-to-date version of the Open Library repository
  2. Successfully launch your local environment using the docker guide
  3. Complete the git cheat sheet tutorial
  4. Find a good first issue to work on

Now you're ready to understand how the code is structured within Open Library.

Approach

This guide follows a "working backwards" approach, starting from what visitors see on the Open Library website and then exploring the sequence of actions (the "lifecycle") that takes place to serve the HTML.

The Anatomy of the Site

HTML Templates

When a patron navigates the Open Library website and requests pages (like the screenshot pictured below), the patron is served "rendered" HTML content from the openlibrary/templates directory.

Screenshot 2024-09-04 at 6 12 47 AM

Here are a few examples of common templates:

NOTE

Some pages, like the /books page, are first-class "registered" Infogami types and are powered through the Infogami wiki framework that runs Open Library. This is true for books, authors, lists, and a few other types. Because these are first-class registered types handled by infogami, You won't find plugin controllers for them in Open Library. Their html templates can be found in the special templates/type directory. When Open Library notices a url containing a key for one of these special types, infogami fetches the corresponding object from the database and passes it as page straight into the template. So for the books page, which is templates/type/edition/view.html, the page value defined in its $def with (page, ...) header is actually a book edition or work object whose properties are defined here.

Most HTML pages within the templates folder do not include the <html>, <head>, and <footer> sections of the website—only the body content. Open Library automatically wraps nearly all page templates with the site template.

The Site Wrapper

While the body of each Open Library page may present differently, most pages share similar scaffolding, look, and feel. For instance, nearly every page will have:

  1. A top black bar topNotice with an Internet Archive logo:
    Screenshot 2024-09-04 at 6 26 30 AM
  2. A header header#header-bar section with the Open Library logo, a search box, a hamburger menu, and an account dropper for logged in patrons:
    Screenshot 2024-09-04 at 6 27 23 AM
  3. A footer menu:
    Screenshot 2024-09-04 at 6 27 53 AM

The top-level html page that is used to define and render the overall structure of every Open Library page is templates/site.html. In its definition, this main site template calls out to other modular, specific "sub-templates" (such as the 3 described above) defined within the templates/site and templates/lib directories, which combine to form the "site". You can read more about this "layout template" philosophy in the official web.py documentation, the micro web framework used by Open Library.

The variable section of the site that changes depending on the requested page is the body. This page is an html template that gets passed in to the site. You may wish to review examples in the Page Templates section.

How a Page Gets Rendered

To serve a rendered template to a visitor, use the render_template() function. The first argument is the name of the page-specific HTML template to render and inject into the site body. The remaining parameters pass variables from Python into the corresponding HTML template file, as defined in the template's header.

For instance, imagine we have a simple template called templates/book.html which requires a book_title, a bookcover_url, and an author_name. Such a template might look like:

py
$def with (book_title, bookcover_url, author=None)

<div>
  <h1>$book_title</h1>
  <img src="$bookcover_url"/>
  $if author:
    <h2>$author</h2>
</div>

The $def with(...) line is how the template declares what variables it needs to be passed in when it is being called and rendered. Elsewhere in the template, the $ symbol acts as an instruction that a variable should be replaced by its value when it's being rendered.

Check the official web.py templator documentation if you'd like a deep dive into all the capabilities of Open Library's python-powered templating system.

CAUTION

The $: combination instructs the template to render this variable as html, as opposed to plaintext. This is sometimes necessary when rendering one html template from another, but should be done with care when rendering a variable whose value may be user-specified because this may result in dangerous XSS attacks, where a bad actor may intentionally save javascript code into a variable, hoping that the website will render and execute this code as html as opposed to safe plaintext.

NOTE

Notice that author in this example is provided as a keyword argument with a default value of None and thus is optional when the template is being called. Similar to when defining a python function, once a keyword argument is defined, all following variables after it must also then be keyword arguments.

From python, the corresponding code to render this templates/books.html template would be:

py
render_template(
  'books',  # name of the template in templates/ directory without .html
  book_title="The Hobbit",
  bookcover_url="https://covers.openlibrary.org/b/id/14624642-L.jpg",
  author="J.R.R. Tolkien"
)

TIP

When we call render_template, it knows to look in the templates/ directory and so we don't need to specify this. Also, we don't need to include the .html extension.

When we call render_template('book', ...) we are fetching the templates/book.html template and passing in the values book_title, bookcover_url, and author from python into the template's $def with (...) section, where it can be substituted as needed into the $variables in the template. The render_template takes this prepared book template and then passes it into site.html where it will be forwarded to the body to be rendered.

Managing Large Templates

When a template becomes too large, or when common logic can be factored out, templates can be broken into smaller logical components and moved into separate micro-templates. These micro-templates can live in the templates/ directory. If a micro-template is self-contained and functions as a standalone widget (such as QueryCarousel), consider saving it in the macros directory—a folder of templates with additional special properties.

Macros

When refactoring, you may decide to move code to the macros directory. A macro is a special type of template that can be accessed using syntax by the page editor in Open Library. For more information about macros, see the very stale/outdated infogami documentation.

Here's what the infogami edit UI looks like for a collection: Screenshot 2024-09-04 at 8 31 02 AM

Here's what the page looks like when it's rendered with a macro: Screenshot 2024-09-04 at 8 31 28 AM

Next: Understanding controllers & routers

Based on this guide, you should now be able to:

  1. Find the corresponding HTML template file for an Open Library webpage in the templates/ directory
  2. Make changes to the site wrapper (for example, modifying the header or footer)
  3. Add a new template and render it... But from where?

The next step is learning where to add render_template(...) code and how to connect a URL that a patron visits with logic to respond to the request.

To answer this question, you'll want to proceed to the Plugins Guide.