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!

vrijdag 26 april 2013

Sitecore and MVC rendersections

Currently I'm working on a project for a customer who wants a Sitecore website based on ASP.NET MVC 4. After working on the project now for a couple of weeks I walked into some problems regarding Sitecore, MVC and RenderSections in Razor, here's what happened:

I created a standard layout in Sitecore, deriving from a Razor file which contained all HTML you normally would expect in a Razor layout file. After that I created a view rendering that derived from another Razor file, a partial file containing some extra information for the page. I actually didn't do anything to create a Sitecore content placeholder, I just referenced the view rendering by it's guid in the Sitecore layout Razor file. So far nothing special, as you would expect I had no problem showing the content from the view rendering on the layout.



Things got complicated when I tried to add a normal Razor RenderSection in the Sitecore layout Razor file, just adding this to the file triggered a yellow screen with the follow error message: "The file '/Views/Shared/_Layout.cshtml' cannot be requested directly because it calls the 'RenderSection' method". Allright, what just happened?! After some searching I found blog-posts saying I shouldn't put a Razor RenderSection on a Razor view file... Well ok, I didn't do that while I put my Razor RenderSection on a layout file, right?

No, not so right. While I've got a Sitecore layout item with a Razor view file referenced to it I actually didn't create a layout file. When you create a layout page in Sitecore and reference it to a Razor file you actually create a view page which isn't able to contain a Razor RenderSection and that's why I saw that yellow screen.

If you want to be able to include Razor RenderSections on your page you should create an actual Razor Layout file. You can do this by referencing another Razor file on top of the one you referenced in your Razor template that is included in your Sitecore layout item. In this new and 'real' Razor Layout file you can add Razor RenderSections, although there is a minor glitch when you setup your solution this way. 



Normally you would use the Razor RenderSections to place some content from your content item in a specific place in your Razor Layout page, you could for example create a Razor RenderSection for including page specific javascript at the bottom of your Razor Layout page. When you create a new page in Sitecore you basically create a content item based on a Sitecore Layout page. Your Sitecore Layout page normally would consist out of one or more renderings, which are a kind of Razor partial views.

Those renderings pose the real problem while Razor RenderSections can't be addressed from a Razor Partial view because it has no hard reference to it's Razor layout file. So if you'd like to have a RenderSection on a Sitecore rendering the default way; you're gonna have a bad time. The only way to add content to a Razor RenderSection is by declaring the content on the Sitecore content item, but that would also mean an extra dependency between the Sitecore content item and the Sitecore rendering while you'd have to manage content for one Sitecore rendering in two places.

There's no easy way to get around this problem. The best way probably is by creating two custom HTML helpers, one for accepting content to be displayed and one for rendering that content on a page. An example of the solution could be found on stackoverflow: http://stackoverflow.com/questions/5433531/using-sections-in-editor-display-templates/5433722#5433722.