For a site that i'm doing at the moment i needed a clean up features for datasource items.
This is not default implemented in sitecore and it's not always you want sitecore to delete datasource items for you.
It really depends on that whole concept behind the site and how the editors use datasource items.
Anyway, for this site vi use many different kinds of renderings and therefore have a lot of datasource items. The site consists of very few, but also very long pages.
The idea was to use renderings and thereby keep the navigation level (tree structure) to a maximum of 3 levels.
I did this before on http://www.beoplay.com and i'm doing it again for the current project. Once the site goes live, i will be more specific about what site i' m talking about.
Using renderings is a good idea. It's page editor freindly and allows for a very easy personalisation and extensive DMS usage. It makes it easy to do response implementations, A/B split testing and reuse content. But it also means creating a lot of Datasource items outside the site structure.
That is as such not a problem, but it WILL lead to a lot of garbage datasource items if you don't clean up your solution.
Sitecore will create the datasource items for you, but when your editors delete a rendering, sitecore will not delete the datasource item again.
So for this project we added a handler and bound it to the ItemSaved event. It will look at the renderings, find the datasource items for these renderings and delete them if they are not used anymore.
Just wanted to share the code to do this with you guys.
As always, comments or questions are welcome.
Best Regards
Lasse Rasch
public class ItemSavedHandler
{
/// <summary>
/// Intercepts item:saved event on sitecore item save. Checks if datasource values have been removed for each sublayout of the item.
///
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnItemSaved(object sender, EventArgs args)
{
var args1 = (SitecoreEventArgs)args;
var itemChanges = (ItemChanges)args1.Parameters[1];
if (itemChanges.HasFieldsChanged)
{
var layoutField = itemChanges.FieldChanges.OfType<FieldChange>().SingleOrDefault(f => f.FieldID == Sitecore.FieldIDs.LayoutField);
if (layoutField == null) return; // presentation hasn't changed
string originalLayoutFieldValue = layoutField.OriginalValue;
string newLayoutFieldValue = layoutField.Value;
var originalLayoutDefinition = LayoutDefinition.Parse(originalLayoutFieldValue);
var newLayoutDefinition = LayoutDefinition.Parse(newLayoutFieldValue);
var originalRenderings = originalLayoutDefinition.GetDevice(Constants.Sitecore.LayoutDefinitionIDs.Default).Renderings.OfType<RenderingDefinition>().ToArray();
var newRenderings = newLayoutDefinition.GetDevice(Constants.Sitecore.LayoutDefinitionIDs.Default).Renderings.OfType<RenderingDefinition>().ToArray();
// find renderings in originalRenderings collection that do not exist in newRenderings
var removedRenderings =
originalRenderings.Where(
candidateRendering => newRenderings.All(otherRendering => otherRendering.UniqueId != candidateRendering.UniqueId)).ToArray();
foreach (var removedRendering in removedRenderings)
{
var datasourceValue = removedRendering.Datasource;
if (string.IsNullOrEmpty(datasourceValue))
{
var property = removedRendering.DynamicProperties.SingleOrDefault(dp => dp.Name == Constants.Sitecore.DynamicPropertyNames.DataSource);
if (property != null)
{
datasourceValue = property.Value;
}
}
if (string.IsNullOrEmpty(datasourceValue))
{
return;
}
var datasourceItem = Sitecore.Context.ContentDatabase.GetItem(datasourceValue);
//check if item is references elsewhere
var itemReferers = Globals.LinkDatabase.GetItemReferrers(datasourceItem, true);
if (itemReferers.Any())
{
return; //item is referenced return. DO NOT DELETE.
}
datasourceItem.Delete();
}
}
}
}
Thank you for sharing your code. For it to work on Sitecore XP 8 a small modification is necessary:
SvarSletvar layoutField = itemChanges.FieldChanges.OfType().SingleOrDefault(f => f.FieldID == Sitecore.FieldIDs.LayoutField);
-change to-
var layoutField = itemChanges.FieldChanges.OfType().SingleOrDefault(f => f.FieldID == FieldIDs.FinalLayoutField);