This blog has moved to http://jermdavis.wordpress.com/

Saturday 29 March 2014

Moving to Wordpress...

After a few weeks of banging my head against Blogger, trying to write posts with code in them that don't look terrible, I have decided to move to Wordpress. Sorry Blogger, but you're just not cutting the mustard.

All future posts will be made to http://jermdavis.wordpress.com

Friday 21 March 2014

Multiple data source locations in the “Select Associated Content” dialog box

Working on some components for a client’s site recently, it struck me that there were circumstances where it would be helpful to editors to allow the “Select Associated Content” dialog to have multiple options without just showing the whole content tree. Scenarios like having both a global shared content folder and a sub-site specific shared content folder that editors could choose between, for example. Something that looked like this:

image

More than one root item displayed in the selection list allows editors to choose an appropriate location without the option to put their data in places you didn’t want them to…

When I started looking into whether this was possible or not, I initially assumed it would end up being quite a complicated modification – involving modifying this dialog, and the pipelines and commands which are used for this bit of UI. But after a bit of digging through the code, it turns out that it’s actually much simpler than this, since the dialog already supports the idea of multiple roots. What we need to provide is the right user interface to configure the roots and a bit of code to ensure this config is passed through to the dialog. Here’s what I came up with, after a bit of hacking:

So what happens when you click the Associated Content button in the UI? Well looking at the commands configuration for the Page Editor, we find that the UI triggers the command Sitecore.Shell.Applications.WebEdit.Commands.SetDatasource. Digging through that class with Reflector, it seems that the Execute() method gets the client page to call the Run() method. This method does all sorts of stuff, but the interesting bit is that it calls a static method called CreatePipelineArgs() to set up the data for the rest of the pipeline. And this is where the interesting stuff happens:

private static GetRenderingDatasourceArgs CreatePipelineArgs(
ClientPipelineArgs args, Item renderingItem)
{
Item clientContentItem =
WebEditUtil.GetClientContentItem(Client.ContentDatabase);
GetRenderingDatasourceArgs getRenderingDatasourceArgs =
new GetRenderingDatasourceArgs(renderingItem)
{
FallbackDatasourceRoots = new List<item>
{
Client.ContentDatabase.GetRootItem()
},
ContentLanguage = (clientContentItem != null) ?
clientContentItem.Language : null,
ContextItemPath = (clientContentItem != null) ?
clientContentItem.Paths.FullPath : string.Empty,
ShowDialogIfDatasourceSetOnRenderingItem = true
};
LayoutDefinition currentLayoutDefinition =
SetDatasource.GetCurrentLayoutDefinition();
ID clientDeviceId = WebEditUtil.GetClientDeviceId();
string uniqueId = args.Parameters["uniqueId"];
if (currentLayoutDefinition != null && !ID.IsNullOrEmpty(clientDeviceId))
{
RenderingDefinition renderingByUniqueId = currentLayoutDefinition
.GetDevice(clientDeviceId.ToString()).GetRenderingByUniqueId(uniqueId);
if (renderingByUniqueId != null)
{
getRenderingDatasourceArgs.CurrentDatasource =
renderingByUniqueId.Datasource;
}
}
return getRenderingDatasourceArgs;
}

The critical bit for what we’re trying to achieve is where the FallbackDatasourceRoots property is assigned a list of items. If we follow this through the code for the rest of the display of the dialog, this property is used to set the contents of the tree view. So this is the bit of code we need to modify to deal with the Datasource Location field of a sublayout or rendering having multiple items assigned.

Slightly annoyingly, the class defining this method doesn’t make life easy for modifying it. In fact due to the use of static methods, we’re pretty much stuck with the idea that we need to decompile the whole class and copy it into our own codebase in order to modify it. Once we’ve done that, we need some code that can process the Datasource Location field and generate a List<Item> that contains whatever items it finds.

If we assume that the data is going to be formatted using the standards for a multi-select field, then we could write a method something like this:

private static List<item> fetchDatasourceRoots(Item renderingItem)
{
List<item> roots = new List<item>();

string itemIDs = renderingItem.Fields["Datasource Location"].Value;

if (string.IsNullOrWhiteSpace(itemIDs))
{
roots.Add(Client.ContentDatabase.GetRootItem());
return roots;
}

string[] ids = itemIDs.Split('|');

if (ids.Length > 0)
{
foreach (string id in ids)
{
roots.Add(Client.ContentDatabase.GetItem(id));
}
}
else
{
roots.Add(Client.ContentDatabase.GetRootItem());
}

return roots;
}

We take the item that represents the Rendering or Sublayout and we extract the value for the Datasource Location field. If it’s empty then we return the default data – the same data the original code used. If it’s not empty then we split it into individual IDs and add each of the items these represent to the collection we return.

And with that, we can modify the code of the CreatePipelineArgs() to initialise the FallbackDatasourceRoots property using our new method instead of the original code.

Two more things need we need to do for this to work. First, we need Sitecore to use our replacement custom SetDatasource class. That just needs a quick config patch, along the lines of:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<commands>
<command name="webedit:setdatasource">
<patch:attribute name="type">
Testing.CustomSetDataSource, Testing
</patch:attribute>
</command>
</commands>
</sitecore>
</configuration>

The “patch:attribute” element here tells the configuration system to replace the value of the specified attribute of the context element with the new value we provide.

The second thing is that the Datasource Location field of the Rendering and Sublayout templates needs to become a multi-select type of field. The TreeListEx control seems appropriate. That field lives at /sitecore/templates/System/Layout/Sections/Rendering Options/Editor Options/Datasource Location in the content tree, so we can change the type:

image

That will change the type of this field for all the renderings and sublayouts. And with that, you can now set multiple items as the Datasource Location:

image

And now both of those items will show up as roots in the Set Associated Content dialog.

Now, there are a few caveats with this modification as it stands. One is that it’s not compatible with the relative data source locations code from a previous post, because it stores GUIDs in the Datasource Location field rather than a string. It also means that any pre-existing data in the Datasource Location field is now of the wrong type – so you will have to reset any values set before this change. And thirdly, because we’ve had to copy and replace an entire command class any future modifications to this in the Sitecore codebase won’t make it into our solution without us doing it manually. So borrow this with a bit of caution...

Tuesday 18 March 2014

Bonus chatter: Getting the Web Forms for Marketers connection string

I hit an issue recently where it was necessary to get direct access to the Web Forms for Marketers database. The code needed to do some custom processing of previous form submissions in a Sitecore website, and it needed to process so much data that the public APIs for WFfM were too slow to use. Slightly annoyingly, the connection string for WFfM is not added to the the standard .Net <connectionStrings/> config element, but is stored in the custom provider configuration in WFfM’s forms.config file:

<formsDataProvider type="Sitecore.Forms.Data.DataProviders.WFMDataProvider,
Sitecore.Forms.Core">
<param desc="connection string">user id=usr;password=pwd;
Data Source=localhost;Database=WebFormsForMarketers</param>
</formsDataProvider>

Not ideal. This is compounded by the fact that the data provider that is loaded via this configuration does not expose the connection string either. So there’s no easy way to extract it via the provider model.

There are two ways to tackle this. The first that springs to mind is that since the forms.config file is just XML, it wouldn’t be hard to write some code that loads and parses it to get the data. The alternative is to use a bit of Reflection on the WFfM provider objects, as that lets us get access to non-public data inside the provider classes.

Intuitively, the XML solution sounds like it’ll make heavier use of CPU and memory to me. It’s a fairly big config file to load just to parse out a single string. So I decided to give the Reflection approach a try and see how that worked out.

NOTE: Whenever you write code that uses Reflection to get at things that are not part of the public APIs of someone else’s code you’re taking  a risk. Inherently this approach makes your code more fragile. There’s no guarantee that the internals you’re accessing will still be there in the next release – and that will lead to errors. Also since there’s a provider model here there’s no guarantee that the internals you’re trying to access will be inside any concrete classes that are configured. Reflection is a useful tool, but don’t forget the risk you’re taking. Please remember that if you choose to borrow from this code.

And with that public service announcement out of the way, lets get to some code…
(As usual, you’d want more error handling in production code)

The public entry point for accessing data from Web Forms for Markerters APIs is the Sitecore.Forms.Data.DataManager class. This is a static class which provides a facade on top of the internal logic for finding the real forms data API objects. When it’s initialised, this class creates an instance of the protected Sitecore.Forms.Data.DataProvider class. And in turn, that type uses Sitecore’s configuration factory classes to get an instance of the concrete class for accessing the WFfM data.

So the first task is to get access to this DataProvider class. There are two ways to go here: Use reflection to create an instance of it, or use Reflection to extract the instance created by Sitecore itself. I decided to go with the second one.

var managerType = typeof(Sitecore.Forms.Data.DataManager);
var managerField = managerType.GetField("provider",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.NonPublic);
var provider = managerField.GetValue(managerType)
as Sitecore.Forms.Data.DataProvider;

The code gets the System.Type for the manager class, then uses that type to extract a reference to the private static field “provider”, which holds a reference to the object we want. Note that since this is a static type, you call GetValue() on the type object itself, rather than on an instance of the type.

Now, looking at the innards of that DataProvider class, it is a bit more helpful. Once it’s loaded the concrete data provider class from configuration, it exposes it in a public property called “InnerProvider”. So we can take that and cast it to its real type:

var innerProvider = provider.InnerProvider
as Sitecore.Forms.Data.DataProviders.WFMDataProvider;

Now note that this is one of the places where this could all go to pot in real life. What if the instance of WFfM you run this code against is using the Oracle data provider, or some other provider type? Well the answer is probably a big messy crash because the cast above will return null. So it makes sense to check if you got the right type back or not. If the cast above returns null then you know this instance of WFfM isn’t configured the way you expect. What you choose to do about that is dependent on what you need this code to do – you could fail gracefully, throw an “unexpected configuration” exception or use an alternative path through the rest of this code that knows about other providers.

But for the sake of blog post simplicity, lets assume that cast worked…

The internals of the WFMDataProvider class include a private field called “shortConnection” which stores the connection string value extracted from the WFfM configuration. So that’s what we need to try and extract:

var innerProviderType = innerProvider.GetType();
var field = innerProviderType.GetField("shortConnection",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
string connectionString = field.GetValue(innerProvider) as string;

And magically, now we have a connection string that can be used with standard SQL connections.

As an aside, having written the code above it turns out that if you want to take a more supportable approach to having access to this connection data and you are able to change the forms.config setup slightly, then WFfM can be configured to get its connection string from the <connectionStrings/> element.

Monday 17 March 2014

Delete aliases along with their items

Last week I looked at how to visualise aliases in the Content Editor, based on a requirement from one of my clients. The second part of the work I was considering here was how you can automatically remove any aliases related to an item when you remove that item. What I wanted to achieve was having the system prompt you to ask if you want the aliases removed whenever you delete an item that has aliases attached. Something like:

image

I bit of digging through docs and Reflector shows that when you delete an item via the UI, Sitecore runs the “uiDeleteItems” pipeline. This performs the series of steps required to verity and then perform the deletion:

<uiDeleteItems>
<processor mode="on" method="CheckPermissions" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="Confirm" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="CheckTemplateLinks" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="CheckCloneLinks" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="CheckLinks" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="CheckLanguage" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="UncloneItems" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="Execute" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
<processor mode="on" method="PostAction" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel"/>
</uiDeleteItems>

Looking at the code behind these components, it seems sensible that if we’re going to extend this pipeline, the “are you sure you want to delete aliases” component needs to go between the “Confirm” step (which prompts to confirm whether you really want to delete the item) and the “CheckTemplateLinks” step. That would allow us to prompt before Sitecore starts the process of deletion. Now as you can probably tell from the config above, the original Sitecore code breaks this sort of “confirm” and “perform” behaviour into multiple pipeline steps. But for the purposes of this example we’ll stick to just the one for simplicity.

So, as we’ve seen with other customisations, we need to start with a basic extension class which will provide our new pipeline step. For this particular pipeline, that class looks like:

public class DeleteExtensions : ItemOperation
{
public void CheckAndDeleteAliases(ClientPipelineArgs args)
{
}
}

and it gets configured with a configuration patch like so:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<processors>
<uiDeleteItems>
<processor patch:after="*[@method='Confirm']" mode="on"
type="Testing.DeleteExtensions ,Testing"
method="CheckAndDeleteAliases"/>
</uiDeleteItems>
</processors>
</sitecore>
</configuration>

Confirming an operation and then optionally performing it in a pipeline requires  pattern of code that we saw in the previous post about confirming commands in the Sitecore UI – the code needs a two step approach where initially we present the UI and the the second time the code is run we detect the results from the UI dialog and act on them. So we need a clause to detect the user saying “no” to alias removal, a clause to detect the user saying “yes” and the code to generate the confirmation dialog. Now interestingly when we have multiple dialogs in a pipeline it turns out we can’t make use of args.IsPostBack directly because it will already be true due to the previous dialog. But the args.Result property is correct when our code runs. Hence we can work around this issue by structuring the code a bit differently here:

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

public void CheckAndDeleteAliases(ClientPipelineArgs args)
{
Assert.ArgumentNotNull(args, "args");

//
// Check for postback data from our dialog
//

// User clicked no - abort the whole deletion
if (args.Result == "no")
{
args.AbortPipeline();
return;
}

// Both further steps require these bits of context data
ListString items = new ListString(args.Parameters["items"], '|');
Database db = getDatabase(args);

// User clicked yes - ok to delete
if (args.Result == "yes")
{
args.Result = string.Empty;
removeAliases(items, db);
return;
}

//
// If we're not handling a postback, check for aliases and present UI
//

// Count the aliases for any items we have as parameters
int aliases = countAliases(items, db);

// If we got any aliases, ask the UI to show a confirm dialog for us
if (aliases > 0)
{
string message;

if(items.Count == 1)
{
Item item = db.GetItem(items[0]);

message = string.Format(
"The item \"{0}\" has {1} alias{2} which will also be {3}." +
" Are you sure?",
item.DisplayName,
aliases,
aliases != 1 ? "es" : string.Empty,
Settings.RecycleBinActive ? "recycled" : "deleted"
);
}
else
{
message = string.Format(
"These {0} items have {1} alias{2} which will also be {3}." +
" Are you sure?",
items.Count,
aliases,
aliases != 1 ? "es" : string.Empty,
Settings.RecycleBinActive ? "recycled" : "deleted"
);
}

Context.ClientPage.ClientResponse.Confirm(message);
args.WaitForPostBack();
}
}

private Database getDatabase(ClientPipelineArgs args)
{
Assert.ArgumentNotNull(args, "args");
Database database = Factory.GetDatabase(args.Parameters["database"]);
Assert.IsNotNull(database, typeof(Database), "Name: {0}", new object[]
{
args.Parameters["database"]
});
return Assert.ResultNotNull<database>(database);
}

private int countAliases(ListString items, Database db)
{
int aliases = 0;

try
{
using (new TaskContext("DeleteItems pipeline - count aliases"))
{
using (new SecurityDisabler())
{
foreach (string item in items)
{
Item itm = db.GetItem(item);
if (itm != null)
{
// Count this one
aliases += countAliases(itm);

// And process any descendent items too
foreach(var descendantItm in itm.Axes.GetDescendants())
{
aliases += countAliases(descendantItm);
}
}
}
}
}
}
catch (Exception ex)
{
Log.Error("Error while counting aliases for items", ex, this);
HttpUnhandledException ex2 =
new HttpUnhandledException(ex.Message, ex);
string htmlErrorMessage = ex2.GetHtmlErrorMessage();
UrlString urlString =
new UrlString("/sitecore/shell/controls/error.htm");
Context.ClientPage.ClientResponse.ShowModalDialog(urlString.ToString(),
htmlErrorMessage);
}

return aliases;
}

private int countAliases(Item item)
{
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, so they can be discarded
}

return aliases;
}
}

So the first time the code runs we count the aliases that might need deletion. This uses a two step process because the parameters for the pipeline give us a list of strings representing the items. So the code goes through those strings, loads each item in turn and counts up the aliases for each one (using a similar query to the one we used in the Alias gutter rendering). One interesting question here is what security context to do the counting with. Ideally we’d do it as the current user – but what happens if the user didn’t create the Alias? For the purposes of this demo code I’m just making use of SecurityDisabler here – but in production code it would be sensible to work out the correct security context to do this under. Though the right answer to that question may well depend on the business rules for your particular site.

If we get more than one alias across all the items we render a confirmation dialog. There’s a bit of code to format the message correctly – depending on whether we have one or more aliases to delete and whether Sitecore is configured to use the Recycle Bin or not. Once we’ve shown the message box we call args.WaitForPostback() to tell the UI that we need to wait for a response.

When the code gets called a second time we check the results. If the result says the user doesn’t want to continue the deletion then we abort the pipeline with a call to args.AbortPipeline(). If the result says that the user says yes to the deletion then we remove the aliases. This code follows a similar pattern to the alias counting code:

    private void removeAliases(ListString items, Database db)
{
try
{
using (new TaskContext("DeleteItems pipeline - remove aliases"))
{
using (new SecurityDisabler())
{
foreach (string item in items)
{
Item itm = db.GetItem(item);
if (itm != null)
{
// process this item
removeAliases(itm);

// And process any descendent items too
foreach (var descendantItm in itm.Axes.GetDescendants())
{
removeAliases(descendantItm);
}
}
}
}
}
}
catch (Exception ex)
{
Log.Error("Error while removing aliases for items", ex, this);
HttpUnhandledException ex2 =
new HttpUnhandledException(ex.Message, ex);
string htmlErrorMessage = ex2.GetHtmlErrorMessage();
UrlString urlString =
new UrlString("/sitecore/shell/controls/error.htm");
Context.ClientPage.ClientResponse.ShowModalDialog(urlString.ToString(),
htmlErrorMessage);
}
}

private void removeAliases(Item item)
{
var aliases = Sitecore.Globals.LinkDatabase.GetReferrers(item)
.Select(l => l.GetSourceItem())
.Where(s => s.TemplateID == aliasID);

foreach (var alias in aliases)
{
if (Settings.RecycleBinActive)
{
Log.Audit(this, "Recycle alias {0} of item {1}",
AuditFormatter.FormatItem(alias),
AuditFormatter.FormatItem(item));
alias.Recycle();
}
else
{
Log.Audit(this, "Delete alias {0} of item: {1}",
AuditFormatter.FormatItem(alias),
AuditFormatter.FormatItem(item));
alias.Delete();
}
}
}

However here, instead of counting the aliases, this code removes them. As mentioned above, this code pays attention to the configuration for whether Sitecore uses the Recycle Bin approach to deletion or not, in the same way the original item deletion code in Sitecore does.

Testing this gives us the dialog box shown in the image at the top of the page, and if we approve the deletion, removes the appropriate aliases. Job’s a good ‘un.

Now whenever I come across blocks of code as similar as the removeAliases(ListString items, Database db) and countAliases(ListString items, Database db) methods I start thinking about how to reduce that down to one method. But I’ll save that for a future discussion...

Monday 10 March 2014

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 somehting 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 “<fully qualified type name>, <assembly name>”.

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.

Wednesday 5 March 2014

Bonus chatter: New ways to browse the .Net Framework source

For a while now it’s been possible to configure Visual Studio to allow you to step into the publicly available source code for the .Net Framework, but a post on Channel 9 today provides information on some new ways to get at this fascinating information MS are releasing.

The one that caught my attention is that MS have released an online browsing tool to allow you to look through the public source in your browser. Perfect for those “why does it do that?” moments you hit every so often when developing.

Take a look for yourself at http://referencesource.microsoft.com/

There’s lots of information about all the features they’re releasing, plans for the future and the ways it can be used on the .Net blog at http://blogs.msdn.com/b/dotnet/archive/2014/02/24/a-new-look-for-net-reference-source.aspx

I’m off for a read…

Tuesday 4 March 2014

Bonus chatter: My favourite proposed feature for C# v6

Having been away on holiday recently, I’ve been doing a bit of catching up on the backlog of RSS-based reading that a week out of the office has generated. And one item stuck out at me as particularly interesting: some of the new features being discussed for the v6 release of the C# compiler. I came across a couple of articles discussing presentations given by Mads Torgersen (the C# Programme Manager) at the end of last year discussing some interesting feature proposals

One thing stuck out at me as being particularly useful to the code I find myself dealing with in Sitecore solutions – something described as “Monadic Null Checking”. A rather technical name for a simple but useful syntax improvement.

When you’re using Model classes for your Sitecore templates, via a mapping tool like Glass, you often find yourself creating models that might look a bit like:

public class ExampleModel
{
    public virtual string Title { get; set; }
    public virtual Image SummaryImage { get; set; }
}

The mapping framework can fill this class in using the data from Sitecore Items – but what happens when that Image field defined here is not filled in by your content editors? Up until now you’ve generally ended up writing code that’s looked a bit like this:

public void DoSomething(ExampleModel model)
{
    if (model.SummaryImage != null)
    {
        // do stuff with the data. For example:
        myImageControl.AlternatText = model.SummaryImage.Alt;
    }
}

You end up littering your code with if() clauses to make sure that the properties of the object are not null before you make use of them. And it’s a common QA issue to find that when you test new code with the sort of edge-case data which content editors sometimes create, you get Null Reference exceptions in places where these guard clauses have been forgotten.

But in the future this could be simpler with a new approach to null-checking. The new “?.” operator is translated by the compiler into exactly the sort of null test we’ve been writing above. So our code becomes something like:

public void DoSomething(ExampleModel model)
{
    // do stuff with the data. For example:
    myImageControl.AlternatText = model.SummaryImage?.Alt;
}

If the value of SummaryImage is null, then the compiler never references the Alt child property – hence preventing the Null Reference issue without the extra code.

While you still have to remember to use this alternative syntax, it’s much shorter and (to me at least) the shorter code is easier to read and understand. Especially where you get into more complex scenarios with nested objects. And it's much less hassle to remember to type "?." every time you access a property than it is to write the whole guard.

Monday 3 March 2014

Next step: Relative data sources…

Having spent a bit of time thinking about relative Data Source Locations last week, it struck me that the logical extension of this is to allow the data sources of components themselves to be relative to the context item. This is particularly useful when you need a branch template, that will include some child items of a page and you want to pre-configure the page’s presentation to display these children via the data sources of UI components.

And happily this is a pretty trivial change for Sublayouts.

It’s a common pattern to develop a base class for sublayouts in order to share code for common tasks like fetching data source values, so it makes perfect sense to extend a bases class like that to manage this behaviour. The first thing it needs to do is find the Sublayout component that exposes the specified data source – which is always the parent object of your UI components. It also makes sense that some components will not need this behaviour, so we’ll create a property that only does any work if we actually use it:

public class BaseSublayout : System.Web.UI.UserControl
{
private Sublayout _sublayout = null;

public Sublayout Sublayout
{
get
{
if (_sublayout == null)
{
_sublayout = this.Parent as Sublayout;
}

return _sublayout;
}
}
}

With that in place, we can now write a similar property for fetching the data source. If the value of the SubLayout object’s DataSource property starts with “./” then it’s relative, and we can substitute the “.” for the Sitecore path of the context item. And we can wrap that behaviour into the same sort of “only calculate it if it’s used” property as we used above:

public class BaseSublayout : System.Web.UI.UserControl
{
private Sublayout _sublayout = null;
private string _dataSource = null;

public Sublayout Sublayout
{
get
{
if (_sublayout == null)
{
_sublayout = this.Parent as Sublayout;
}

return _sublayout;
}
}

public string DataSource
{
get
{
if (_dataSource == null)
{
_dataSource = Sublayout.DataSource;
if (_dataSource.StartsWith("./"))
{
_dataSource = Sitecore.Context.Item.Paths.FullPath
+ _dataSource.Substring(1);
}
}
return _dataSource;
}
}
}

So now if we ask for the DataSource property when the current item is “/sitecore/Content/Home” and the data source for the component’s binding is set to “./Headlines” then the result will be “/sitecore/Content/Home/Headlines”.

You can then use this base class in a simple user control:

public partial class ExampleControl : BaseSublayout
{
protected void Page_Load(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(base.DataSource))
{
Item itm = Sitecore.Context.Database
.GetItem(base.DataSource);
//
// do something with the item
//
}
else
{
//
// component has no data source configured
// possible error state?
//
}
}
}

Interestingly, it turns out that you cannot do the same thing with Renderings easily – code inside Sitecore for displaying renderings detects that the relative data source path “does not exist” and hence hides the rendering. You can see this if you put Page Editor into Debug mode and look through the output that generates.

And it sounds like that could be something to investigate for the future…

Friday 21 February 2014

Improving your Sitecore IA with relative DataSource Locations

As someone famous** once said, with great power comes great responsibility – and the power of Sitecore’s component-based page model puts a lot of responsibility on us developers to create a structure for component data sources that makes sense to content editors. The two most common patterns I find myself using are that of having a “shared content” folder somewhere in the content tree which reusable DataSource items live in, and having items as children of the component’s page. When using the “shared content” folder you can easily set the DataSource Location field for your UI component to point to location where all the relevant data gets filed, but you can’t easily do that if you want to have your DataSource items as children of the page. So you tend to end up leaving the DataSource Location field blank to allow the user to pick the current page as the place to create the new item.

Experience shows that doesn’t work too well in practice. When you don’t control where the DataSource items get stored, they tend to end up getting spread around the content tree and making a bit of a mess of the IA. That lead to some thinking about how we might be able to improve on this situation – is it possible to force the DataSource Location to be relative to the current item?

Well, a bit of digging through the code and some experimentation says it’s not too hard to provide relative DataSource Locations. You can set up a UI component with a relative path for the DataSource Location, and then manually create a folder under your page:

One

This just works! When you click the “Set Associated Content” button, the tree view shows the right folder:

Two

But sadly this doesn’t work in the situation where the folder called “Items” doesn’t exist. In that case, Sitecore tries to deal with the error condition of “that folder doesn’t exist” by setting the root of the tree above to the root of the content tree…. Not so good…

We could deal with this by using a Branch Template – when you create your page that could automatically create the Items folder too. But the maintenance of that approach is a bit tedious because you probably end up with one Branch Template for every one of your Page Templates, and you have to remember to change all your Insert Options to match. What would be much better is if we could magically create the “Items” folder whenever it was needed.

After a bit of research, I discover that when Sitecore puts up the “Select the Associated Content” dialog box, in the background it runs the “getRenderingDatasource” pipeline in order to work out what to show in the tree view. So extending this pipeline should enable us to ensure that the location exists. We can create an extension class by providing a method called “Process” which accepts the correct arguments object:

namespace Testing
{
  public class CreateRelativeDataSourceFolder
  {
    public void Process(GetRenderingDatasourceArgs args)
    {
    }
  }
}

And then we can add it to the pipeline with a quick configuration patch:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getRenderingDatasource>
        <processor patch:before="processor[@type='Sitecore.Pipelines.GetRenderingDatasource.GetDatasourceLocation, Sitecore.Kernel']"
            type="Testing.CreateRelativeDataSourceFolder, Testing"/>
        </getRenderingDatasource>
      </pipelines>
    </sitecore>
  </configuration>

The “patch:before” attribute here tells Sitecore to insert this new item at the start of the pipeline, before Sitecore attempts to load the appropriate item – thus giving us the chance to create it first if necessary.


The first thing we need to do is get the value of the DataSource Location field for the component we’re setting the data source for. The GetRenderingDatasourceArgs parameter that i passed in to our pipeline processor includes a reference to this data – the args.RenderingItem property gives us access to the Sitecore Item for the UI component. So with that item we can grab the value of the field that stores the DataSource Location. We can get the ID of this field from the Sitecore UI, and write a quick bit of code to get the value and check it’s valid.

public class CreateRelativeDataSourceFolder
{
  private static ID DataSourceLocationField = 
             new ID("{B5B27AF1-25EF-405C-87CE-369B3A004016}");
  private static string RelativePath = "./";

  public void Process(GetRenderingDatasourceArgs args)
  {
    string dataSourceLocation = args.RenderingItem
             .Fields[DataSourceLocationField].Value;

    if (string.IsNullOrWhiteSpace(dataSourceLocation))
    {
      return;
    }

    if (!dataSourceLocation.StartsWith(RelativePath))
    {
      return;
    }
  }
}

Once we’ve got the value of the field we check that it’s not empty and that it starts with a “./” relative path. If either of these isn’t true then this pipeline component has nothing to do and we can bail out and let the rest of the pipeline sort things out for us.


With that done, the next step is to work out what the full Sitecore path of our relative item would be, and then check if this item exists in the database. If it does exist then we have nothing to do – we can just return control to the rest of the pipeline.  But if the item doesn’t exist, we can create it. And that means adding a few more lines of code. To create an item you need to have a name for it and to have the Template ID for the sort of item to create. In this case, the name is just the path specified for the DataSourceLocation without the preceding “./” on the front. And the ID for the “Folder” template is easy to find from the Sitecore UI. So that extends our basic code to this:

public class CreateRelativeDataSourceFolder
{
  private static ID DataSourceLocationField = 
      new ID("{B5B27AF1-25EF-405C-87CE-369B3A004016}");
  private static ID FolderTemplateID = 
      new ID("{A87A00B1-E6DB-45AB-8B54-636FEC3B5523}");
  private static TemplateID FolderTemplate =  
      new TemplateID(FolderTemplateID);
  private static string RelativePath = "./";

  public void Process(GetRenderingDatasourceArgs args)
  {
    string dataSourceLocation = args.RenderingItem
      .Fields[DataSourceLocationField].Value;

    if (string.IsNullOrWhiteSpace(dataSourceLocation))
    {
      return;
    }

    if (!dataSourceLocation.StartsWith(RelativePath))
    {
      return;
    }

    if (string.IsNullOrWhiteSpace(args.ContextItemPath))
    {
      return;
    }  

    string subFolderPath = args.ContextItemPath +
       dataSourceLocation.Substring(1);

    if (args.ContentDatabase.GetItem(subFolderPath) != null)
    {
      return;
    }

    Item currentItem = args.ContentDatabase
      .GetItem(args.ContextItemPath);

    if (currentItem == null)
    {
      return;
    }

    string newItemName = dataSourceLocation.Substring(2);

    using (new SecurityDisabler())
    {
      currentItem.Add(newItemName, FolderTemplate);
    }
  }
}

Recompile that, give it a test and now the child folder will be automatically created if it does not exist – success!


** The internet isn’t entirely sure if that was Stan Lee writing Uncle Ben from Spiderman, or Voltaire. Pick whichever you prefer…

Monday 17 February 2014

Confirming Sitecore Commands

Be honest people: how many of you have wasted a few hours of your life by accidentally clicking “Revert Database” when you meant to click “Update Database” after a long evening at the development coal face? I certainly have. And if you’re like me and tend to serialise only the things you’ve changed in your project, then that means you generally end up with a dead instance of Sitecore.

Once is enough for this sort of mistake, and it lead me to thinking about how you can customise the Sitecore UI to prevent these issues in a generic way. What I decided to try was some code to allow you to configure a confirmation dialog before the action of any command on the ribbon. And it turns out it’s not too hard to do.
The starting point for anything triggered by clicking a button in the ribbon is a class which inherits from Sitecore.Shell.Framework.Commands.Command, and they require an Execute() method:

namespace Blog.Examples
{
    public class ConfirmCommand : Command
    {
        public override void Execute(CommandContext context)
        {
        }
    }
}

Whenever you want to make use of a class derived from Command in your ribbon configuration, you need to register it in the Sitecore configuration. And that means a quick configuration patch. Each entry in the commands collection in configuration needs two bits of data. The “type” attribute is a normal .Net type descriptor for the class you’ve implemented, and the “name” attribute is the textual name you will use later when you specify what gets called when a ribbon button is clicked. So we can use example:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="extensions:confirm" 
 type="Blog.Examples.ConfirmCommand,Blog.Examples" />
    </commands>
  </sitecore>
</configuration>

With that done, we need to make the command present some UI. Having done a bit of spelunking through the commands that ship with Sitecore, they share a common pattern where the command launches a pipeline in order to allow the UI to display UI and then process its results:

public class ConfirmCommand : Command
{
    public override void Execute(CommandContext context)
    {
        Sitecore.Context.ClientPage.Start(this, "Confirm", 
context.Parameters);
    }

    protected void Confirm(ClientPipelineArgs args)
    {
    }
}

The next thing to consider is how the code will find out what it is supposed to be confirming. Looking at some of the commands that ship with Sitecore, they get parameters passed from their item in the ribbon in the definition of their click event:

image

Examining the code for these commands shows that the parameters inside the brackets are parsed into the Parameters collection of the commands context object as a name/value collection. A quick test shows that this means you can just wrap the command you want to confirm as a parameter of the confirm command itself. So, thinking this through the confirm command will also need to know what text to put in the confirmation dialog, so we need two parameters:

extensions:confirm(title=revert the database,
         cmd=itemsync:loaddatabase(revert=1))

The “title” parameter optionally provides the text to append to the dialog message, and “cmd” provides the command to be executed. And the code to process this looks like this: (Error handling and validation omitted for clarity)

protected void Run(ClientPipelineArgs args)
{
    string msg;
    if (!string.IsNullOrWhiteSpace(args.Parameters["title"]))
    {
        msg = "Are you sure you want to " + 
args.Parameters["title"] + "?";
    }
    else
    {
        msg = "Are you sure you want to do that?";
    }

    SheerResponse.Confirm(msg);
    args.WaitForPostBack();
}

The call to args.WaitForPostBack() signals to the internals of Sitecore that this code wants to know the response from the dialog box that we’ve displayed. What happens as a result of that is when the user makes their click our code gets run again, passing in a new parameter to say what button got clicked. So we need to refactor this method slightly to detect that we’ve had a “postback” from the dialog box, and process it appropriately. That makes our Run() method look like this:

protected void Run(ClientPipelineArgs args)
{
    if (args.IsPostBack)
    {
        if (args.Result == "yes")
        {
            string cmd = args.Parameters["cmd"];
            Sitecore.Context.ClientPage.SendMessage(this, cmd);
            return;
        }

        if (args.Result == "no")
        {
            return;
        }
    }
    else
    {
        string msg;
        if (!string.IsNullOrWhiteSpace(args.Parameters["title"]))
        {
            msg = "Are you sure you want to " + 
args.Parameters["title"] + "?";
        }
        else
        {
            msg = "Are you sure you want to do that?";
        }

        SheerResponse.Confirm(msg);
        args.WaitForPostBack();
    }
}

Simple enough change. When the code is run for the first time, we display the dialog. But on the second pass through we check the args.Result value. If the user does not confirm the action then we just return and do nothing else. But if they confirm that they do want the command to run, we use the SendMessage() API call to tell Sitecore to start the original command.

So all that’s left to do is to configure a ribbon button with the confirm command and try it out. The buttons in the Content Editor ribbon are defined in the Core database, at:

/sitecore/content/Applications/Content Editor/Ribbons/Chunks

So you can find your required ribbon button in there and adjust its configuration by wrapping the existing command with the new confirm command. To avoid messing things up while I’m testing, I’ve created a new ribbon button that wraps up the Revert Item command for testing purposes:

image

And with that saved, the ribbon updates and you can test out the command:

image

Success – and we can now wrap any command in the ribbon with a confirmation.

Bootstrapping…

This blog is a challenge to myself to try and write something technical once a week for a year.
Writing Sitecore and website code for a living means I keep looking at things and thinking “that would be of interest to others” but up until now I’ve never been organised enough to get stuff written down and published. So as an experiment I’m going to find at least one thing a week to write about.
Part experiment to see if I can do it, and part exercise in personal development. And part reason to stop sitting in front of the telly in the evenings playing games.
So, place your bets Ladies and Gents – how many weeks will I manage?