ItemUpdated event happening twice when you force Check Out option

Well, today at work, I was really confused why ItemUpdated event in the Document Library was getting called twice. Debugging, it appears that the ItemUpdating event or the ItemUpdated event occurs two times when you enable the Require Check Out option for Document Library! - Thats bad, this was increasing the database requests that we were doing in the ItemUpdated event!

So, Here is THE Solution if you don't want the ItemUpdating and ItemUpdated event get called twice! - http://support.microsoft.com/kb/939307

To work around this behavior, examine the vti_sourcecontrolcheckedoutby property inside an event receiver. If the vti_sourcecontrolcheckedoutby property exits in the BeforeProperties property but not in the AfterProperties property, the event was caused by checking in a document. The following sample code shows you how to do this.

if (properties.AfterProperties["vti_sourcecontrolcheckedoutby"] == null && 
properties.BeforeProperties["vti_sourcecontrolcheckedoutby"] != null)
{
//This is when the update event is triggered by check-in.
}
else
{
//This is triggered by events other than check-in action.
}

SharePoint - Setting Item level permission

Back at work, I had a requirement to set item level permission in a SharePoint Document Library. What does this mean? Below is a ‘diagram’

image

Diagram 1

It is very simple to explain in simple words – I want to set unique permissions on each file in my document library.

And the same in SharePoint words – I want to set unique permissions on a ListItem (SPListItem) which resides in a List (SPList) which again resides in a SharePoint Web Application (SPWeb).

The file resides inside a ListItem and thus you need to access the file via its ListItem. Using site settings, you can configure the default permissions for the document library. But there is no option to manage the default permission level for a ListItem. Whenever a new item is added to the document library, SharePoint adds the parent’s permission levels down to the child – i.e. document library’s permission levels to the list item – This is called Role Inheritance (Blue Line in the Diagram 1). So, if you want to add unique permissions to a list item, you need to first break this role inheritance and then add unique permissions.

The only way to achieve this is to write code. I had two options :

1) Handle adding unique permissions in any of the document library event handlers (of your choice)

2) If you have separate application dealing with adding items to the document library, then you can directly handle permissions in that application

Well, I needed both because I had to set permissions on the item when it was manually added to the library using the SharePoint website and also via the application :(

I started to investigate which event handler to use. Since I had to access item’s properties(fields), I had to choose ItemUpdated event handler. Why? Below is the ‘event sequence’ that happens when you upload a document in the document library:

image

Diagram 2

When a document is uploaded, first the item is added and then checked in. Once the item is checked in, SharePoint allows you to provide values to the custom fields that you may have for your item. Even for the default fields SharePoint adds them once the item is checked in. I am not sure how useful the ItemAdding event is as I was not able to even retrieve the item that was being added in that event. But once the item is added, I got the freedom to set permissions but still I was not able to grab the field values that user may enter, as the item is not yet checked in and the user is not presented with the input screen. Once the item is checked in, now the user gets the screen to input the field values and when the user clicks OK, the ItemUpdating and ItemUpdated events fire up!

That was when we use the SharePoint web UI.

If you use code to add an item to the document library, things happen when you add and then perform an update on the item.

Now remember that since I am using an event handler to set permissions, even If I perform an update via the code, my event handler will fire up, which is great!

This was easy : Wrote my ItemUpdated event handler, set the permissions. As I said earlier I had to break the role inheritance and only then set permissions on the item. Great! I was ready to test this. I had a separate application (web service) which deals with uploading files to the document library. I had a count of 600 files in the initial run to upload.

I hit the button and the application started uploading files..

first 50 – super fast, excellent

next 100 – fast, Excellent :D

next 100 – fast, Excellent :D

next 100 – slow, OK :-/

next 10 – dead slow, NOT OK :-/

next …. – dead slow as it was still dealing with the above 10 items :( :(

And as this upload was going on, our DBA guys came running to me (as they know am dealing with SharePoint database) saying that the CPU usage in the SharePoint database machine is 100% and the database transaction log is growing exponentially high! I was really confused and told them to wait until my 600 files were uploaded. After fifteen minutes I had to stop my application as the disk space where the database logs + database resides became full :(

My team was very furious now. My small application brought the database server down!  DBA guys had set up only 5GB of space to the whole SharePoint database (That’s more than enough for our SharePoint Web Application) and they had enabled full transaction logging on the server. The logs were getting bigger and bigger that it went up to 2GB!

What was happening?

It took time but we eventually found out what was happening! The DBA guys were helpful in telling us about a stored procedure that was being called most of the time and also those calls being ‘threaded’ calls (multiple threads). Sample calls below:

WSS_Content_Workspace.dbo.proc_GetTpWebMetaDataAndListMetaData 
WSS_Content_Workspace.dbo.proc_SecGetRoleDefskdc-dmz-consume-SharePoint-Config.dbo.proc_CompleteTimerRunningJob
WSS_Content_Workspace.dbo.proc_SecRemovePrincipalFromScope
WSS_Content_Workspace.dbo.proc_GetTpWebMetaDataAndListMetaData
WSS_Content_Workspace.dbo.proc_GetTpWebMetaDataAndListMetaData

That helped to us to start investigating with setting the permissions. So, there were two places:

1) Breaking the role inheritance

2) Setting manual permissions

We have two options when we break the role inheritance which is show below

image

You can choose to either copy the document library permissions’ to the item or not. Does this make a big difference? Well, YES!

If you choose to copy the permissions down to the item, then SharePoint copies the permissions to the item and does an item update! This makes our event handler to get invoked and that again sets permissions and then again my code has an item update after setting the field values, again, my event handler gets invoked and sets permissions. Unfortunately my code had set to copy the permissions down to the item :( And remember, the event handlers are asynchronous and thus those ‘threaded’ calls! Just think this for 600 files!

Now, you might have another question – how come copying down the permissions slowed things down ?

When we set unique permission to an item, SharePoint copies that permission back to the document library with ‘Limited Access’ permission level. SharePoint does this because it is not smart enough to find whether that unique permission already has access to the document library. SharePoint even goes beyond in adding this unique permission to the site as again SharePoint is not smart enough to find whether that unique permission already has access to the site.(Red Line in the Diagram 1) Why? Unless you have access to site, you cant access the document library and unless you have access to the library, you cant access the item! This goes on for each item, so at the 201st item we would have 200 unique permissions added to the document library and site. So, when we break the role inheritance with copy permissions set true – SharePoint will copy all those 200 permissions to the newly created item! We are now forced to remove those permissions and set our new permissions! That’s bad and that is what this blog post uses !  Don’t even try removing that permission level added to the document library because SharePoint is smart enough to remove the permission on any item inside that document library, thinking that that permission does not need to have access to the items!

So, the proper code to use when you set item level permission and break role inheritance would be :

if (!item.HasUniqueRoleAssignments){    item.BreakRoleInheritance(false);}

 

 

This does not copy down the permissions to the items :D

One small flag caused us a lot of trouble and you wouldn’t even notice this CPU hike, logs usage etc., unless you have a very restricted environment where everything is limited! I don’t want to get into various other problems that we faced as a consequence of this, but do not forget to really analyze your code step by step before you completely blame SharePoint ;)

Any links for good practices?

1) Common coding issues when using the SharePoint object model

2) Using Disposable Windows SharePoint Services objects

3) Roger Lamb’s cool posts about SharePoint 2007 and WSS 3.0 Dispose Patterns with examples

Extending SharePoint by Adding Custom Actions

So, you want to add your custom items in the SharePoint menus or toolbars ? You can do so by adding your own Custom Action. Custom Action is a SharePoint Feature which makes your life easier if you want to customize your site.

Some examples of where you might want to add items may be:

1) In the Upload Drop Down menu in the Document Library

sharepoint-custom-action-example

2) In the Edit Menu content block

sharepoint-custom-action-example1

If you do a web search, you will find that many people have achieved this by modifying a file called core.js. This is a JavaScript file used by SharePoint master page and has all the code in creating those menus.

But *it is not recommended* to change the SharePoint's core files.How do we do this then?

If you do sometimes want to hide items in the Edit Control Block, MSDN again has a neat article on how to perform this.

My only advice when doing customisations to a SharePoint website would be, *not to modify the default SharePoint files* !

Here is my modified Edit Control Block for my Document Library :

I have hidden the Send To menu and added a new action item - Send Item for Review

sharepoint-custom-action-item

Code Snippets: Upload and Download a file from Sharepoint Document Library

Here are sample code snippets to upload and download a file from Sharepoint Document Library

Upload a File:

   1: public static void UploadDocument(string siteCollectionUrl, 
   2:                             string siteUrl,string libraryName)
   3: {
   4:     using (SPSite hostSite = new SPSite(siteCollectionUrl))
   5:     {
   6:         #region Reading a file
   7:  
   8:         string filename = "C:\\TFS.doc";
   9:         FileInfo fInfo = new FileInfo(filename);
  10:         long numBytes = fInfo.Length;
  11:         FileStream fStream = 
  12:             new FileStream(filename, 
  13:                            FileMode.Open, 
  14:                            FileAccess.Read);
  15:         BinaryReader br = new BinaryReader(fStream);
  16:         byte[] fileBuffer = br.ReadBytes((int)numBytes);
  17:         br.Close();
  18:         fStream.Close();
  19:  
  20:         #endregion
  21:  
  22:         #region Fields
  23:  
  24:         Dictionary<string, string> DocumentFields = 
  25:                           new Dictionary<string, string>();
  26:         DocumentFields.Add("Status", "Received");
  27:  
  28:         #endregion
  29:  
  30:         #region Upload File into the Document Library
  31:  
  32:         using (SPWeb targetWeb = hostSite.OpenWeb(siteUrl))
  33:         {
  34:             DateTime submissionDate = DateTime.Now;
  35:             SPList submissionsList = targetWeb.Lists[libraryName];
  36:             SPFolder folder = submissionsList.RootFolder;
  37:  
  38:             string fileName = "Sample.doc";
  39:  
  40:             SPFile newSubmissionFile = folder.Files.Add(fileName,fileBuffer);
  41:  
  42:             SPListItem submissionItem = newSubmissionFile.Item;
  43:  
  44:             foreach (KeyValuePair<string, string> documentField in DocumentFields)
  45:             {
  46:                 submissionItem[documentField.Key] = documentField.Value;
  47:             }
  48:  
  49:             submissionItem.Update();
  50:         }
  51:  
  52:         #endregion
  53:     }
  54:  
  55:     Console.WriteLine("File Uploaded Successfully!");
  56: }

Download a File:

   1: public static void DownloadDocument(string siteCollectionUrl, 
   2:                     string siteUrl, string libraryName, string fileName)
   3: {
   4:     using (SPSite hostSite = new SPSite(siteCollectionUrl))
   5:     {
   6:         #region Download the File from the Document Library
   7:  
   8:         using (SPWeb targetWeb = hostSite.OpenWeb(siteUrl))
   9:         {
  10:             DateTime submissionDate = DateTime.Now;
  11:             SPList submissionsList = targetWeb.Lists[libraryName];
  12:             SPFolder folder = submissionsList.RootFolder;
  13:             SPFile targetFile=targetWeb.GetFile(folder.Url + "/" + fileName);
  14:  
  15:             if (targetFile.Exists)
  16:             {
  17:                 byte[] fileContents = targetFile.OpenBinary();
  18:  
  19:                 if (!File.Exists(fileName))
  20:                 {
  21:                     File.Create(fileName)
  22:                         .Write(fileContents, 0, 
  23:                                fileContents.Count());
  24:  
  25:                     Console.WriteLine("File saved successfully");
  26:                 }
  27:                 else
  28:                 {
  29:                     Console.WriteLine("File Already Exists");
  30:                 }
  31:             }
  32:         }
  33:  
  34:         #endregion
  35:     }
  36: }

Creative Commons License
Chaks' Corner Blog by Chakkaradeep Chandran is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
Based on a work at www.chakkaradeep.com.
Permissions beyond the scope of this license may be available at http://www.chakkaradeep.com.