Wednesday, February 13, 2008

Power of Sitecore: Separation of UI and Data Layers Continued


This is a continuation to the previous blog post about one of the most powerful features of the product.

You can develop a notion of themes in Sitecore very easily. This will allow you as a Sitecore solution developer and architect to create a list of predefined themes for further usage.

Since it is a common best practice to store any meta data or system information that is not relevant to the site content itself in a global section, consider creating a special data template for your custom themes with the following fields:

Layout - any lookup field (I used treelist in this example for good demo purposes) data source is set to /sitecore/layout/layouts. Used for picking up a layout for a theme.

CSS - a field of type "lookup" pointing to a media folder storing all the CSS files.
FavIcon - a field of type "lookup" pointing to a media folder storing all the favorite icon files.

Here is how this can look like:

theme

Our second step will be to extent the content template used for the home page of the website to include a reference to a theme. The idea here is to have a single selection of a theme on the home item. After this, dynamic layout replacement logic will be able to locate the selected theme on the home item and perform layout substitution, as well as CSS and favicon processing. By simply extending the home item template, I have added a tree list field (just for the sake of good demonstration) and pointed it to the "themes" folder:

theme_selection

Next step is to create a dummy empty layout that will be used by the dynamic layout replacement logic. It is pretty simple, any aspx layout will do for that. Here is how it can look:

empty_layout

Notice that you can use it the same way as you used to - assign renderings and sublayouts to placeholders, etc. Again, the GUID of this empty dummy layout will be used the dynamic layout replacement logic which is basically another custom LayoutResolver added after the default one in the httpRequestBegin pipeline:

<processor type="Sitecore.Pipelines.HttpRequest.LayoutResolver, Sitecore.Kernel" />
<processor type="SCUSAINC.SimpleDemoSite.Customizations.Pipelines.LayoutResolver, SCUSAINC.SimpleDemoSite"/>

The logic of this processor is pretty straightforward - replace the EMPTY layout with the one defined in the selected theme.

Here you go - you now can change the overall look and feel of the website with one click! And that's thanks to the dynamic presentation engine and data/UI separation in Sitecore.

Code attached:

using Sitecore;
using Sitecore.Pipelines.HttpRequest;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Data.Fields;

namespace SCUSAINC.SimpleDemoSite.Customizations.Pipelines
{
   public class LayoutResolver
   {
      public void Process(HttpRequestArgs args)
      {
         if ((Context.Item != null) && (Sitecore.Context.Database != null))
         {
            LayoutItem layout = Context.Item.Visualization.Layout;
            // check if the layout ID equals to the "EMPTY Layout ID"
            if (layout != null && layout.InnerItem.ID == ID.Parse("{A7DF692D-AA72-4D1F-96D7-6F27B2326DDB}"))
            {
               Item homeItem = Sitecore.Context.Database.Items[Sitecore.Context.Site.StartPath];

               if (homeItem != null)
               {
                  if (homeItem.Fields["theme"] != null && homeItem["theme"] != string.Empty)
                  {
                     if (homeItem.Fields["theme"].Type == "tree list")
                     {
                        MultilistField themeField = homeItem.Fields["theme"];

                        ID themeID = ID.Null;

                        if (ID.TryParse(themeField.Items[0], out themeID))
                        {
                           Item themeItem = Sitecore.Context.Database.GetItem(themeID);

                           if (themeItem != null && themeItem.Fields["layout"] != null && themeItem["layout"] != string.Empty)
                           {
                              if (themeItem.Fields["layout"].Type == "tree list")
                              {
                                 MultilistField layoutField = themeItem.Fields["layout"];

                                 ID layoutID = ID.Null;

                                 if (ID.TryParse(layoutField.Items[0], out layoutID))
                                 {
                                    Item layoutItem = Sitecore.Context.Database.GetItem(layoutID);

                                    Context.Page.FilePath = layoutItem["path"];
                                 }
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

3 comments:

James said...

Hi,

Does this article still work/make sense with Sitecore 6? I'm brand new to the CMS and I'd like to reuse as much presentation content as I can so the theme idea appeals.

Thanks!

James

Alex Shyba said...

Hi James,

I have not tried it on version 6 yet.
The code may be a bit different due to some API changes but the same approach most likely can be used. Also I am not sure how the designer mode will play out in this picture.

Syed said...

Awesome,
I have tried this with Sitecore 6.1 and works perfect.

Solved my problems.

Thanks to Alex