August 14, 2011

A look at the new fabrik theme system

Well here’s the preview:

The theme configuration page in fabrik

And here’s the technical part:

When developing fabrik I set out to create a more flexible theme engine for both theme developers (I want to make my life as easy as possible) and for our users. One of the first things I did was to separate themes and layouts.

In fabrik, a theme defines the overall formatting of the site and the layout page (header, menu, footer). It can also override some of the default pages in the site, perhaps adding additional markup or functionality.

Layouts are templates for specific pages in the site. So in fabrik we have layouts for the home page, project list page, project detail page, post list page and post detail page. Layouts do contain some CSS, but this is purely for “layout” purposed. A layout may also register some scripts to set up client side plugins like galleries or light boxes.

Originally, I decided that a theme and a layout would be two independent things. A theme developer could opt in to use the layout system but did not have to style all the layouts if he didn’t want to.

However, after launching the beta and starting working on some new fabrik themes I realized that this relationship didn’t work too well. A theme developer may wish to create new layouts. We couldn’t just include them in the existing layout selector as the layout wouldn’t exist for other themes. In addition to this, the all or nothing approach to supporting layouts wasn’t great.

The thing we were missing here was some sort of theme definition that allows the theme developer to provide information about his theme such as configurable attributes and layouts. The fabrik user can then configure the theme to their liking, thus creating a theme configuration.

Another nice feature I added was the ability for the theme developer to supply pre-sets for any configurable attributes. This would allow them to set up different color schemes, or combinations of options that they thought worked well together.

I believe this approach offers maximum flexibility. The theme developer can choose what to make configurable and in the case of the default themes (like the one in the above screenshot) the end user can tweak it to their liking.

Implementation

I’m not going to post the code as this feature is still very much in development but I thought I would give an overview of how the system is built.

As you can imagine, perhaps the most difficult was building the interface. Essentially we are creating a generic form, with any number of inputs, of varying data types, all of which needs to be persisted to storage.

Fortunately asp.net mvc helps us out quite a bit here. One of the built in template helpers EditorFor, inspects the data type of the passed model and renders the appropriate form element. This is what will render text as an input[type=text] and a boolean value as an input[type=checkbox]. This helper also supports passing of template hints and additional values to be consumed by the chosen template. It’s these features that have allowed us to render color pickers or select lists.

If you’ve made use of Data Annotations before then you’ll be aware that this sort of thing is easily achievable by decorating your model with attributes. Unfortunately in this case, we don’t have a specific model class, we’re effectively binding to a list of key value pairs, or specifically a Dictionary<string, object>. I did start working on a ModelMetaDataProvider that can work in a similar way to the default data annotations based provider, but it’s not ready yet.

When we bind our form, we need to make sure that the theme attribute values are the correct type. The default theme configuration values and the user configuration values are actually persisted as string. However, the theme configuration defines the CLR type for each attribute so we are able to convert it when we need to.

For example:

<themeAttribute 
    name="BackgroundColor" 
    dataType="System.String"
    defaultValue="ffffff" 
    displayName="Background Color" 
    description="The background color for your site."
/>

Interestingly, when we post the form back to our controller, we need to actually post back string values. If you try and post back an object dictionary the default model binder does all kind of crazy things.

Once we have the string values for each attribute, we validate them against the original definition and then persist them to storage.

The theme configuration is available from the front end (public) application. We wrapped the configuration in a dynamic dictionary so that we can access attributes like so:

body {
    background: #@Theme.Attributes.BackgroundColor;
    color: #@Theme.Attributes.PrimaryColor;
    font-family: @Theme.Attributes.PrimaryFont;
}

As you can see, we are using these attributes directly in our Css, which is effectively an asp.net mvc view, produced by a controller action.

There’s still plenty of work to do on this before it’s production ready so once done, I’ll blog in more detail about the implementation.

© 2020 Ben Foster