Introduction

One of the areas where the SharePoint 2007 platform (both WSS 3.0 and MOSS 2007) still needs to improve is deployment despite all the major improvements made when compared to its antecessor, the 2003 version. One of the faults I ran recently into is related with updating list content types event handlers when site content types event handlers are updated.

For those who don’t know, let’s start by a brief introduction about content types and explain the difference between a site content type and a list content type. Content types are a new concept introduced in SharePoint 2007 and is basically a reusable collection of settings that can be applied to a certain category of content. Content types basically define the metadata (they are composed by a reusable set of columns called site columns) and behaviors (event handlers, workflows, etc) of a certain category of content. They are stored centrally and are reusable throughout a site or site collection depending on its scope. For more information about content types please click here.

So what is the difference between a site content type and a list content type? Probably, the easiest way to explain this is to think of site content types as templates (in the sense that they are reusable) and list content types as instances of those templates. Site content types are defined at the site level and are visible to the site where they are created and all its subsites (this concept is called content type scope) and they are reusable and not bound to any specific list, meaning that they can be associated with any list within the sites under its scope. When a site content type is associated with a list, a copy of the site content type is copied into the list, creating a new instance. This instance is called a list content type.

Example

The best approach to deploy content types is to use features (for more information about deploying site content types as a feature please click here). Registering content type event handlers can also be achieved using a feature (check this article for more information) or by using Patrick Tisseghem’s Event Handler Explorer. The problem is when there are already list content types based on a site content type and we need to add a new event handler to it and have all list content types also updated. In my experience, both using the feature model approach or using Patrick’s application didn’t update the list content types. The remainder of this post will show you how to do this programmatically.

The following example basically uses a console application to register a event handler and associate it to a content type.

class Program
{
    private const string URL = "http://myIntranet";
    private const string CONTENT_TYPE = "MyContentType";
    private const string EVENT_RECEIVER_NAME = "MyEventReceiver";
    private const string EVENT_RECEIVER_ASSEMBLY =
        "MyEventReceivers.MyEventReceiver, MyEventReceiver, " +
        "Version=1.0.0.0, Culture=neutral, PublicKeyToken=5989549632a535cf";
    private const string EVENT_RECEIVER_CLASS_NAME = 
        "MyEventReceivers.MyEventReceiver";

    static void Main(string[] args)
    {
        //open site collection
        using (SPSite siteCollection = new SPSite(URL))
        {
            using (SPWeb site = siteCollection.OpenWeb())
            {
                //get content type
                SPContentType contentType = 
                    site.ContentTypes[CONTENT_TYPE];
                // add ItemUpdating event handler
                ContentTypeHelper.AddEventHandler(contentType, 
                    EVENT_RECEIVER_NAME, EVENT_RECEIVER_ASSEMBLY, 
                    EVENT_RECEIVER_CLASS_NAME, 10000, 
                    SPEventReceiverInstallerType.ItemUpdating);

                site.Update();
            }
        }
    }
}
internal class ContentTypeHelper
{
/// <summary>
/// Register an event handler in a list
/// </summary>
/// <param name="contentType">SharePoint Content Type</param>
/// <param name="receiverName">Event receiver name</param>
/// <param name="className">Event handler class name</param>
/// <param name="sequenceNumber">Sequence number</param>
public static void AddEventHandler(SPContentType contentType, 
string receiverName, string assemblyName, 
string className, Int32 sequenceNumber, 
SPEventReceiverInstallerType eventReceiverInstallerType)
{
//get assembly full name
string assemblyFullName = System.Reflection.Assembly
.GetAssembly(Type.GetType(assemblyName)).FullName;
// remove existing event handlers
RemoveEventHandlers(contentType, assemblyFullName);
SPEventReceiverDefinition receiverDefinition = null;
//ItemAdding event handler
if (SPEventReceiverInstallerTypeHelper.CheckOption
(eventReceiverInstallerType, 
SPEventReceiverInstallerType.ItemAdding))
{
// add ItemAdding event handler
receiverDefinition = contentType.EventReceivers.Add();
receiverDefinition.Name = receiverName;
receiverDefinition.Type = 
SPEventReceiverType.ItemAdding;
receiverDefinition.Assembly = assemblyFullName;
receiverDefinition.Class = className;
receiverDefinition.SequenceNumber = sequenceNumber;
receiverDefinition.Update();
}
//ItemAdded event handler
if (SPEventReceiverInstallerTypeHelper.CheckOption
(eventReceiverInstallerType, 
SPEventReceiverInstallerType.ItemAdded))
{
// add ItemAdded event handler
receiverDefinition = contentType.EventReceivers.Add();
receiverDefinition.Name = receiverName;
receiverDefinition.Type = 
SPEventReceiverType.ItemAdded;
receiverDefinition.Assembly = assemblyFullName;
receiverDefinition.Class = className;
receiverDefinition.SequenceNumber = sequenceNumber;
receiverDefinition.Update();
}
//ItemUpdating event handler
if (SPEventReceiverInstallerTypeHelper.CheckOption
(eventReceiverInstallerType, 
SPEventReceiverInstallerType.ItemUpdating))
{
// add ItemUpdating event handler
receiverDefinition = contentType.EventReceivers.Add();
receiverDefinition.Name = receiverName;
receiverDefinition.Type = 
SPEventReceiverType.ItemUpdating;
receiverDefinition.Assembly = assemblyFullName;
receiverDefinition.Class = className;
receiverDefinition.SequenceNumber = sequenceNumber;
receiverDefinition.Update();
}
//ItemUpdated event handler
if (SPEventReceiverInstallerTypeHelper.CheckOption
(eventReceiverInstallerType, 
SPEventReceiverInstallerType.ItemUpdated))
{
// add ItemUpdating event handler
receiverDefinition =contentType.EventReceivers.Add();
receiverDefinition.Name = receiverName;
receiverDefinition.Type = 
SPEventReceiverType.ItemUpdated;
receiverDefinition.Assembly = assemblyFullName;
receiverDefinition.Class = className;
receiverDefinition.SequenceNumber = sequenceNumber;
receiverDefinition.Update();
}
//ItemCheckingIn event handler
if (SPEventReceiverInstallerTypeHelper.
CheckOption(eventReceiverInstallerType, 
SPEventReceiverInstallerType.ItemCheckingIn))
{
// add ItemCheckingIn event handler
receiverDefinition = contentType.EventReceivers.Add();
receiverDefinition.Name = receiverName;
receiverDefinition.Type = 
SPEventReceiverType.ItemCheckingIn;
receiverDefinition.Assembly = assemblyFullName;
receiverDefinition.Class = className;
receiverDefinition.SequenceNumber = sequenceNumber;
receiverDefinition.Update();
}
//ItemCheckedIn event handler
if (SPEventReceiverInstallerTypeHelper.CheckOption
(eventReceiverInstallerType, 
SPEventReceiverInstallerType.ItemCheckedIn))
{
// add ItemCheckedIn event handler
receiverDefinition = contentType.EventReceivers.Add();
receiverDefinition.Name = receiverName;
receiverDefinition.Type = 
SPEventReceiverType.ItemCheckedIn;
receiverDefinition.Assembly = assemblyFullName;
receiverDefinition.Class = className;
receiverDefinition.SequenceNumber = sequenceNumber;
receiverDefinition.Update();
}
//update content type - setting the updateChildren 
//parameter to true will update list content types
contentType.Update(true);
}
/// <summary>
/// Remove an event handler registration from a list.
/// </summary>
/// <param name="list">SharePoint list</param>
/// <param name="receiverClass">Event receiver class name</param>
internal static void RemoveEventHandlers(
SPContentType contentType, 
string receiverClass)
{
SPEventReceiverDefinition receiverDefinition = null;
// cycle through all the receiver definitions and 
//delete the receiver with the supplied class name
for (int i = contentType.EventReceivers.Count - 1;
i >= 0; i--)
{
receiverDefinition = contentType.EventReceivers[i];
if (receiverDefinition.Class == receiverClass)
{
receiverDefinition.Delete();
}
}
}
}

The statement that does the trick is "contentType.Update(true);" in the end of the AddEventHandler method. Setting the updateChildren parameter of this method to true will update list content types.

LEAVE A REPLY

Please enter your comment!
Please enter your name here