Themes

If there's one thing this blogging engine should definitely support, it's themes. Chameleon's main principles are customizability and extensibility, and themes are the most fundamental way to pursue these principles. This page will discuss the options and implementation of theming. It starts with choosing a templating engine and integrating it (added in version 0.6), then adds the functionality to actually use different themes (also added in version 0.6), and finally extends the power of themes with plugins (added in version 0.7).

Architecture

This section will discuss how the templating will be set up, and how it will be integrated into the application. First, we choose a templating engine, and from there we integrate it into the application.

Templating engines

There are different ways to implement the templating into Chameleon. We could use the method of parsing templates built in into Rails, and extend it to store themes on a different location in different directories for each theme. But of course, we could also use a templating engine, which may be some existing one or one we build ourselves. Let's explore these possibilities a bit further.

Theming built in into Rails extended

This solution is quite foolish to pursue, mostly because it seems to be scalable in no way. It is a quite easy solution to implement, but the amount of bugs and security holes that may accompany this decision seems to be boundless. Therefore, this solution is immediately abandoned.

Existing templating engines

Of course, there's no reason we should build a templating engine ourselves when there are already some good options available. One of these is Liquid, a simple yet powerful templating engine, with pre-built packages for Rails (as a plugin). Liquid is used by applications like Shopify (an online application that allows you to create little online stores easily) and Mephisto (another blogging engine). One of the biggest advantages of Liquid is that it aims to be secure, and as a consequence you can allow users to change the design themselves (without risking them getting direct access to the database).

Because Liquid is already quite widely used (relatively), it is ensured that there is a minimum amount of bugs or security holes in the engine. Therefore, it seems to be appropriate to opt for this solution. Some people may also already know how to use the engine, which will make using it for our application even easier. Besides, Liquid is proven to be both an easy to use and easy to implement solution, as we can see in those other applications already using it. Some of those even allow the theme to be changed through the admin interface, without having to download and upload a file!

Our own engine

It seems to be quite pointless to implement our own templating engine, when such good engines specifically aiming at the same goal as ours are already available. When using an already existing engine, we also make conversion from other platforms using that engine more easy. This option will thus not be explored any further.

Outcome: Liquid was chosen as the engine to build upon. It will be integrated as a Rails plugin.

Integrating Liquid

Next to simply installing Liquid, some extensions to Liquid will have to be provided too. Functions to display options, links, formatted entries… in the templates need to be provided.

At first glance, they best seemed to fit in the lib directory. However, because I was not so familiar with the Rails initialization and loading process, it (incorrectly) appeared that this was not feasible, because it looked like the file was not loaded in the correct order (a problem with the dependencies). So, the next idea was to put them in a plugin "Chameleon", which was loaded directly after Liquid. Therefore, the plugin had to be named "002_chameleon", and Liquid had to be named "001_liquid", because Rails loads plug-ins in alphabetical order (this has changed in Rails 1.2).

However, after a second attempt, it did seem possible to put the files in the lib directory. Currently, all Liquid-specific files reside in lib/chameleon/liquid, and they are dynamically loaded all by the ThemingSystem (created in the next section).

Using different themes

After enabling templating, which means template files can be written and parsed in a special language, we need to combine this ability with the ability to actually use different themes. We need to know where we'll place the different themes and their templates and other specific files, and we'll have to load the theme the user selected in the Admin Center. This section discusses exactly that.

Organization

All themes will go in the themes directory directly in the root Chameleon directory. Each theme gets his own directory, with the following structure:

  • templates: A directory containing the Liquid templates for the theme.
  • images: A directory that contains the necessary images for the theme.
  • javascripts: A directory that contains the necessary JavaScript files for the theme.
  • stylesheets: A directory that contains all stylesheets for that theme.
  • theme.yml: A YAML-file containing some information about the theme, such as its name, a description…
  • screenshot.png (optionally): A screenshot of the theme, for in the Admin Center. This was not implemented.

The ID of the theme is the exact name of its directory, this means that several themes with the same name in their theme.yml can be installed as long as they are placed in a different directory.

Loading the theme

The ID of the theme the user selected is saved in the database, and this theme gets loaded upon a page request by the ThemingSystem. If the theme the user selected is not found, the application will try to fall back on the default theme, if that one isn't found either, an error message is displayed.

The ThemingSystem will expose some functions to the application, the main one being set_theme which not only sets the desired theme, but also enables the theming system. set_theme changes the template directory for BlogController (BlogController.template_root), and loads some files needed for Liquid. In ThemingSystem, there are also three new actions: stylesheets, javascripts and images, which will redirect requests for stylesheets, scripts and images for that controller to their respective directory inside the theme directory (i.e., http://www.example.com/themed-controller/stylesheets/style.css will for example render the file at themes/theme_id/stylesheets/style.css).

Managing themes

Themes can be managed through the Admin Center. To install or remove a theme, its location in themes can simply be added or deleted, as long as the theme is not used at that moment this won't cause any problems. The list of themes in the Admin Center is automatically fetched when the "Themes" page is displayed, using the theme.yml files in the different directories in themes.

Extending the power of themes

Themes will be able to include a special kind of plugin, a "theme plugin", which can set some specific configuration variables for the theme. It can for example change the amount of entries displayed on an archive page, or define the amount and the types of the entries on the homepage. A theme plugin works like a normal plugin, but is automatically enabled when a theme is selected. It allows the theme to customize the Ruby code to its needs.

This section is not finished yet.

An example of such a plugin would be:

class KubrickTheme < ThemePlugin
  VERSION = "1.0"
  entries_on_homepage 10
  entries_on_search :all
  [...]
end
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution 2.5 License.