MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Saturday, September 13, 2008

Adding Event Receivers using STSADM

A few months ago I created a CodePlex project that allows you to configure a library to allow the storing of web part page history.  The project at its core used three item event receivers to listen to certain key events and either backup the page or restore the page based on the event.  Recently I was thinking about what might be the best way to allow people to script the settings that enable the storing of page history.  I decided that I would start off by providing a simple STSADM command that would allow you to script the adding of event receivers to a list (fortunately making it work for webs and content types didn't really require any additional work so I covered those as well).  The new command I created is called gl-addeventreceiver.

I'll probably eventually come up with something else but this at least is a start - I primarily don't like it because it requires that people know how the web part history project works so I'll probably create a command specific to enabling the history so that it abstracts out the technical details which could always change (there just hasn't been a whole lot of interest in the project so I've not really spent much time on it).  In the meantime this command is a nice generic tool that could be used by developers and administrators for various reasons (I'll probably create an enum and remove command to supplement this one - just haven't gotten around to it yet).

Adding an event receiver via code is really very simple - just a matter of calling the Add method of the SPEventReceiverDefinitionCollection object which you can get via the EventReceivers property of either the SPList, SPContentType, or SPWeb object.  To get an existing event receiver (to check for existence as we don't want to add twice) we have to loop through the collection comparing the assembly name, class name, and event receiver type.

   1: /// <summary>
   2: /// Adds an event receiver to the specified target
   3: /// </summary>
   4: /// <param name="url">The URL.</param>
   5: /// <param name="contentTypeName">Name of the content type.</param>
   6: /// <param name="target">The target.</param>
   7: /// <param name="assembly">The assembly.</param>
   8: /// <param name="className">Name of the class.</param>
   9: /// <param name="type">The type.</param>
  10: /// <param name="sequence">The sequence.</param>
  11: /// <param name="name">The name.</param>
  12: public static void Add(string url, string contentTypeName, TargetEnum target, string assembly, string className, SPEventReceiverType type, int sequence, string name)
  13: {
  14:     using (SPSite site = new SPSite(url))
  15:     using (SPWeb web = site.OpenWeb())
  16:     {
  17:         SPEventReceiverDefinitionCollection eventReceivers;
  18:         if (target == TargetEnum.List)
  19:         {
  20:             SPList list = Utilities.GetListFromViewUrl(web, url);
  21:  
  22:             if (list == null)
  23:             {
  24:                 throw new Exception("List not found.");
  25:             }
  26:             eventReceivers = list.EventReceivers;
  27:         }
  28:         else if (target == TargetEnum.Site)
  29:             eventReceivers = web.EventReceivers;
  30:         else
  31:         {
  32:             SPContentType contentType = null;
  33:             try
  34:             {
  35:                 contentType = web.AvailableContentTypes[contentTypeName];
  36:             }
  37:             catch (ArgumentException)
  38:             {
  39:             }
  40:             if (contentType == null)
  41:                 throw new SPSyntaxException("The specified content type could not be found.");
  42:  
  43:             eventReceivers = contentType.EventReceivers;
  44:         }
  45:         SPEventReceiverDefinition def = Add(eventReceivers, type, assembly, className, name);
  46:         if (sequence >= 0)
  47:         {
  48:             def.SequenceNumber = sequence;
  49:             def.Update();
  50:         }
  51:     }
  52: }
  53:  
  54: /// <summary>
  55: /// Adds an event receiver to a the specified event receiver definition collection.
  56: /// </summary>
  57: /// <param name="eventReceivers">The event receivers.</param>
  58: /// <param name="eventReceiverType">Type of the event receiver.</param>
  59: /// <param name="assembly">The assembly.</param>
  60: /// <param name="className">Name of the class.</param>
  61: /// <param name="name">The name.</param>
  62: /// <returns></returns>
  63: private static SPEventReceiverDefinition Add(SPEventReceiverDefinitionCollection eventReceivers, SPEventReceiverType eventReceiverType, string assembly, string className, string name)
  64: {
  65:     if (GetEventReceiver(eventReceivers, eventReceiverType, assembly, className) == null)
  66:     {
  67:         eventReceivers.Add(eventReceiverType, assembly, className);
  68:         SPEventReceiverDefinition def = GetEventReceiver(eventReceivers, eventReceiverType, assembly, className);
  69:         if (!string.IsNullOrEmpty(name))
  70:         {
  71:             def.Name = name;
  72:             def.Update();
  73:         }
  74:         return def;
  75:     }
  76:     return null;
  77: }
  78:  
  79: /// <summary>
  80: /// Gets the event receiver.
  81: /// </summary>
  82: /// <param name="eventReceivers">The event receivers.</param>
  83: /// <param name="eventReceiverType">Type of the event receiver.</param>
  84: /// <param name="assembly">The assembly.</param>
  85: /// <param name="className">Name of the class.</param>
  86: /// <returns></returns>
  87: private static SPEventReceiverDefinition GetEventReceiver(SPEventReceiverDefinitionCollection eventReceivers, SPEventReceiverType eventReceiverType, string assembly, string className)
  88: {
  89:     foreach (SPEventReceiverDefinition erd in eventReceivers)
  90:     {
  91:         if (erd.Assembly == assembly && erd.Class == className && erd.Type == eventReceiverType)
  92:         {
  93:             return erd;
  94:         }
  95:     }
  96:     return null;
  97: }

The help for the command is shown below:

C:\>stsadm -help gl-addeventreceiver

stsadm -o gl-addeventreceiver


Adds an event receiver to a list, web, or content type.

Parameters:
        -url <web or list URL>
        -assembly <assembly>
        -class <class name>
        -type <itemadding | itemupdating | itemdeleting | itemcheckingin | itemcheckingout | itemuncheckingout | itemattachmentadding | itemattachmentdeleting | itemfilemoving | fieldadding | fieldupdating | fielddeleting | sitedeleting | webdeleting | webmoving | itemadded | itemupdated | itemdeleted | itemcheckedin | itemcheckedout | itemuncheckedout | itemattachmentadded | itemattachmentdeleted | itemfilemoved | itemfileconverted | fieldadded | fieldupdated | fielddeleted | sitedeleted | webdeleted | webmoved | emailreceived | contextevent | invalidreceiver>
        -target <site | list | contenttype>
        [-contenttype <content type name if target is ContentType>]
        [-sequence <sequence number>]
        [-name <the name to give to the event receiver>]

The following table summarizes the command and its various parameters:

Command Name Availability Build Date
gl-addeventreceiver WSS v3, MOSS 2007 Released: 9/13/2008

Parameter Name Short Form Required Description Example Usage
url   Yes The URL to the web or list to add the event receiver to. -url http://portal/pages
assembly a Yes The fully qualified assembly name containing the event receiver class to add. -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08"

-a "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08"
class c Yes The fully qualified class name of the event receiver to add. -class Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver

-c Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver
type   Yes The event type to add.  The command does not validate that you are adding the correct type for the specified target or that the specified class contains handlers for the type specified. -type itemupdated
target   No The target type to add the event receiver to.  Must be either "list", "site", or "contenttype".  If omitted defaults to "list". -target list
contenttype ct No, unless target is contenttype The name of the content type to add the event receiver to if the target is contenttype. -contenttype "Page"

-ct "Page"
sequence seq No The sequence number specifies the order of execution of the event receiver. -sequence 1000

-seq 1000
name n No The name to give to the event receiver.  The name has no significance but can be useful when later listing the event receivers. -name "Handle Saving of Page History"

-n "Handle Saving of Page History"

The following is an example of how to add three event receivers to a pages library - the three commands illustrated below constitute the required event receivers that must be enabled to turn on the web part page history:

stsadm -o gl-addeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -class "Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver" -target list -sequence 1000 -type itemcheckedin

stsadm -o gl-addeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -class "Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver" -target list -sequence 1000 -type itemupdating

stsadm -o gl-addeventreceiver -url http://portal/pages -assembly "Lapointe.WebPartPageHistory, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3216c23aba16db08" -class "Lapointe.WebPartPageHistory.ListEventReceivers.SourceListEventReceiver" -target list -sequence 1000 -type itemupdated

1 comment:

Anonymous said...

It appears that using this command to add an event receiver to a content type doesn't push down the change to the child content types. I think the code needs to execute "contentType.Update(true)" after adding the event receiver to the collection.