1 December 2015

Part 3: Page History: IoC using Sitecore API

Following my previous post, I wanted to do a quick Parenthese to talk about simple IoC. Like most of the developers out there we are usually going for Castle.Windosr as IoC on most of our projects. This is working quite nicely in most of the cases and it is quite nice. However, on this special instance, my module code is actually really simple and it feels like Castle.Windsor would be quite an overkill... This would also add another dependency to Castle.Windsor which I did not want. Lucky there is another way using only Sitecore API. Since we already have a dependency on Sitecore then it is fine. Secondly this is really simple to implement... This is based on the great blog post from Anders Laub Christoffersen http://sitecorepromenade.blogspot.com.au/2015/12/part-2-page-history-code-to-retrieve.html

so what is it all about. Well this solution is using Sitecore Type Mapping to resolve your custom types. So what we do is add a configuration patch to add the following section:


  
    
      
      
    
  



This type mapping "type" attribute will point to classes that implement the Interfaces we need to resolve using the IoC. The next step is to write resolver class that will be used to look at the configuration typeMappings and create the object based on the "Type" attribute in from the configuration

    public class TypeResolver
    {
        public static T Resolve(string typeName, object[] parameters = null)
        {
            var xpath = "typeMappings/mapping[@name='" + typeName + "']";
            return (T)(parameters == null ?
              Sitecore.Reflection.ReflectionUtil.CreateObjectFromConfig(xpath) :
              Sitecore.Reflection.ReflectionUtil.CreateObjectFromConfig(xpath, parameters));
        }
    }



we can now use the class to resolve our interfaces based on the config. For instance on our controller, we will be able to do something like

    public class OPageHistoryController : System.Web.Mvc.Controller
    {
        private IPageHistoryService pageHistoryService = TypeResolver.Resolve("PageHistoryService");

        /// 
        /// get the full item history information including the renderings datasources
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public ActionResult GetCurrentItem(string id, string database, string language, string version)
        {
            return Json(pageHistoryService.GetPageHistory(id, database, language, version), JsonRequestBehavior.AllowGet);

        }
    }

Part 2: Page History: code to retrieve history information and datasource information

Following our previous post, we will now see a bit of the code that will allow us to retrieve the information we need to display the history of the item (Page Item) as well as retrieving all datasource items from the presentation renderings.

The first thing is that on the speak application running on the content tab, we need to retrieve the current item: ID, Version and Language so we can pass it on to a Service to retrieve the current item and its history. We want to pass on the version and language information so the content of the tab will be different when the user is changing the version and Language as per:


Well, your SPEAK app is mostly knockout JS based and all your actions to get your Data from will be located on the JS. This is where you will call the Webservice using Ajax Call and retrieve the json object with all your history information. So the easiest way will be to pass on the ID, Version, Language to the Controller call. Those parameters can easily get retrieved form the Query String Parameters using the following JS method:

        GetQueryStringParameter: function (name) {
            name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
            var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
                results = regex.exec(location.search);
            return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
        }

You can then call your webservice using something similar to:

        GetPageHistory: function (app) {
            jQuery.ajax({
                type: "GET",
                dataType: "json",
                url: "/OPageHistory/OPageHistory/GetCurrentItem?id=" + this.GetQueryStringParameter("id") + "&database=" + this.GetQueryStringParameter("database") + "&language=" + this.GetQueryStringParameter("language") + "&version=" + this.GetQueryStringParameter("version"),
                cache: false,
                success: function (data) {
                    app.set("pagehistory", data);
                },
                error: function () {
                    console.log("There was an error in GetPageHistory() function!");
                }
            });
        }

Obviously you may want to retrieve this data when building the model. so the above code will go into the Model Initialisation.

Now this will call the Controller to retrieve the json data and then bind it to the pagehistory. So what we now need to do is to make sure our service is up and running, accessible and return the correct jSon. In this exemple, I wanted to try MVC Area and define the service there. we could do a normal Sitecore WebAPI service but as this code is more a PoC I wanted to go with the Area...



To declare the area, I will be using a pipeline action:

    
      
        
        
      
    
      


During the pipeline action what you want to do is to check for the routes and if the route for the area does not exist then we will need to add it to the routes list:


                List list = Enumerable.ToList(Enumerable.Where((IEnumerable)RouteTable.Routes, (Func)(route =>
                {
                    Route route1 = route as Route;
                    if (route1 != null && route1.Defaults != null && route1.Defaults.ContainsKey("area"))
                        return string.Equals(route1.Defaults["area"].ToString(), "OPageHistory", StringComparison.Ordinal);
                    return false;
                })));
                if (!Enumerable.Any((IEnumerable)list))
                {
                    new OPageHistoryAreaRegistration().RegisterArea(new AreaRegistrationContext("OPageHistory", RouteTable.Routes));
                }
                else
                {
                    foreach (Route route in list)
                        route.DataTokens["UseNamespaceFallback"] = (object)true;
                }


During the Area registration we will just add the following route:

        context.MapRoute(
                "OPageHistory_default",
                "OPageHistory/{controller}/{action}/{id}",
                (object)new { area = "OPageHistory", action = "Index", id = UrlParameter.Optional },
                (object)new { 
                    controller = ".*OPageHistory"
                }
            );


Now in order to be able to test and access some code you will need to create a controller: OPageHistory with a GetCurrentItem method so the call to /OPageHistory/OPageHistory/GetCurrentItem will resolve... So lets create something like:

    public class OPageHistoryController : System.Web.Mvc.Controller
    {

        /// 
        /// get the full item history information including the renderings datasources
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public ActionResult GetCurrentItem(string id, string database, string language, string version)
        {
            return Json("Hello world", JsonRequestBehavior.AllowGet);

        }
    }


If all is working fine, you should now be able to call controller /OPageHistory/OPageHistory/GetCurrentItem

Now our controller is accessible, we just need to look at the different code to retrieve:


  1. Datasource information: We already saw something like that in a previous blog post that you can review here: http://sitecorepromenade.blogspot.com.au/2015/08/loop-through-renderings-in-presentation.html. This will go through the presentation details and retrieve the renderings. This will then retrieve the datasource associated with the rendering from the RenderingReference.Settings.Datasource... You can also check the code in bitbucket
  2. Item History. The item history is in 2 parts: you first need to retrieve the item versions. Once you have the workflow enable, everytime you update the item, sitecore will create a new version. so the item can have multiple versions. Then each version will go through different states: Draft > Awaiting for Approval > Approved...
An easy way to retrieve the versions of the item is to use the following code. This will return an item array:
            var allOlderVersions = item.Versions.GetOlderVersions();


You can then loop through the list of items to get the workflow history. The workflow history (WorkflowEvents) can be retrieved using the workflow provider as per the following
            var workflowProvider = (item.Database.WorkflowProvider as Sitecore.Workflows.Simple.WorkflowProvider);
            if (workflowProvider == null)
                return;

            if (workflowProvider.HistoryStore == null)
                return;

            var workflowEvents = workflowProvider.HistoryStore.GetHistory(item);
            foreach (var wEvent in workflowEvents)
            {
                var vHistory = new VersionHistory(wEvent, item.Database);
                if (vHistory != null)
                    ItemVersionHistory.Add(vHistory);
            }


Combining all, you can easily retrieve all the history for not only the page item but go through the presentation and retrieve the history of each item set as rendering datasources. On the module code in bitbucket, I am also looping through the children of the item from the datasource. The reason behind is that some of the datasource items use their children items. For instance to render an accordion component, I usually set an item as datasource (Accordion Panel) then each of its children will be a section of the accordion...

On the next part, I just wanted to do a quick parenthese to show a quick way of doing IoC using Sitecore...

Part 1: Page History: Create a content Editor Tab running a SPEAK App

As per the previous Blog Post: http://sitecorepromenade.blogspot.com.au/2015/12/viewing-history-of-page-and-components.html, We will first see how to create a Content Editor Tab in Sitecore and link a new SPEAK App that will run in the Tab.


So the first thing we will look is where do we create the content editor. Well as you can imagine it will be in the Core database. So if you open sitecore desktop and switch the context to Core DB, you can then navigate to the following location: /sitecore/content/Applications/Content Editor/Editors


You can create a new folder for your editor item. This will be from the Common/Folder template.
You can also create a new item from the template /sitecore/templates/Sitecore Client/Content editor/Editor
This item will be the Tab Item you want to add to your templates...


When filling in the fields you will notice the URL field. This field is where you specify which application you would like to run. Sitecore will embbed this resources to run in an iframe inside the Tab. You could easily run a normal aspx page there which will be quite simple to create but for the purpose of the PoC, we will select to go for a SPEAK app... The URL for you App will be similar to: /Sitecore/Client/Your Apps/YourAppName


So let's create a new SPEAK App. There are awesome tutorials around on how to create a SPEAK App. One of my favourite is written by Vikram (Thanks for your hard work.):  https://cmsview.wordpress.com/2015/10/07/sitecore-8-for-beginners-creating-speak-application/ 
It explain quite in details how to create a simple app but also how to create a custom one and how to get data from a custom component... This blog post is not intended to be atutorial to show you how to create the SPEAK app so I will not go over all the steps to create the App itself. But in the nutshell you will create a custom SPEAK Component using Sitecore ROCKS:


This will create you the three files: cshtml, and 2 js  files:

Now in sitecore you will have created the application items which will be as per the following:

On the Application presentation, you will have your rendering added:

Now your Speak App is set. Make sure your updating the Content editor item as per the above to point the URL field to the new SPEAK APP:


Now , the last missing link is to make it appear on a page or on the template standard value. To do that, Switch to the Master Database and locate either the page you want your content editor tab and/or your template standard values item where you want to add your tab to. On the Appearance section, locate the "Editors" Field:

Add the new editor item we have created:

This will now bring the new tab as per the following:


The next steps will be looking into the code that will read the item and retrieve all the presentation and history information....

Part 0: Page History Module: Viewing the history of the page and the components items

Many Sitecore Implementation includes different type of items: Page Items but also Data Items. As part of sitecore best practices, we will use Datasources for rendering quite extensively. This means that every pages on our website are composed of one main Item (Page Item itself) plus all the Datasources Items which can be defined in different location.


Although we can use the workflow history to view each individual item history, it is quite troublesome to view all the page elements history.

So I wanted to PoC a quick module that will display a summary of the history on a new Content editor Tab. This is intended to help content editor to view the page elements, their location on the tree as well as their history (Version and Workflow history).



What I wanted to do is giving some sample on:
  • How to add a new Tab on the content Editor
  • How to access presentation information from Code and retrieve history from a version information
  • How to Use Sitecore API as Simple IoC
  • How to use a custom SPEAK App. For the purpose of the sample code, the SPEAK app is only a one custom rendering which could be easily split into several smaller rendering if require. However, making it a full SPEAK App was not the main purpose here.
You can find the final code in my BitBucket: https://bitbucket.org/yannrandri/opagehistory 
The sitecore package can be found in the following location:

So I separated this work in multiple blog post to ensure this one entry was not getting to big...

Please see
Part 1: Page History: Creating a Content editor Tab that run a SPEAK App
Part 2: Page History: code to retrieve history information and datasource information
Part 3: Page History: IoC using Sitecore API