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.