Zine

open source content publishing system


Theme Development

If you want to give your Zine installation a custom look and feel themes are the way to go. Creating your own theme is a simple process, all you have to do is to create a new plugin with the template files and shared data such as JavaScript or CSS files in.

Step 1: Creating a new Plugin

The first thing is creating a new plugin. The easiest way is creating a new empty folder in the instance's plugin folder with the short name of your theme plus the "_theme" suffix (e.g. instance/plugins/my_theme/). The latter is optional but avoids name clashes with other plugins.

So if for example your theme is called "myrtle" you should create a folder called "myrtle_theme".

The next step is adding the metadata. The metadata is located in a file called metadata.txt and contains some key, value pairs delimited by colons. It should at least provide the Name, Author, License and Summary of the theme:

Name: Myrtle Theme
Author: Armin Ronacher <armin.ronacher@active-4.com>
License: GPL
Summary: A brownish theme called "myrtle".

Step 2: Creating the Driver Code

Plugins can provide more than just a theme. So the first thing we have to do is to add some code that provides a theme. If you have a theme and you want to provide multiple colorschemes you can also add more than one theme to a plugin, the process is the same.

All the code that controls the plugin instantiation goes into a file called __init__.py inside the plugin folder. The minimal code you have to provide looks like this:

from os.path import join, dirname

TEMPLATE_FILES = join(dirname(__file__), 'templates')
SHARED_FILES = join(dirname(__file__), 'shared')

def setup(app, plugin):
    app.add_theme('<theme_name>', TEMPLATE_FILES, plugin.metadata)
    app.add_shared_exports('<theme_name>_theme', SHARED_FILES)

Don't forget to replace <theme_name> with the name of your theme! Now what this code does is pretty simple. Whenever Zine loads the plugin it calls the setup function. From there we add a new theme to the application and tell it the location of the template files (they are located in the templates folder in your plugin directory) and give it the metadata of the theme. Because a plugin can ship more than just one theme we could provide a dictionary with the metadata here, but because here we have just one theme we just pass it the plugin metadata.

The next step is optional but probably required for many themes. It adds a new shared export location to Zine. If you for example want to serve some CSS or JavaScript files you can add a shared folder and add a shared export. Shared exports are available at /_shared/<plugin_name>/<filename>.

To get a link to such an exported file you can use the url_for function but we will cover that later.

Step 3: Overriding Templates

If we would enable our plugin right away it would look exactly like the default theme. That's because if Zine cannot find a template in the template path of the theme it falls back to the default templates. Most themes will just use CSS and the layout template so let's start with that.

All templates inherit from the layout.html template. So if we create such a file in our templates directory we can add our own content and CSS files to the layout template. A simple layout template could look like this:

<!DOCTYPE HTML>
<html>
<head>
  <title>{% block title %}{% endblock %} &mdash; {{ cfg.blog_title|e }}</title>
  <link rel="stylesheet" type="text/css" href="{{
    shared_url('<plugin_name>::style.css') }}">
  {{ get_page_metadata() }}
</head>
<body>
  <div class="header">
    <h1>{{ cfg.blog_title|e }}</h1>
    <p class="description">{{ cfg.blog_tagline }}</p>
  </div>
  {% block contents %}{% endblock %}
</body>
</html>

The templates are normal Jinja templates and the two blocks you have to provide are called contents and title. Because some plugins and even Zine itself need to add some page metadata to all files in order to work correctly, you *have* to call get_page_metadata somewhere in the head section. The cfg object allows you to access the configuration. So if you don't want to hardcode the blogname in the template you can do {{ cfg.blog_title|e }} (the |e filter escapes the title).

Because we want to use our own CSS file for style informations we add a link tag that points to the style.css file of our plugin. (Don't forget to replace <plugin_name> with the name of your plugin!)

Step 4: Adding CSS Files

So Zine will now look for your CSS file called style.css inside the shared folder we provided in the driver code.

So just add a shared/style.css file and add your content there. Also check out already existing themes for inspiration.

Step 5: Adding Widgets

Zine inherits some terms and implementation details from WordPress. One of the concepts WordPress provides are widgets. For Zine widgets are just objects that are both accessible with the attribute operator and renderable by just using the widget in an expression. That means that you can do this with a widget:

{% set widget = get_tag_cloud() %}
{% for tag in widget.tags %}
  ...
{% endfor %}

Or to render the whole cloud just:

{{ widget }}

There is also a function render_widgets() which just renders the contents of the _widgets.html template. If your template has a sidebar somewhere just use render_widgets() there and put your widget calls into a _widgets.html template. This allows users to override the widgets in the administration interface.

Step 6: Adding a Preview Image

If you want to share your theme with the Zine community it's a good idea to add a preview image to the theme. The rules for such a preview image are simple: It must be a valid image with the dimensions 240x170 pixels. Save your preview image in the shared folder as preview.png and add a new line to the metadata.txt:

Preview: <plugin_name>::preview.png

And once again replace <plugin_name> with the name of your plugin.

Step 7: Load your plugin

Touch (i.e. the unix command touch) the zine.wsgi file in your apache instance in get the new theme to appear in the plugins list.