Got the Sitecore MVP Award

This wednesday it got official. I’v been awarded with the Sitecore MVP Award. It feels really great to be recognised for my knowledge in the Sitecore community. I also want to congratulate the other 41 persons world wide that also was awarded.

Integrate an external media library into Sitecore – Part 3

Finally the last post in this serie is done.

In the previous post we covered how we did to store the information we needed in the Image field in Sitecore.

In this post I will show you what we did to get the information stored in the Image field out on the website. I’ll start with a short warning, this will get complicated, so I will not go into details on all parts. At some point in this project we took some shortcuts which will explain some of the solutions we did that could have been better. This post does not contain much code examples since this post would be way to long, so at the end is a zipped file with all classes used in this solution.

When requesting any media from Sitecore, all file ends with .ashx, this ending is mapped to a requesthandler that steams the media to your browser.
Our plan was to use this in such a way that we request an predefined media-item inside Sitecore based on one of our extended media templates with the external media id and media extension as querystrings.
So we created an item /sitecore/media library/EmptyMedia/EmptyImage based on the template Externa Image that we created in one of the earlier posts without any data.
This media-item would get the url:
~/media/EmptyMedia/EmptyImage.ashx
We were going to request this item with both the id from the external media library and the extension of the image like this:
~/media/EmptyMedia/EmptyImage.ashx?extid=30779&extension=jpg

This was going to be our base for requests, now we needed a requesthandler that would take care of our media items and not mess up the existing Sitecore media functionality.
We started by creating a complete copy of the existing MediaRequestHandler that Sitecore uses for handling all .ashx requests. And started modifying it.

Our custom requesthandler would have to check if the request was done with extid as querystring and based on this either handle it as an external media item or as a Sitecore media item.

In Sitecores MediaRequestHandler, the ProcessRequest method wich is triggered on request, runs a method bool DoProcessRequest(HttpContext context)  which validates the request and creates an important object Sitecore.Resources.Media.Media and passes this to the method bool DoProcessRequest(HttpContext context, MediaRequest request, Sitecore.Resources.Media.Media media) which then loads the stream and passes it to the response output.

Simple right? Well we thought so, and we wanted to use these methods since they were already there. We started by checking if the request was done with extid as querystring and if that was the case a ran our custom method:

bool DoProcessExternalRequest(HttpContext context, MediaRequest request, string id, string extension)

This method was responsible for creating an object of the Sitecore.Resources.Media.Media. So we did some research on the Media class and found out that to create this we needed to create a MediaData object and pass it to the constructor of Media. And to create the MediaData object we needed a MediaItem that was based on the EmptyImage item inside Sitecore.
We now knew which objects we needed, so it was time to find out where the stream of data was loaded. After some poking around we found that the MediaRequestHandler calls the method GetStream in Sitecore.Resources.Media.Media wich in turn runs the pipeline getMediaStream with the MediaData object as parameter. All of the processors in the getMediaStream pipeline triggers a chain of methods that at one point ends up running the method GetBlobStream of MediaData which would retrieve the data from the Field by calling its GetBlobStream method. This was our entry point. We were going to check if the stream delivered from the Field objects was null and if so check if so validate if the request was for an external media file and deliver its stream instead.

So we had to be able to make the external id and extension available for this method. Since we were going to use this in some different cases to we decided to make an extension of the complete chain of classes and and mimic the behavior. We already had an extension of the MediaItem called ExtMediaItem created in the previous post which could hold the external id, extension and the width and height. So over to the MediaData class, we needed it to be able to hold and pass the external id and extension and to handle the ExtMediaItem. So the class ExtendedMediaData was created and we did the following alterations in this class:

  • Added attribute ExtID to hold and pass the external media id
  • Altered MediaId to deliver a unique id for external media. The MediaId is, what I think, used for caching.
  • GetBlobStream – this method was altered to try to load the data from the external media library if the stream that was delivered by the field is null.

With that altered we tried to get this working, so we created a ExtMediaItem based on the EmptyImage in Sitecore.
We created a ExtendedMediaData based on the ExtMediaItem and provided it with the  the values from the querystring for extid and extension.
This was then fead into a Media object and sent to
bool DoProcessRequest(HttpContext context, MediaRequest request, Sitecore.Resources.Media.Media media)
in the RequestHandler.
Unfortunately this didn’t, we got an exception telling us that the object provided wasn’t a MediaData object, even though ExtendedMediaData inherits from MediaData.
Because the lack of time we decided to create an extension of the Media class aswell where we altered all MediaData objects handled in the class to ExtendedMediaData, which worked.

So now when I make the following request:
~/media/EmptyMedia/EmptyImage.ashx?extid=30779&extension=jpg
the following steps are made:

  1. We check if the url contains extid and calls a custom method to build a Media object.
  2. An ExtMediaItem based on the item /sitecore/media library/EmptyMedia/EmptyImage is created
  3. An ExtendedMediaData is created with the ExtMediaItem and also provided with the ExtId and Extension values.
  4. A custom Media class wich intherits Sitecores Media class i created with the ExtendedMediaData.
  5. This Media object is provided to the DoProcessRequest method in the requesthandler.
  6. The DoProcessRequest calls the method GetStream on the Media object which triggers the getMediaStream pipeline.
  7. At some point one of the processors in the getMediaStream pipeline triggers a chain of methods that calls the GetBlobStream in the ExtendedMediaData.
  8. If the stream delivered by the Field object in ExtendedMediaData, it tries to get a stream from the web service of the external media library based on the extid value.
  9. Once this stream has been fetched, it’s returned and handled by the processors in the getMediaStream pipeline, like being resized. This is where the extension is needed, if the ExtendedMediaData doen’t have an extension it won’t be handled as a image.
  10. When the getMediaStream pipeline is done processing the stream it’s return to the Media object which will add the stream to Sitecores media cache and then return it to the RequestHandler.
  11. This is where it all ends, when the stream has been delivered to the RequestHandler, it is written to the output stream of the HTTP response object.

So thats the end of the series on how we integrated an external media library. Below is a link to all files used, they are not in any order what so ever and there might be some or alot of code that isn’t in use 🙂

Here’s the zip-file 😀

Sitecore Gutters

After a long and time consuming projects it’s now time for another post:)

Today I’m going to show some examples of Gutters I use when developing and that we recommend editors to use. But first of lets look at what a Gutter is.

A Gutter is an indicator that displays any kind of simple information of an item to the left of the Content Tree in the Content Editor like this:

What kind of information is up to you. I’m going to show two simple examples to indicate if:

  • if related items of an item has been published and if it is the latest version.
  • a media items is used anywhere.

 

To be able to create a Gutter we will have to create a class that inherits from Sitecore.Shell.Applications.ContentEditor.Gutters.GutterRenderer and override the GetIconDescriptor method. If we want to indicate something we return a GutterIconDescriptor and if we have nothing to indicate we return null. So lets start with the first example to indicate if related items are published and if it’s the latest versions. I going to call my class “RelatedItemsPublishedNotification”, easy and simple 😉

public class RelatedItemsPublishedNotification : Gutters.GutterRenderer
{
    protected override GutterIconDescriptor GetIconDescriptor(Item item)
    {
        return null;
    }
}

This is the base class for the Gutter. What we want to do is to get all related items, in other words all links of that item. This is done by calling item.Links.GetAllLinks() which will return an array of ItemLink. A ItemLink isn’t the actual Item, we will have to get the item from the Master database using the TargetItemID attribute. Now you might ask why I don’t use the GetTargetItem method? Well it seems like this method checks with the web database and returns a valid version and if non exist the latest. So if we want to compare versions later on we will have to do like this.
Since we want check if it is published we will have to check if the item exist in the Web database. If it doesn’t exist I want to return a GutterIconDescriptor that tells the user “A related item is not published!”. The Gutter also displays a Icon and I’ve choosen the Network/16×16/environment_error.png icon. I also want to isolate the usage of this Gutter to the sitecore/content section of the Content Tree which I will do by checking if the item path starts with /sitecore/content/

public class RelatedItemsPublishedNotification : Sitecore.Shell.Applications.ContentEditor.Gutters.GutterRenderer
{
    protected override GutterIconDescriptor GetIconDescriptor(Sitecore.Data.Items.Item item)
    {
        if (!item.Paths.FullPath.StartsWith("/sitecore/content/")) return null;
        var itemLinks = item.Links.GetValidLinks();
        var webDb = Factory.GetDatabase("web");
        var masterDb = Factory.GetDatabase("master");

        foreach (var itemLink in itemLinks)
        {
            var masterItem = masterDb.GetItem(itemLink.TargetItemID);
            var webItem = webDb.GetItem(masterItem.ID);
            if (webItem == null)
            {
                var descriptor = new GutterIconDescriptor();
                descriptor.Icon = "Network/16x16/environment_error.png";
                descriptor.Tooltip = "A related item is not published!";
                return descriptor;
            }
        }
        return null;
    }
}

So this will indicate if a related item hasn’t been published. I also wants to indicate if it isn’t the latest version. To do this I will have to compare the version numbers of the item in the web and the master. If the item in the master database have a higher version number I want to tell the user “A related items latest version has not been published!” and indicate with another icon Network/16×16/environment_warning.png. Like this:

public class RelatedItemsPublishedNotification : Sitecore.Shell.Applications.ContentEditor.Gutters.GutterRenderer
{
    protected override GutterIconDescriptor GetIconDescriptor(Sitecore.Data.Items.Item item)
    {
        if (!item.Paths.FullPath.StartsWith("/sitecore/content/")) return null;
        var itemLinks = item.Links.GetValidLinks();
        var webDb = Factory.GetDatabase("web");
        var masterDb = Factory.GetDatabase("master");

        foreach (var itemLink in itemLinks)
        {
            var masterItem = masterDb.GetItem(itemLink.TargetItemID);
            var webItem = webDb.GetItem(masterItem.ID);
            if (webItem == null)
            {
                var descriptor = new GutterIconDescriptor();
                descriptor.Icon = "Network/16x16/environment_error.png";
                descriptor.Tooltip = "A related item is not published!";
                return descriptor;
            }
            if (masterItem.Version.Number > webItem.Version.Number)
            {
                var descriptor = new GutterIconDescriptor();
                descriptor.Icon = "Network/16x16/environment_warning.png";
                descriptor.Tooltip = "A related items latest version has not been published!";
                return descriptor;
            }
        }
        return null;
    }
}

The Gutter is now complete and it’s time to configure it in Sitecore. This is really simple and is all done in the Core database and are stored at /sitecore/content/Applications/Content Editor/Gutters. Just duplicate on of the available Gutter nodes like the one called “Locked Items”. Alter the text in the Header field to something like “Related Items Published” and change the Type to the namespace, assembly of the class. I my example it’s Pyramid.Utils.Gutters.RelatedItemsPublishedNotification, Pyramid.Utils. And now you’re done. You will now be able to activate the new Gutter in the Sitecore Content Tree.

 

Now lets dive into the other Gutter which will indicate if a media item is being used, and tell how often it is being used using the tooltip. This is kind of straight forward. After some digging around in the Sitecore assemblies looking at how Sitecore displays the usage in the Media Library folder view I found out that I can get all references to an item by calling Sitecore.Globals.LinkDatabase.GetReferrers(item); This will return an array of ItemLinks which represent all items that reference to the current item. And by using the length of the array I’ll get the usage count. So basically the class will look like this:

public class MediaInUseNotification : GutterRenderer
{
    protected override GutterIconDescriptor GetIconDescriptor(Sitecore.Data.Items.Item item)
    {
        if (!item.Paths.FullPath.StartsWith("/sitecore/media library/")) return null;
        var referrers = Globals.LinkDatabase.GetReferrers(item);
        if (referrers.Length == 0) return null;

        var descriptor = new GutterIconDescriptor();
        descriptor.Icon = "Business/16x16/paperclip_delete.png";
        descriptor.Tooltip = string.Format("This media items is used {0}!", referrers.Length == 1 ? "1 time" : referrers.Length + " times");
        return descriptor;
    }
}

When configured in Sitecore the result will look like this:

 

Kind of cool huh? Well you can do more 😉 You can actually add a command on the Click event of the Gutter. This is done on the GutterIconDescriptor object that is return. Find the command you want to run and add it like this before returning the descriptor:

descriptor.Click = String.Format("pagedesigner:reset(id={0})", item.ID);

This click event will trigger the pagedesigner:reset command with the current item id as parameter which will reset the current items layout definitions to standard values. This is a snippet from an older Gutter that in later Sitecore version has been implemented as an default Gutter (Presentation Overridden).

Thats all for now

Playing with Sitecore Include files

Sitecores include files which are stored in the /App_Config/Include/ folder are a nifty way of alter configurations without editing the web.config file. However sometimes you find yourself questioning how you would write the configurations in the include file.

Unfortunately there’s a lack of good documentations so most of the time you will have to go with the trial and error approach. The only useful documentation I’ve found is this and some forum posts.
As your help you have the tool for showing the merged web.config file at your side. Which can be found at /sitecore/admin/showconfig.aspx

I’ve been poking around in the Sitecore.Xml.Patch.XmlPatchUtils class and found out that the following attributes are available when writing your configuration file.

patch:before
patch:after
patch:instead
patch:delete
patch:attribute

Most often you don’t need to write that complicated configurations like when you want to add a processor into a pipeline before or after a specific processor. The most basic is just adding at the last position like this example where we add a command at the last position.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <commands>
            <command name="test:mycommand" type="MyClasses.MyCommand, MyAssembly"/>            
        </commands>
    </sitecore>
</configuration>

Sometimes you need do add it at a specific position, like before or after a processor. The following example will add the “MyClasses.DoMyStuffProcessor, MyAssembly” processor after the “Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel” processor in the httpRequestBegin pipeline.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <pipelines>
            <httpRequestBegin>
                <processor patch:after="*[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" type="MyClasses.DoMyStuffProcessor, MyAssembly" method="SomeProcess"/>
            </httpRequestBegin>
        </pipelines>
    </sitecore>
</configuration>

Often you want to change the value of an element like a Sitecore setting, like changing the MailServer value. This is done like this.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            <setting name="MailServer">
                <patch:attribute name="value">localhost</patch:attribute>
            </setting>
        </settings>
    </sitecore>
</configuration>

Or maybe remove one which is done like this

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            <setting name="SomeSetting">
                <patch:delete/>
            </setting>
        </settings>
    </sitecore>
</configuration>

However sometimes you want to alter some more complex structure like when changing the interval of the task agent for the master database. Since this configuration looks allot like the one for the core database, where the only difference is the value of the sub elementlike you can see below.

<agent type="Sitecore.Tasks.DatabaseAgent" method="Run" interval="00:10:00">
  <param desc="database">core</param>
  <param desc="schedule root">/sitecore/system/tasks/schedules</param>
  <LogActivity>true</LogActivity>
</agent>
<!-- Agent to process schedules embedded as items in a database -->
<agent type="Sitecore.Tasks.DatabaseAgent" method="Run" interval="00:10:00">
  <param desc="database">master</param>
  <param desc="schedule root">/sitecore/system/tasks/schedules</param>
  <LogActivity>true</LogActivity>
</agent>

Here we only want to change the agent for the master database. What I did was using the patch:instead attribute and replaced the entire configuration like this.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <scheduling>
            <agent patch:instead="*[@type='Sitecore.Tasks.DatabaseAgent' and contains(.,'master')]" method="Run" type="Sitecore.Tasks.DatabaseAgent" interval="00:30:00">
                <param desc="database">master</param>
                <param desc="schedule root">/sitecore/system/tasks/schedules</param>
                <LogActivity>true</LogActivity>
            </agent>
        </scheduling>
    </sitecore>
</configuration>

As you can see I used a little xpath where i look for an agent of the type ‘Sitecore.Tasks.DatabaseAgent’ and which contains the string ‘master’ somewhere inside.

You can probably alter any Sitecore configuration using include files, but some alterations will probably have you use more complex xpath queries.

I hope this little playtime with Sitecores include file was helpful.