Saturday 19 May 2012

How to use BeforeProperties and AfterProperties in an RunWithElevatedPrivileges block

I came up against a tricky problem this week when I wrote code that would not work for a non-admin account and needed to wrap it up in a privilege block (note: I am not using RunWithElevatedPrivileges as it's dodgy, see earlier blog entries on the subject, but it's along similar lines.)

As explained before, an override method for an SPItemEventReceiver object always passes in the properties of the list item as a parameter. This allows for manipulation of the object. 

But we also know that the properties object cannot be used within the SPSecurity.RunWithElevatedPrivileges block as an entirely new site object needs to be created with separate web, list and item objects created with it. This is fine usually, but what if we need to compare the value of a column before being updated with the results after it has been changed? Usually we would be able to avail of properties.BeforeProperties["Fieldname"] and properties.AfterProperties["Fieldname"] and compare the two, but our new SPListItem object inside the privilege block does not possess either of these attributes. So, what to do?

The solution is below - important lines in bold:

SPItemEventDataCollection before = properties.BeforeProperties;
SPItemEventDataCollection after = properties.AfterProperties;
Guid siteID = properties.SiteId;
Guid webID = properties.ListItem.Web.ID;               
//this is for getting the user Token to log in as system
SPSite tempSite = new SPSite(siteID);
SPUserToken sysToken = tempSite.SystemAccount.UserToken;
//need to dump this as not declared in a using block
tempSite.Dispose();
//start the privilege block
using (SPSite site = new SPSite(siteID, sysToken))
{
    using (SPWeb web = site.OpenWeb(webID))
    {
        if (before["FieldName"].ToString() != after["FieldName"].ToString())
        { someBooleanVariable = true; }
        //do work
    }

}