Level: 200
 

Sitecore Extension Methods – Part 1

TrophyThis article contains the first part of the entries submitted in the Sitecore Extension Method Competition. In this part we will especially focus on Item extensions.
Written by: Jens Mikkelsen
Sun, Dec 18 2011

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.

 

 

 

 

 

 

 

 

 

 

 

 

 

Please rate this article


1 rates / 5 avg.

  • About the author:

    Jens Mikkelsen

    Jens Mikkelsen is a partner at Inmento Solutions a Sitecore consulting firm. He works as a Sitecore specialist and consulting helping clients architect and build quality Sitecore solutions using the newest modules and tools. 

    Further he has been deeply envolved in various complex solutions and has built up a strong knowledge of Sitecore architecture and best practices. He has especially focused on and is specialized in debugging and analyzing Sitecore solutions.

     

    Jens is very interested in the technical mechanisms in the new marketing products such as Sitecore DMS and Sitecore ECM.

    My Sitecore Freelance CV

6 responses to "Sitecore Extension Methods – Part 1"

Nice collection. Most of these are very basic though, so I don't understand why Sitecore doesn't have methods like these in the API.

I don't like the ParentList method, by the way. It should be called AncestorList (the ancestors are the parent, grandparent, etc. of an item). And Sitecore already has the item.Axes.GetAncestors() method for that.
Posted: Monday, December 19, 2011 10:35 AM
Hi Robin

I completely agree with you. The ParentList should have used item.Axes.GetAncestors().

In fact when I did a Sitecore documentation lookup (aka Reflector) it turns out that the code is almost similar

public Item[] GetAncestors()
{
List<Item> list = new List<Item>();
Item parent = this._item;
while (parent.Parent != null)
{
parent = parent.Parent;
list.Add(parent);
}
list.Reverse();
return list.ToArray();
}

I guess it is just my past horrific performance experiences with Axes that prevented me from thinking along those terms.

Also I finaly got around to read Uncle Bob's excellent book on Clean Code where he dictates that using boolean flags is a bad and evil thing because it breaks the single responsibility principle and directly signals that the function does more than one thing.

Last but not least I am not sure if I like the fact, that I return a null with no explicit warning. It probably should just have been an empty list.

However I don't like the way that Sitecore creates a list and then throws it away by returning a fixed array. By default I simply gave up on arrays a long time as something from the past best left there. But using GetAncestors() internally would make little sense if I were to convert the list that Sitecore threw away to a new IEnumerable on the way out. So I guess in the end it comes down to personal preferences.

One thing is for sure the naming is off and the use of the boolean flag could have been made more explicit by splitting the method in two as AncestorsAscending() and AncestorsDescending().


Posted: Tuesday, December 20, 2011 9:49 AM
Hi Bo

I agree. But I'm always a bit in doubt on the point of returning null instead of an empty list.

In general, I would always want an empty list. Because that saves me from doing null checks all the time.

But the behaviour of a repeater convinced me to prefer returning null (even though I don't like that behaviour). An empty list causes a repeater to render the header and footer, where null causes it to leave them out.
Posted: Tuesday, December 20, 2011 10:21 AM
IsInContextSite is referencing Context.Site.RootItem that is not present i 6.5 a replacement would be:

Item rootItem = Context.Site.Database.Items[Context.Site.RootPath];

Regards,
Jan
Posted: Wednesday, December 21, 2011 10:52 AM
Hi Jens Mikkelsen,
Sincere Thanks for this post!
Just one thing. Please correct the spelling of the company name mentioned in mine and Kiran Patil's Introduction above...

Its "Investis" and not "Inventis"

Thanks and Regards,
Varun
Posted: Sunday, December 25, 2011 12:15 AM
Hi,
I have a problem in updating a particular child data in gridview.
Please give some hints
Posted: Monday, January 02, 2012 2:51 PM

Leave a reply


Notify me of follow-up comments via email.
 
 
#nbsp;