First of all I would like to thank everyone who participated! All in all 10 persons participated with 47 entries. Further we would like to apologize for the delay in publishing these articles. We will do our best to follow up a little faster in the future.
The participants in the competition are as follows:
If I got the names, blogs or anything wrong, please let me know and I will correct them as quickly as possible.
In these articles we will publish the different entries. I have tried and group them by functionality, so that for instance Item extensions concerning publishing is presented together.
Publishing
First we are going to provide Item extensions and a couple of persons have provided extensions that revolve around publishing. The general idea seems to partly be to move publishing API to the Item class to make it simpler to publish an item. Both Rasmus Rasmussen and Anders Laub contributed with publishing and unpublishing mechanisms, and they both look something similar to this:
public static void Publish(this Item item, bool deep)
{
var publishOptions = new PublishOptions(item.Database,
Database.GetDatabase("web"),
PublishMode.SingleItem,
item.Language,
DateTime.Now);
var publisher = new Publisher(publishOptions);
publisher.Options.RootItem = item;
publisher.Options.Deep = deep;
publisher.Publish();
}
public static void UnPublish(this Item item)
{
item.Publishing.UnpublishDate = DateTime.Now;
item.Publish(true);
}
This should make it a little easier to publish as you can now do item.publish(false). Anders Laub has taken this a little further and addressed the issue of how to publish a deleted item. The issue is that you have to publish the parent of the deleted item including children. This can be a little abstract for editors to understand, but with the following extension you have the opportunity to handle it on deletion time of the item:
public static void DeleteAndPublish(this Item item)
{
var parent = item.Parent;
item.Delete();
parent.Publish(true);
}
Now this will first delete it and then start the publishing process on the parent. Remember to run this with an appropriate user having the right security access or use a SecurityDisabler.
Ronald Niewenuis have further contributed with this method, which tests whether an item is published or not:
public static bool IsPublished(this Item pItem)
{
Database lWebDb = Factory.GetDatabase("web");
if (pItem != null && lWebDb != null)
{
Item lWebItem = lWebDb.GetItem(pItem.ID, pItem.Language, pItem.Version);
if (lWebItem == null || pItem.Statistics.Updated > lWebItem.Statistics.Updated)
{
return false;
}
}
return true;
}
This method does not only test if an item is in the web database. It also checks if the item in the master database is newer. In that way you can test if the item needs to be published. Similar functions were contributed by Bo Breiting.
Bo Breiting has also contributed with a method, which tests whether an item has publishing restrictions, which will cause it to be published or not:
public static Boolean IsValid(this Item item)
{
if (item == null)
return false;
CheckboxField hideVersion = item.Fields["__Hide version"];
if (hideVersion.Checked)
return false;
DateTime validFrom = !String.IsNullOrEmpty(item["__Valid from"]) ? DateUtil.IsoDateToDateTime(item["__Valid from"]) : DateTime.MinValue;
DateTime validTo = !String.IsNullOrEmpty(item["__Valid to"]) ? DateUtil.IsoDateToDateTime(item["__Valid to"]) : DateTime.MaxValue;
return DateTime.Now >= validFrom && DateTime.Now <= validTo;
}
Item Relations
There have also been a few entries on how item relates to each other. I think this is really clever, as the Sitecore API can be a bit tricky here and you can easily make mistakes, which might result in a serious performance issue.
The first method is from Bo Breiting. Here he provides a method of getting parents of a given item. The cool thing is that you can actually reverse the order of the items by an optional parameter. I can see how this might come in handy, when you are building navigation on your website:
public static IEnumerable<Item> ParentList(this Item item, Boolean descending = true)
{
if (item == null)
return null;
List<Item> parents = new List<Item>();
Item currentItem = item.Parent;
while (currentItem != null)
{
parents.Add(currentItem);
currentItem = currentItem.Parent;
}
if (descending)
parents.Reverse();
return parents;
}
Now this next method is a really great idea. It was both provided by Anders Laub and Varun Shringarpure. With the method it is really easy to get referrers of an item using the link database. This means that you should not worry about the performance impact and it is much easier to use the functionality, when is available directly on the Item class:
public static IEnumerable<Item> GetReferrersAsItems(this Item item)
{
var links = Globals.LinkDatabase.GetReferrers(item);
return links.Select(i => i.GetTargetItem()).Where(i => i != null);
}
Now you can easily get linking items by calling item.GetReferrersAsItems(); Brilliant!
Templates
There have been quite a few entries revolving around templates. This is probably because the Sitecore API lacks a little in this area. For instance there is no single method call to test if an item derives from a template. I guess that is why we have received entries from Keith Wood, Bo Breiting and Varun Shringarpure. The Item is derived functionality comes in different versions, where some take advantage of caching. The simpler implementation looks something like this:
public static bool IsDerived(this Item item, ID templateId)
{
if (item == null)
return false;
if (templateId.IsNull)
return false;
TemplateItem templateItem = item.Database.Templates[templateId];
bool returnValue = false;
if (templateItem != null)
{
Template template = TemplateManager.GetTemplate(item);
returnValue = template != null && template.ID == templateItem.ID || template.DescendsFrom(templateItem.ID);
}
return returnValue;
}
If you want to apply caching, you can follow Keiths example:
private static readonly ReaderWriterLockSlim TemplateDescendentsIdentityLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private static readonly SortedList<string, Guid[]> TemplateDescendentsIdentityCache = new SortedList<string, Guid[]>(StringComparer.InvariantCultureIgnoreCase);
internal static IEnumerable<Guid> GetTemplateDescendentIds(Guid templateId, Database sitecoreDatabase)
{
Guid[] identities;
string key = sitecoreDatabase.Name + templateId;
bool success = false;
TemplateDescendentsIdentityLock.EnterReadLock();
try
{
success = TemplateDescendentsIdentityCache.TryGetValue(key, out identities);
}
finally
{
TemplateDescendentsIdentityLock.ExitReadLock();
}
if (!success)
{
Template template = TemplateManager.GetTemplate(new ID(templateId), sitecoreDatabase);
List<Guid> list = template != null ? template.BaseIDs.Select(id => id.Guid).ToList() : new List<Guid>();
identities = list.Union(list.Where(a => a != templateId).SelectMany(o => GetTemplateDescendentIds(o, sitecoreDatabase))).ToArray();
TemplateDescendentsIdentityLock.EnterWriteLock();
try
{
TemplateDescendentsIdentityCache[key] = identities;
}
finally
{
TemplateDescendentsIdentityLock.ExitWriteLock();
}
}
return identities;
}
public static bool IsInstanceOfTemplate(this Item sitecoreItem, Guid templateId)
{
Guid itemTemplateId = sitecoreItem.TemplateID.Guid;
if (itemTemplateId == templateId)
{
return true;
}
return GetTemplateDescendentIds(itemTemplateId, sitecoreItem.Database).Any(descendantId => descendantId == templateId);
}
Now you can use these methods to further extend the API. Bo has submitted an extension method, where you can retrieve all children that derive from a certain template:
public static IEnumerable<Item> ChildrenDerivedFrom(this Item item, ID templateId)
{
ChildList children = item.GetChildren();
List<Item> childrenDerivedFrom = new List<Item>();
foreach (Item child in children)
{
if (child.IsDerived(templateId))
childrenDerivedFrom.Add(child);
}
return childrenDerivedFrom;
}
Finally there is a little extension method submitted by Varun, which identifies if a given item is a template:
public static bool IsTemplate(this Item item)
{
return item.Database.Engines.TemplateEngine.IsTemplatePart(item);
}
I think that the many great extensions give better access to the template information of an item. If you haven’t already got the extension, that tests whether an item inherits from a template, you should put it in your toolbox right away!
Sites
Only one item extension concerns sites (as in SiteContext), but it is quite useful in multisite scenarios. Often you stumble into an issue, where you want to determine if a given item is part of a particular site. Bo Breiting has come up with an excellent extension method for doing this test. It takes the current SiteContext and tests if an item is a part of the hierarchy of the site.
public static Boolean IsInContextSite(this Item item)
{
if (item == null)
return false;
Assert.IsNotNull(Context.Site, "Sitecore.Context.Site required by the Item.IsInSite extension is null");
Assert.IsNotNullOrEmpty(Context.Site.RootPath, "Sitecore.Context.Site.RootPath required by the Item.IsInSite extension is null or empty");
Assert.IsNotNull(Context.Database, "Sitecore.Context.Database required by the Item.IsInSite extension is null");
Item rootItem = Context.Site.RootItem();
Assert.IsNotNull(rootItem, String.Format("Unable to retrieve the item at path {0} using the database {1}", Context.Site.RootPath, Context.Database.Name));
Item currentItem = item;
while (currentItem != null)
{
if (currentItem.ID.Guid.Equals(rootItem.ID.Guid))
return true;
currentItem = currentItem.Parent;
}
return false;
}
Languages and versioning
Anders Laub, Varun Shringarpure and Kiran Patil have all contributed with extensions, which let you determine if an Item has a version in a given language, which could be quite useful in bilingual sites.
public static bool HasLanguage(this Item item, string languageName)
{
return ItemManager.GetVersions(item, LanguageManager.GetLanguage(languageName)).Count > 0;
}
public static bool HasLanguage(this Item item, Language language)
{
return ItemManager.GetVersions(item, language).Count > 0;
}
Varun Shringarpure has also contributed a method which gives the amount of versions a given item has in a given language:
public static int LanguageVersionCount(this Item item, Language lang)
{
if (item == null)
return 0;
Item currentItem = item.Database.GetItem(item.ID, lang);
if (currentItem.Versions.Count > 0)
return currentItem.Versions.Count;
else
return 0;
}
These methods are quite useful as the ItemManager.GetContentLanguages doesn’t handle versions all that well.
We will post the rest of the extension methods and the winner in the next part.