Friday, March 2, 2012

One problem with updating alert template for Sharepoint list

In this post I would like to describe one problem with Sharepoint alerts. Alerting is OTB Sharepoint feature which can be used for subscribing on the events from list or document library. Supported event types are:

  • All changes
  • New items are added
  • Existing items are modified
  • Items are deleted

Each list has single associated alert template. Programmatically you can read and set this alert template using SPList.AlertTemplate property. Each list created using OTB list template (e.g. Custom list) has associated OTB alert template. Alert templates are located in 14/Template/Xml/AlertTemplates.xml file. E.g. for Custom list there is “SPAlertTemplateType.GenericList” alert template. Alert templates contain subject and body which will be sent to end user.

You can modify existing alert templates or create new one. Most of the articles which I found say that you should not make changes in original AlertTemplates.xml (this is quite reasonable because it may be overriden by next Sharepoint update). Instead create a copy of this file, modify it (add new alert template with unique name or update existing template) and call updatealerttemplates stsadm command:

   1: stsadm -o updatealerttemplates -url http://example.com -f MyAlerttemplates.xml

However it is not the only way to create alerts templates. You can also create alert templates in configuration database. I will write separate post about it.

But let’s return to the original topic: suppose that you created new custom list. After creation it will have SPAlertTemplateType.GenericList alert template associated. Then users subscribe to events from this list or you subscribe them by yourself (it is possible to specify different users in the OTB alerts subscribe page). After that you change alert template, e.g. programmatically:

   1: list.AlertTemplate = alertTemplate;
   2: list.Update();

or using 3rd party component via UI. You may expect that users who subscribed to the list previously will receive emails created with updated alert template. But this is not the case. Previously subscribed users will receive emails created with old alert template, but those users who will subscribe after modification of alert template – will receive new emails.

It may cause many problems and a lot of debugging efforts. E.g. if you created custom alert handler (inheritor of IAlertNotifyHandler interface), attach it to some alert template (here is the good example of how to do it: How To: Customizing alert emails using IAlertNotifyHandler. It also uses example with file, not with config database), then associate alert template with list – but it is not fired anyway. One of the reason is that users were subscribed before alert template was changed for this list.

One way to avoid it is to set alert template for all subscribers when list alert template was changed. Here is code for this (didn’t test it by myself, but it should work):

   1: list.AlertTemplate = at;
   2: list.Update();
   3:  
   4: foreach (SPAlert alert in list.ParentWeb.Alerts)
   5: {
   6:     if (alert.ListID == list.ID)
   7:     {
   8:         alert.AlertTemplate = list.AlertTemplate;
   9:         alert.Update();
  10:     }
  11: }

or re-subscribe users manually. Hope this information will be useful for you if you will face with similar problem.

9 comments:

  1. Have you gotten around to writing the blog entry to create alert templates in configuration database?

    I would love to have an alternative method for getting my alert templates into the DB.

    ReplyDelete
  2. hi Unknown, I didn't write it yet, but remember about it and will write it at some point. The main idea is that SPAlertTemplate inherits SPPersistedObject class which means that you can persist them in the farm configuration database.

    ReplyDelete
  3. Holy freaking crap. Thank you a million times over for this blog post. I had no idea that you could individually update a user's alert's alert template... and it is exactly what I was looking for to circumvent the pitfalls I've been treading through while attempting to modify the OTB alert templates on a specific list. You rock!

    ReplyDelete
  4. you are welcome :) it also added headache for me in own time, so I shared the details about this "non-intuitive behavior".

    ReplyDelete
  5. This is just what I was after.

    Are you able to provide the expected value for my custom alert template in the PowerShell examples?

    For example, if I create a copy of alertteamplates.xml how would I reference it? As a string? Relative path?

    at = "myalert.xml" # Is this correct?
    list.AlertTemplate = at;

    ReplyDelete
  6. Paul, once you create copy of alerttemplates.xml, made changes in it (e.g. created new template inside it or changes existing) and called updatealerttemplates command you may get instance of alert template by name. Try the following code:

    var col = new SPAlertTemplateCollection(SPWebService.ContentService);
    var at = col.GetValue("myalert");

    ReplyDelete
    Replies
    1. GetValue method is generic, but in comments these symbols are trimmed. Here is pseudo code:
      var at = col.GetValue[SPAlertTemplate]("myalert");

      Delete
  7. Hi Alexey,

    I've been looking for a way to avoid using the stsadm command or creating a copy of the alerttemplates.xml.

    According to the API it appears possible to create a custom alert template for a list on the fly, which to me seems far friendlier.

    Assuming custom XML string which could be passed directly within the script (or managed as an external file with other custom alerts)...

    $xml = '
    ...custom template here...
    '

    I've written the following script which takes four parameters - webUrl, listName, templateName and templateXml. Do you think something like this would be possible? As a developer this seems like a much better way to manage the custom templates while keeping them under source control.

    function AddAlertToList
    {
    param(
    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [string]$theWebUrl,

    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [string]$theListName,

    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [string]$theTemplateName,

    [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    [string]$theXML
    )

    $web = Get-SPWeb $theWeburl
    $theList = $web.Lists[$theListName];
    $newTemplate = New-Object Microsoft.SharePoint.SPAlertTemplate
    $newTemplate.Name = $theTemplateName
    $newTexmplate.Xml = $theXML
    $list.AlertTemplate = $newTemplate
    $List.Update()
    }

    ReplyDelete
  8. Paul, creating and adding of new alert templates to the lists dynamically for sure possible. E.g. in SP2007 there was popular SPSolutions Alerts Manager which had UI for doing that. They created alert templates in config database (base class of their alerts inherited SPPersistedObject as far as I remember and automatically was serialized and saved to config database).

    ReplyDelete