Sunday, October 7, 2012

Event Receiver and column level security in SharePoint 2010

SharePoint doesn't support field/Column level security out of the box. So when we want to allow some fields to be filled up by only a particular group then we need to go for a custom development. As I found out there are some ways to do it,

1) Using InfoPath (I guess the best way)
2) Using Event Receiver (If you don’t have InfoPath, then the best way :)

I will be developing this with Event Receiver.

Understanding Event Receiver


An event receiver in Microsoft SharePoint Foundation 2010 is simply a method that is called when a triggering action occurs on a specified SharePoint object. Triggering events include actions such as adding, updating, deleting, moving, checking in, and checking out. SharePoint objects that listen for events—that is, event receiver hosts—include objects such as site collections, sites, lists, and workflows.—Microsoft Site Definition.

There can be various events like ItemAdding, ItemAdded, ItemUpdating, ItemUpdated,ItemDeleting, ItemDeleted. These events are broadly categorized into (before) synchronous and (after) asynchronous events. The method suffix such as “ing” (e.g. ItemAdding) and “ed” (e.g. ItemAdded) will tell you whether it gets invoked before or after the actual change is made.

Now there are three holders (beforeProperty, afterproperty, listitem) which carry the data during these events, hence it is very important to understand which event receiver will carry data in these holders and which not. The below table will clears this mist.
 
 


Creating Event Receiver through Visual Studio

Problem Statement: We have an "Expense Report" list in which there are two columns a) Amount  b) DateCheck . These two columns are required to be filled by only “My Test Owners” group users.
Let’s Start :
  1. Fire up Visual Studio 2010 and from the templates select “Event Receiver” for the new project under SharePoint 2010 and name the project as “HideFieldEventReceiver”

  2. The configuration wizard would ask you for the URL of the site against which you would like to create the Event Receiver, also choose deploy as farm solution. Then choose the Event Receiver settings as below.

  3. Visual Studio will create an EventReceiver.cs and Elements.XML, the EventReceiver.cs will have methods for both ItemAdding and Item Updating events, and Elements.XML will help in connecting to the List and the events.
    As in Elements.Xml the Event is connected to all Custom List, hence we need to modify it as below to make it connect to our specific List called “Expense Report”. Remove ListTemplateId to ListUrl.

  4. Now open the EventReceive.cs file and in the ItemAdding Event add the below code. This will run whenever a user is adding a new Item in List, which will check the permissions of the user and also identify if he is updating those restricted fields. We will be using the AfterProperties of the column to check if the fields are being updated.

  5.                 public override void ItemAdding(SPItemEventProperties properties)
            {
                base.ItemAdding(properties);
                if (properties.AfterProperties["Amount"].ToString() != string.Empty || 
                    properties.AfterProperties["DateCheck"].ToString() != string.Empty)
                {
                    using (SPWeb ob = properties.OpenWeb())
                    {
    
                        SPGroup group = ob.Groups["My Test Owners"];
                        if (!ob.IsCurrentUserMemberOfGroup(group.ID))
                        {
                            properties.Cancel = true;
                            properties.ErrorMessage = "You are not allowed to fill this";
                        }
                    }
                }
            }
            
  6. Now in the Item Updting Event add the below code. This is little complicated as we need to check if there is a change in the field value by comparing the ListItem property(before Value) and AfterProperty (afterValue) of the fields.

  7. public override void ItemUpdating(SPItemEventProperties properties)
            {
                base.ItemUpdating(properties);
                if ((properties.ListItem["Amount"]!=null && properties.ListItem["Amount"].ToString() != properties.AfterProperties["Amount"].ToString())
                    || (properties.ListItem["DateCheck"]!=null && 
                    DateTime.Parse(properties.ListItem["DateCheck"].ToString(), null, DateTimeStyles.AdjustToUniversal) != 
                    DateTime.Parse(properties.AfterProperties["DateCheck"].ToString(), null, DateTimeStyles.AdjustToUniversal)))
                {
                    using (SPWeb ob = properties.OpenWeb())
                    {
                        SPGroup group = ob.Groups["My Test Owners"];
                        if (!ob.IsCurrentUserMemberOfGroup(group.ID))
                        {
                            properties.Cancel = true;
                            properties.ErrorMessage = "You are not allowed to Edit the restricted Fields";
                        }
                    }
    
                }
                else if ((properties.ListItem["Amount"] == null && properties.AfterProperties["Amount"].ToString() != string.Empty)
                    || (properties.ListItem["DateCheck"]==null && properties.AfterProperties["DateCheck"] !=null && 
                    properties.AfterProperties["DateCheck"].ToString() != string.Empty))
                {
                    using (SPWeb ob = properties.OpenWeb())
                    {
                        SPGroup group = ob.Groups["My Test Owners"];
                        if (!ob.IsCurrentUserMemberOfGroup(group.ID))
                        {
                            properties.ErrorMessage = "You are not allowed to Edit the restricted Fields";
                            properties.Status = SPEventReceiverStatus.CancelWithError;
                            properties.Cancel = true;
                            
                        }
                    }
                }
            }
    
  8. Deploy the solution and test the code. I believe the code is very much self-explanatory where we test on the events if the fields have changed or not. If they are changed we check whether the current user is a member of the Owners group which is allowed for access. If they are not then we show the Error with Properties.ErrorMessage and we cancel the event