Visualising Aliases

Recently a client I work for came to me with an interesting question. Their Sitecore-based website makes heavy use of Aliases to set up shortened URLs for publicity purposes, but they were finding it difficult to manage the large number of aliases they were creating. Their key issues were remembering which pages had aliases defined, and remembering to delete aliases when they removed the pages they were related to.

Sitecore provides a helpful dialog for managing aliases, in the Presentation ribbon tab:

image

However you still have to remember to click on it – and that’s one of the things the client was hoping for help with.

Having had a bit of a think about what could be done here, I came up with two ideas – visualising which items have aliases defined, and automating that deletion. Lets have a look at visualisation first.

Having done a bit of reading around, it turns out that you are able to easily customise the “gutter” in the Sitecore Content Editor. This is the left hand column of the content tree. Out of the box, right-clicking the gutter gives you a series of optional information icons to enable:

image

When you select one of these options, the system will render extra icons in the gutter for any item which meets the criteria defined by this custom gutter control – so lets see what’s involved in adding a gutter rendering to show when content items have aliases defined.

Custom gutter renderings are classes based on the Sitecore.Shell.Applications.ContentEditor.Gutters.GutterRenderer class. All you have to provide is an implementation for the GetIconDescriptor() method:

public class AliasGutterRenderer : GutterRenderer
{
    protected override GutterIconDescriptor GetIconDescriptor(Sitecore.Data.Items.Item item)
    {
        return null;
    }
}

If we want to visualise Aliases, we need to write some sort of query to detect if the current item has any Aliases attached to it. There are a couple of ways you can go about this. One way would be to write a query for children of the /sitecore/system/Aliases item, and look for references to the current item’s ID in the Linked Item field. But Sitecore can make our life easier than this – since it maintains something called the Link Database for us. Inside the Sitecore database, it maintains references between items. Whenever you fill in a field which stores a link between two or more items this relationship is stored in the Link Database to make it easy to query these relationships later. You can see this data yourself when you click the Links button in the Navigate tab of the ribbon:

image

You’ll notice that this example page has lots of links in the database table – it references things like workflow, templates, sublayouts etc. But it also shows that it is referred to by an Alias item. And that shows us that we can use the Links Database to do the query we need.

So we can write a bit of code to count the number of Aliases related to the current item with a simple Linq query:

public class AliasGutterRenderer : GutterRenderer
{
    private static Sitecore.Data.ID aliasID = new Sitecore.Data.ID("{54BCFFB7-8F46-4948-AE74-DA5B6B5AFA86}");

    protected override GutterIconDescriptor GetIconDescriptor(Sitecore.Data.Items.Item item)
    {
       int aliases = Sitecore.Globals.LinkDatabase.GetReferrers(item)
            .Select(l => l.GetSourceItem())
            .Where(s => s.TemplateID == aliasID)
            .Count();

        return null;
    }
}

Every time Sitecore processes an item in the content tree it passes that item into our method – and we count the Aliases. The GetReferrers() method here fetches the list of items which refer to the item in question. We then use a select clause to change the data we get back from IDs into items. And then we filter those items by Template ID to ensure we have a list of only referring Aliases. Finally calling Count() tells us how many items matched our criteria.

Having done a bit of testing, it turns out there are some scenarios where the data in the Link Database can be outdated. And that can cause this query to fail in certain circumstances. The most common being after the deployment of a package which affects the items and their aliases. This issue is resolved with the “Rebuild Link Database” option in the Control Panel – but to prevent issues, for demo purposes we’ll catch any exceptions thrown by this query. In real-world code we should do something like log errors or generate a custom icon that can trigger the link database rebuild if necessary – but for the purposes of this example we’ll just discard the error.

The next job is generating the custom icon to display – and this is where the return type of the GetIconDescriptor() method comes  in. The return type of the method is the GutterIconDescriptor class – and we fill in and return an instance of this to tell the UI what to render. It’s simple enough to fill in – you can provide a path to an icon, a tooltip string and an on-click event to trigger. And when you return this data, the UI renders it in the gutter.

Putting all that together gives us:

public class AliasGutterRenderer : GutterRenderer
{
    private static Sitecore.Data.ID aliasID = new Sitecore.Data.ID("{54BCFFB7-8F46-4948-AE74-DA5B6B5AFA86}");

    protected override GutterIconDescriptor GetIconDescriptor(Sitecore.Data.Items.Item item)
    {
        GutterIconDescriptor gid = null;
        int aliases = 0;

        try
        {
            aliases = Sitecore.Globals.LinkDatabase.GetReferrers(item)
                .Select(l => l.GetSourceItem())
                .Where(s => s.TemplateID == aliasID)
                .Count();
        }
        catch (Exception)
        {
            // this should always succeed - exceptions seem to come
            // if link database is out of date
            // In production code we'd do something cleverer than
            // this and try to prevent rather than catch the exception
        }

        if (aliases > 0)
        {
            gid = new GutterIconDescriptor();
            gid.Icon = "Network/32x32/spy.png";
            gid.Tooltip = string.Format("This item has {0} alias{1}", aliases, aliases > 1 ? "es" : "");
            gid.Click = "item:setaliases(id=" + item.ID + ")";
        }

        return gid;
    }
}

Here we’ve set the gutter icon to be the same as the icon used by the Aliases button in the ribbon for consistency. And we’ve set the tooltip to show how many aliases the current item has. Finally, the Click event has been set to the same command used by the Aliases button in the ribbon – a call to the “item:setaliases” command, passing the ID of the current item.

So there’s the code – what do we need to do to make this custom gutter rendering available for users? The data for configuring the Content Editor UI lives in the Core database, under /sitecore/content/Applications/Content Editor/Gutters and configuring a new Gutter Renderer is as simple as adding a new item here based on the “Gutter Renderer” template:

image

The “Header” field here contains the text displayed in the right-click context menu we saw back at the beginning of this post. The “Type” field contains the fully qualified .Net type descriptor for the custom rendering class we defined. As usual for configuring extension types, this is formatted as “, ”.

And once you have configured that for your custom class, you can go back to the Master database and enable the new renderer by right-clicking the gutter:

image

Our new custom gutter render is now visible, and its selected state will be remembered between user sessions. Once it’s selected, we start to see the alias icon in the gutter for any item which has Aliases defined:

image

And clicking the icon will launch the Aliases dialog.

Next week we’ll have a look at how we might remove Aliases automatically when the item they refer to is deleted.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s