A different road to Sitecore 8 Upgrade – The final chapter: Presentation!

This post rounds out the series with presentation magic! Following my hypothetical 😉 theme, the next challenge is to somehow translate the presentation from webforms to MVC. Along with moving content, we would also have to set the presentation of these items.

I know that in my hypothetical situation, I have made it a bit more difficult by not only upgrading to Sitecore 8 but also moving from webforms to MVC. Life would be easier if I just stayed with webforms!!! Nope!

I am glad in this situation I decided to go with Sitecore MVC. Was it the right decision, absolutely! Was it easy, absolutely not!

With the final solution it makes it feasible to have an infrastructure which forces re-usability and test-ability. No complaints about that.

Now back to presentation. The first thing you need to do is to keep a tally of the sublayouts to views translations in a spreadsheet. There might be cases where you have consolidate multiple sublayouts into one view or the other way round. You might also have a need for one sublayout to be replaced by multiple views.

In my case, I went with this simple assumption: For a specific kind of presentable item, the standard values presentation on that template should match the functionality in the webforms solution and the MVC solution. Both the standard values presentation (in webforms and MVC) might not match in terms of the number of presentation items but they will match in what they render.

Once that simple rule is set in stone (well for the most part, there will always be exceptions, right?), the rest should be achievable by scripts.

I am not an MVC guru by any means and I feel that in technology its not what you know its how you learn it!. I pride myself in picking up technology as I work, as do a lot of Sitecore folks I know!

For scripting I always use Sitecore PowerShell. I love using PowerShell commandlets and rolling out libraries in solutions to get complicated tasks done and have a repeatable process.

In this scenario we are going to do the following:

  1. Traverse through the current content tree ( you define what to go through, but typically be sitecore\content\home)
    1. For each item we look to see if there is presentation on the standard values, if so we save it
    2. We do a diff of the standard values and the item in questions and store the diff
    3. At the end of this process, we should have an items.xml (diff’ed presentation for each item) and a standardvalues.xml
  2. Take these XML definitions and transfer them to the new site
  3. Build an xml file called presentationdefinitions.xml which has the translation from sublayouts to views
  4. At this point the assumption is that you have set the standard values on the new Sitecore 8 instance, so that template items have base UI
  5. Traverse through the new content tree (this would be the same since we are importing content package from current Sitecore 6.5+)
    1. While traversing the tree which should be the same as the current tree, we reset presentation
    2. Look up the item in the items.xml from the current content site, find the translated presentation
    3. Set the current item with the diff’ed and translated (sublayouts to views) presentation items
  6. Publish and enjoy!

Current PowerShell Commandlet in Sitecore 6.5+
The commandlet which I have in the Sitecore 6.5+ solution shown below. The main purpose is to catalog standard values, use the standard values to get diff’s on item presentation and also keep tabs of unique sublayouts used across the current 6.5+ website.

    [Cmdlet("Store", "Presentation")]
    public class PresentationCommandlet : BaseCommand
    {
        [Parameter]
        public string PassID { get; set; }

        XDocument docStandardValues = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("templates"));
        XDocument docItems = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("items"));
        XDocument docUniqueRenderings = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("renderings"));

        protected override void ProcessRecord()
        {
            //get the master database
            Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase("master");
            var selectItem = master.GetItem(new ID(PassID));
            if (selectItem != null)
            {
                LoopForChildren(selectItem);
            }

            docStandardValues.Save(@"D:\inetpub\Sitecore\Website\temp\xml\StandardValues.xml");
            docItems.Save(@"D:\inetpub\Sitecore\Website\temp\xml\Items.xml");
            docUniqueRenderings.Save(@"D:\inetpub\Sitecore\Website\temp\xml\UniqueRenderings.xml");
        }

        private XElement StoreStandardValuesPresentation(TemplateItem template)
        {
            XElement svaluesNode = new XElement("temp");
            Item standardValues = template.StandardValues;

            if (!IsStandardValuesLoaded(template.ID.ToString(), ref svaluesNode) && standardValues != null)
            {
                svaluesNode = new XElement("template", new XAttribute("id", template.ID.ToString()), new XAttribute("nm", template.Name));

                string rawLayout = Sitecore.Data.Fields.LayoutField.GetFieldValue(standardValues.Fields[Sitecore.FieldIDs.LayoutField]);
                Sitecore.Layouts.LayoutDefinition layout = Sitecore.Layouts.LayoutDefinition.Parse(rawLayout);

                for (int i = 0; i < layout.Devices.Count; i++)
                {
                    Sitecore.Layouts.DeviceDefinition device = layout.Devices[i] as Sitecore.Layouts.DeviceDefinition;

                    if (device != null)
                    {
                        for (int j = 0; j < device.Renderings.Count; j++)
                        {
                            Sitecore.Layouts.RenderingDefinition rendering = device.Renderings[j] as Sitecore.Layouts.RenderingDefinition;

                            if (rendering != null)
                            {
                                if (!string.IsNullOrEmpty(rendering.ItemID))
                                {
                                    Database master = Sitecore.Configuration.Factory.GetDatabase("master");

                                    Item renderingItem = master.GetItem(rendering.ItemID);

                                    string dataSource = string.IsNullOrEmpty(rendering.Datasource) ? "" : rendering.Datasource;

                                    XElement rNode = new XElement("r", new XAttribute("nm", renderingItem.DisplayName)
                                        , new XAttribute("ph", rendering.Placeholder), new XAttribute("id", rendering.ItemID), new XAttribute("ds", dataSource));

                                    svaluesNode.Add(rNode);
                                }
                            }
                        }
                    }
                }

                docStandardValues.Element("templates").Add(svaluesNode);
            }

            return svaluesNode;
        }

        private void StoreItemPresentation(Item itm, XElement svNode)
        {
            if (itm != null)
            {
                XElement itmNode = new XElement("item", new XAttribute("id", itm.ID.ToString()), new XAttribute("nm", itm.Name));

                string rawLayout = Sitecore.Data.Fields.LayoutField.GetFieldValue(itm.Fields[Sitecore.FieldIDs.LayoutField]);
                Sitecore.Layouts.LayoutDefinition layout = Sitecore.Layouts.LayoutDefinition.Parse(rawLayout);

                for (int i = 0; i < layout.Devices.Count; i++)
                {
                    Sitecore.Layouts.DeviceDefinition device = layout.Devices[i] as Sitecore.Layouts.DeviceDefinition;

                    if (device != null)
                    {
                        for (int j = 0; j < device.Renderings.Count; j++)
                        {
                            Sitecore.Layouts.RenderingDefinition rendering = device.Renderings[j] as Sitecore.Layouts.RenderingDefinition;

                            if (rendering != null)
                            {
                                if (!string.IsNullOrEmpty(rendering.ItemID))
                                {
                                    Database master = Sitecore.Configuration.Factory.GetDatabase("master");

                                    Item renderingItem = master.GetItem(rendering.ItemID);

                                    string dataSource = string.IsNullOrEmpty(rendering.Datasource) ? "" : rendering.Datasource;

                                    //check if there are rendering which match the standard values exactly, if they donot record them
                                    //we are only worried about one device, the default device, you can extend it further for all devices and versions
                                    var svItems = (from t in svNode.Descendants("r")
                                                   where t.Attribute("ph").Value == rendering.Placeholder
                                                    && t.Attribute("id").Value == rendering.ItemID
                                                    && t.Attribute("ds").Value == dataSource
                                                   select t).ToList();

                                    if (svItems != null && svItems.Count() == 0)
                                    {
                                        StoreUniquePresentationDetails(renderingItem.DisplayName, rendering.ItemID);

                                        try
                                        {
                                            //depending on your situation, you would want to do before j-1 or after j+1 so that you get the right placement
                                            Sitecore.Layouts.RenderingDefinition nextRendering = device.Renderings[j - 1] as Sitecore.Layouts.RenderingDefinition;
                                            string nextDataSource = string.IsNullOrEmpty(nextRendering.Datasource) ? "" : nextRendering.Datasource;
                                            Item nextRenderingItem = master.GetItem(nextRendering.ItemID);

                                            XElement rNode = new XElement("r", new XAttribute("nm", renderingItem.DisplayName)
                                            , new XAttribute("ph", rendering.Placeholder), new XAttribute("id", rendering.ItemID), new XAttribute("ds", dataSource),
                                                new XElement("aftr", new XAttribute("nm", nextRenderingItem.DisplayName)
                                            , new XAttribute("ph", nextRendering.Placeholder), new XAttribute("id", nextRendering.ItemID), new XAttribute("ds", nextDataSource)));

                                            itmNode.Add(rNode);
                                        }
                                        catch
                                        {
                                            XElement rNode = new XElement("r", new XAttribute("nm", renderingItem.DisplayName)
                                                , new XAttribute("ph", rendering.Placeholder), new XAttribute("id", rendering.ItemID), new XAttribute("ds", dataSource));

                                            itmNode.Add(rNode);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                docItems.Element("items").Add(itmNode);
            }
        }

        private void StoreUniquePresentationDetails(string renderingName, string renderingID)
        {
            var items = (from t in docUniqueRenderings.Descendants("r")
                         where t.Attribute("id").Value == renderingID
                select t).ToList();

            if (items != null && items.Count() == 0)
            {
                XElement itmNode = new XElement("r", new XAttribute("id", renderingID), new XAttribute("nm", renderingName));
                docUniqueRenderings.Element("renderings").Add(itmNode);
            }
        }


        private bool IsStandardValuesLoaded(string templateID, ref XElement svNode)
        {
            var svItems = (from t in docStandardValues.Descendants("template")
                          where t.Attribute("id").Value == templateID
                          select t).ToList();

            if (svItems != null && svItems.Count() == 1)
                svNode = svItems.FirstOrDefault();

            return (svItems!= null && svItems.Count() == 1);
        }

        private void LoopForChildren(Item itm)
        {
            if (!string.IsNullOrEmpty(itm[Sitecore.FieldIDs.LayoutField]))
            {
                XElement svNode = StoreStandardValuesPresentation(itm.Template); //get svNode if not recorded, create and get it

                //get the standard values node
                //look at the current item's layout details
                //ignore whats in the standard values
                //add whats not in the standard values

                StoreItemPresentation(itm, svNode);
            }

            //Iterate though children
            Sitecore.Collections.ChildList childList = itm.Children;
            foreach (Item child in childList)
            {
                LoopForChildren(child);
            }
        }

    }

This commandlet produced the following sample outputs.

Items.xml

 

  
  
    
      
    
  
  
    
      
    
    
      
    
  

UniqueRenderings.xml

 

  
  
  
  
  
  

StandardValues.xml