C# Extension to Delete selected SharePoint List Items

It is a common requirement to delete set of SharePoint list items from a list. In most case you might want to do that quickly as well. Here is a extension method that I wrote to accomplish that.

 public static void DeleteItemsCollection(this SPList list, SPWeb web, SPListItemCollection listitemCollection)
{
StringBuilder deletebuilder = DeleteBatchCommand(list, listitemCollection);
web.ProcessBatchData(deletebuilder.ToString());
}

 

StringBuilder DeleteBatchCommand(SPList spList, SPListItemCollection listitemCollection)
{
StringBuilder deletebuilder = new StringBuilder();
deletebuilder.Append(“<?xml version=\”1.0\” encoding=\”UTF-8\”?><Batch>”);
string command = “<Method><SetList Scope=\”Request\”>” + spList.ID +
“</SetList><SetVar Name=\”ID\”>{0}</SetVar><SetVar Name=\”Cmd\”>Delete</SetVar></Method>”;

foreach (SPListItem item in listitemCollection)
{
deletebuilder.Append(string.Format(command, item.ID.ToString()));
}
deletebuilder.Append(“</Batch>”);
return deletebuilder;
}

The method “DeleteItemsCollection” can be called up on any list together with SPWeb and the collection list items that you want to get rid of the list. You can simply execute the method as follows.

myList.DeleteItemsCollection(web, itemsToDelete);

 

Advertisements

Add Quick Launch Navigation Entries together with Localization Text using Powershell

Its a time where most SharePoint developers look to do everything possible with Powershell rather than writing thousands of C# code. One common simple such requirement is to add Navigation Entries to Quick Launch. This is specially very useful when you work with a site where publishing features are not available so that you can not use the GUI to add navigation entries either.

Here is the code,

    $webURL =  “http://mySPSiteURL/&#8221;
$enTitle = “Chaminda’s Blog ”
$frTitle = “Blog de Chaminda”
$urlLink =”https://chamindasomathilaka.wordpress.com&#8221;

$webs = Get-SPWeb -Identity $webURL -ea Stop

$navigation= $webs.Navigation.QuickLaunch

$culture = New-Object System.Globalization.Cultureinfo(“fr-FR”)
$cultureFrCA = New-Object System.Globalization.Cultureinfo(“fr-CA”)

$newnode = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode($enTitle, $urlLink, $true)

$addedNode = $navigation.AddAsLast($newnode)

$addedNode.TitleResource.SetValueForUICulture($culture, $frTitle)
$addedNode.TitleResource.SetValueForUICulture($cultureFrCA, $frTitle)

$addedNode.Update()

$webs.Update()

That is to add a Quick Launch navigation entry directing my blog with both English and French translation (this is in case you have both English and French languages are available on your site).

Same code can be used to add Navigation entry to Top Navigation Area as well. you just need to initialize $navigation variable with correct navigation.

   $navigation= $webs.Navigation.TopNavigationBar

Changing SharePoint database from SQLExpress Edition to Default SQL Server Instance for Standalone Installation

I’m a big lover of using base machine for SharePoint development work instead of using different VMs. I love that approach,

  1. SharePoint runs much faster as it has more comfortable memory and processing power usage
  2. Visual Studio runs much faster
  3. I have access to all other tools such as MS office etc
  4. Last but no least I will not have to transfer my tools from every time I put up a new VM

I have been running SharePoint 2010 on my desktop for more than a year now, while working few different SharePoint and non-SharePoint projects.  I used this very nice article on how to setup SharePoint on Windows 7 few times now to set my dev env without noticing the SharePoint installation automatically install a SQL Server Express edition instance and make use that as the SharePoint’s database.  This was only noticed when I want to get rid of any redundant processes so that I can free up some of the processing power on the machine. For other .net related development I used to have an full SQL Server installation and addition to that my machine is running an SQL Express Edition instance for SharePoint. So I wanted to get rid of this express edition and make use of full SQL Server edition’s default instance for SharePoint as well.  So started in a hard way, uninstall my SQL express edition instance and ran the SharePoint Configuration Wizard, thinking it will notice the loss of SharePoint Configuration database and ask me to create new farm. But it didn’t go that way, instead wizard keep crashing with the message saying unable connect to SQL Server.

However finally I was able to get it to configure new farm by following steps.

  1. Got the env to disconnect from the farm by setting the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\14.0\WSS\ServerType to CLEAN_INSTALL and running the SharePoint Configuration Wizard (refer this)
  2. Then set the Server role to be Application (instead SingleServer) by setting registry entry HKLM/Software/Microsoft/Shared Tools/Web Server Extensions/12.0/WSS -> ServerRole key from ‘SingleServer’ to ‘Application’  (refer this)
  3. Then run the ShaerPoint Configration Wizard to configure new farm by providing default sql server instace (or any other SQL Server database instance) as the database

Handle with care, Json Serialization when SharePoint Object Model’s Objects in the code

My team went crazy with the generic error message that they were getting while trying to Serialize an object created of some POCO. One of them even lost good whole day trying to troubleshoot what exactly is the issue.

The serializer is one of the best in business for .Net , the Newtonsoft.Json.JsonConvert.

And the error message they were getting is

“An unhandled exception of type ‘System.StackOverflowException’ occurred in mscorlib.dll”

which hardly express any means of the cause. This error message is thrown when following piece of code is getting executed.

 JsonConvert.SerializeObject(MyEntity, Newtonsoft.Json.Formatting.Indented);

Finally I figured it out that one of the newly added Properties of MyEntity’s class causing the issue.

The newly added Property is of SPUser field.  So the entity class looked like following,

   public class MyEntity

{

public string Name { get; set; }

public string LoginName { get; set; }

public SPUser User { get; set; }

}

So the issue with the Serializer unable to serialize the instance of SPUser. The when I checked the serializer is unable to serialize SPFieldUserValue and SPFieldUserValueCollection as well.

Workaround :

Anyway fortunately I did not want this field necessarily be in the Json that I was getting.

So I decorated each of those fields with [JsonIgnoreAttribute] attribute. Doing that I had to import the Newtonsoft.Json with using directive. That made my entity class look like ,

   public class MyEntity

{

public string Name { get; set; }

public string LoginName { get; set; }

[JsonIgnoreAttribute]

public SPUser User { get; set; }

}

In conclusion, even if you use more improved serializer like Newtonsoft.json, you should be very careful about what you are trying to serialize and desterilize especially when you are dealing with SharePoint Object model’s classes.

Changing SharePoint database from SQL Express edition instance to SQL default instance for Standalone Installation

I’m a big lover of using base machine for SharePoint development work instead of using different VMs. I love that approach,

  1. SharePoint runs much faster as it has more comfortable memory and processing power usage
  2. Visual Studio runs much faster
  3. I have access to all other tools such as MS office etc
  4. Last but no least I will not have to transfer my tools from every time I put up a new VM

I have been running SharePoint 2010 on my desktop for more than a year now, while working few different SharePoint and non-SharePoint projects.  I used this very nice article on how to setup SharePoint on Windows 7 few times now to set my dev env without noticing the SharePoint installation automatically install a SQL Server Express edition instance and make use that as the SharePoint’s database.  This was only noticed when I want to get rid of any redundant processes so that I can free up some of the processing power on the machine. For other .net related development I used to have an full SQL Server installation and addition to that my machine is running an SQL Express Edition instance for SharePoint. So I wanted to get rid of this express edition and make use of full SQL Server edition’s default instance for SharePoint as well.  So started in a hard way, uninstall my SQL express edition instance and ran the SharePoint Configuration Wizard, thinking it will notice the loss of SharePoint Configuration database and ask me to create new farm. But it didn’t go that way, instead wizard keep crashing with the message saying unable connect to SQL Server.

However finally I was able to get it to configure new farm by following steps.

  1. Got the env to disconnect from the farm by setting the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\14.0\WSS\ServerType to CLEAN_INSTALL and running the SharePoint Configuration Wizard (refer this)
  2. Then set the Server role to be Application (instead SingleServer) by setting registry entry HKLM/Software/Microsoft/Shared Tools/Web Server Extensions/12.0/WSS -> ServerRole key from ‘SingleServer’ to ‘Application’. (refer this)

Things to look for when setting Item Level Permission with List Event Handler

One of the key benefits of using SharePoint list as primary means of data storage is the OOB feature to be able to set Item Level Permission. Setting Item Level Permissions or can either be done with the GUI or with some custom code. In case of Custom Code, it can be either done with List Event Handler or with a Workflow. What this article is focusing on is use of Event Handler to Set/Update Item Level Permission.

  1. First thing to be remembered is that the SPListItem you get from SPItemEventProperties object which received with the Event Hander argument is initialized to the user context of whom the event originated. This is especially important if you are having few custom permission levels or user roles, so that user who is originating list event may not always have required rights to perform some of the actions such as add/update item level permission.  So it is recommended that you reinitialize the SPListItem on which you are going to work on for Item Level Permission with an Elevated Privileged context.
    public override void ItemAdded(SPItemEventProperties properties){
     EventFiringEnabled = false;
     SPListItem currentItem = properties.ListItem;
     SPSecurity.RunWithElevatedPrivileges(delegate {
    using (SPSite elevatedSite = new SPSite(properties.ListItem.Web.Site.ID))
           {
           using (SPWeb elevatedWeb = levatedSite.OpenWeb(properties.ListItem.Web.ID))
     {
             SPList elevatedList = elevatedWeb.Lists[currentItem.ParentList.ID];
             SPListItem elevatedListItem = elevatedList.GetItemById(currentItem.ID);
    ...

    If you don’t do that it is more likely that you may experience following errors.

    UnauthorizedAccessException: <nativehr>0x80070005</nativehr><nativestack></nativestack>  
    at Microsoft.SharePoint.Library.SPRequest.AddOrUpdateItem(String bstrUrl, String bstrListName, Boolean bAdd, Boolean bSystemUpdate, Boolean bPreserveItemVersion, Boolean bUpdateNoVersion, Int32& plID, String& pbstrGuid, Guid pbstrNewDocId, Boolean bHasNewDocId, String bstrVersion, Object& pvarAttachmentNames, Object& pvarAttachmentContents, Object& pvarProperties, Boolean bCheckOut, Boolean bCheckin, Boolean bMigration, Boolean bPublish, String bstrFileName, ISP2DSafeArrayWriter pListDataValidationCallback, ISP2DSafeArrayWriter pRestrictInsertCallback, ISP2DSafeArrayWriter pUniqueFieldCallback)
  2. Then let us take a look some of the common steps we follow to set Item Level Permission.
    1. Break Role Assignment Inheritance
    2. Remove all the role assignments
    3. Add any User/Group Assignments

    First two steps of the above can written as follow,

    listItem.BreakRoleInheritance(false);
    
    foreach (SPRoleAssignment assignment in listItem.RoleAssignments)
    {
         assignment.RoleDefinitionBindings.RemoveAll();
         assignment.Update();
    }

    In this case I am using “listItem.BreakRoleInheritance(false)” so that the collection of role assignments contain only 1 role assignment containing the current user after the operation. Some thing to remember with this is that this is very resource intensive operation as it will break all role assignments and delete user/group security assignment one by one.  This will take considerable time if the number of security assignment(either of groups or users) of the parent sharepoint object( in this case the Site).  In my case I had about 2000 users associated with the site, which made considerable delays.

    Then the other segment to remove all the RoleDefinitions assigned to the Item.

    One of the interesting scenario that I observed is that if you use this piece of code in high volumes, there is high chance of you code getting failed.  In my case I even had try/catch block wrapping the code segment, but I hardly had any exceptions caught by the catch statement, but the ULS log had following stack

    at Microsoft.SharePoint.Utilities.SqlSession.OnPostExecuteCommand(SqlCommand command, SqlQueryData monitoringData) 
    at Microsoft.SharePoint.Utilities.SqlSession.ExecuteReader(SqlCommand command, CommandBehavior behavior, SqlQueryData monitoringData, Boolean retryForDeadLock) 
    at Microsoft.SharePoint.SPSqlClient.ExecuteQueryInternal(Boolean retryfordeadlock) at Microsoft.SharePoint.SPSqlClient.ExecuteQuery(Boolean retryfordeadlock) 
    at Microsoft.SharePoint.Library.SPRequestInternalClass.UpdateMembers(String bstrUrl, UInt32 dwObjectType, String bstrObjId, Guid& pguidScopeId, Int32 lGroupID, Int32 lGroupOwnerId, Object& pvarArrayAdd, Object& pvarArrayAddIds, Object& pvarArrayLoginsRemove, Object& pvarArrayIdsRemove, Boolean bRemoveFromCurrentScopeOnly, Boolean bSendEmail) at Microsoft.SharePoint.Library.SPRequest.UpdateMembers(String bstrUrl, UInt32 dwObjectType, String bstrObjId, Guid& pguidScopeId, Int32 lGroupID, Int32 lGroupOwnerId, Object& pvarArrayAdd, Object& pvarArrayAddIds, Object& pvarArrayLoginsRemove, Object& pvarArrayIdsRemove, Boolean bRemoveFromCurrentScopeOnly, Boolean bSendEmail) 
    at Microsoft.SharePoint.SPRoleAssignmentCollection.RemoveMembers(Int32[] IdsToRemove, Int32 roleId, Boolean removeFromCurrentScopeOnly) at Microsoft.SharePoint.SPRoleAssignmentCollection.Remove(Int32 index)

    What I realized from that stack trace is the operation have being aborted from the SQL Server operation it self.  So few hours of googling made me aware this is happening because of an XACT abort resulting from the stored procedure. And this is happening since multiple threads trying this operation. I saw some are suggesting to run a command to ignore abort. However since MS is not recommending to touch SQL db, I went the other option of solution to have a lock on the code segment. In that way I ensure the code segment is owned by single thread and have an exclusive lock until it completes. That solved my issue. so my looked like,

    lock (removePermissionLock)
     {
          listItem.BreakRoleInheritance(false);
    
          foreach (SPRoleAssignment assignment in listItem.RoleAssignments)
          {
            assignment.RoleDefinitionBindings.RemoveAll();
            assignment.Update();
          }
         listItem.Update();
     }
  3. Use of same SPListItem initialized with the Elevated Privileges to Break Inheritance and Add new user/group assignments. If you happen to associate/or start Workflow, use the same SPListItem object to avoid any Save Conflicts. In my case I had three different SPListItem initialized for Break Inheretance, Add new user/group assignements and Start the Workflow. That made my workflow code to fail stating the Save Conflict. Weirdly the workflow produced the Conflict error after some operations on the workflow code on immediately at the time of start of workflow on the Event Handler it self. The error logged in was happen to be,
     Save Conflict. Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.0x81020015

Run a Powershell Script Block with Elevated Privileges

Familiar with this ?

SPSecurity.RunWithElevatedPrivileges(delegate
{

…….code to be run with Elevated Privileges

});

You can do the same with a Powershell Script as well,

[Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges(
    {
        $web = Get-SPWeb -identity $url
        $list = $web.Lists[“ListName”]
        $spListItems = $list.Items;
        $spListItems | Foreach-object {[MyProject.CustomCode]::MyMethod($_,$web)}
    }
)