maandag 10 juni 2013

Theming in Sitecore MVC

On a recent project I was asked to create some kind of theming depending on the URL in Sitecore. The customer wanted to be able to add a themed logo, a custom color and possibly some more customization's. He also wanted to be able to add, edit and delete these themes on the fly.

At first I started to think about using Sitecore layout items for this purpose. In theory that could be quit a nice way to render different themes. I would create a custom layout template that derived from the standard layout template. After that I would add some custom fields to the custom layout template to save the theme color and the theme logo. So far, so good. Everything works nice and easy and in theory you should be able to swap layout files at runtime.

That sounded like a good idea, but it's actually quit impossible to use field values from a layout template on a content item. So much for this cool theming theory, I had to take a different approach.

If we want to use editable fields on a page we probably can't work without fields on a content item. The problem with this approach is that I don't want extra fields on my content items. Heck, I don't want to have to change my theming on all content items every time something changes.

Sitecore.Context.Items to the rescue! This array is normally empty and can be used to store additional content items for usage on your webpage. This is how I've set up my solution using the Items array:

The customer didn't know how the theming should be presented in the URL. At first they chose a solution in which the theme name is part of the relative URL, something like www.mysite.com/{themename}/restofpath/sitecorewildcard. After some discussions they changed it over to a URL that specified the theme name as the sub-domain like {themename}.mysite.com/path/sitecorewildcard. In the end I ended up with a version that could theoretically work in both ways, with minor changes necessary to switch solutions. I implemented the sub-domain solution.

I created an item-resolver which was inserted into the pipeline right after the custom Sitecore item-resolver. This item-resolver checks whether the Sitecore solution contains an item with the same name as the theme name we got from the URL. It does so by trying to get a Sitecore Item from a custom Sitecore repository for unit testing purposes. The GetItem method does nothing more than getting a Content Item based on the specified path. This method also dictates that you name the Content Item the way you specify it as a sub-domain. If there is an item in the CMS I put it in Context.Items array for use on out webpages, as shown in the screenshot below:

Itemresolver implementation.


When you look at this picture, you see that I call a UrlPartsHelper class to get the current domain. The method I'm calling is the one below, which I in turn got from a the internet from a website that I no longer recall...

Url helper class


I added the www because that's not one of my preferred theme's. When I wouldn't add this check the solution wouldn't return strange values for an www theme as long as I don't add an www theme to the Brand-folder.

One final thing needs to be done before we can see the final version working on our website. You need to hook up the item fields on a webpage. You can do this by creating your own custom HTML helper and call it for all fields on your page. I created a HTML helper that expects a fieldId as a parameter. In the method I check for the Theme Item to exist in the Context.Items collection and when it's there, I check for a field with the id that I passed as a parameter. If it exists I return the required output.

One final note: to ensure more robust code I would advise for creating string constants which hold the various content item paths, names and id's. This way your template looks more readable while you can manage your item constants from code.

Cheers!