Tuesday, November 18, 2014

Delete duplicate fields

Find all duplicated fields in a list under all content types.


 Clear-Host  
 Add-PSSnapin microsoft.sharepoint.powershell -ErrorAction SilentlyContinue  
 [System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c")   
 $web=Get-SPWeb "http://site:1234"  
 function cleanFieldLinks($listName){  
   $list = $web.GetList($listName)  
      ForEach($ctype in $list.ContentTypes){  
      write-host $ctype.Name": "$ctype.ID  
           $ct = $ctype  
           $flDub = $ct.FieldLinks | group-object DisplayName | where { $_.Count -gt 1 }  
           foreach($fl in $flDub) {  
                $skipFirst = $fl.Group | select-object -skip 1  
                foreach($flDel in $skipFirst){  
                     $ct.FieldLinks.Delete($flDel.Id)  
                }  
           }    
           $ct.Update()  
      }  
 }  
 cleanFieldLinks("/Library")  
 $web.Dispose()  


Source

Wednesday, September 3, 2014

Custom SharePoint web service - item level permissions


If you are reading this post, you have propably already found out that SharePoint 2010 native web services do not support setting permissions on an item level. Luckily you can create one yourself.

Again, im not going to cover the basics about creating a web service for SharePoint 2010. You can find an answer to that in my other post regarding SharePoint web services.

So here is the code for the web service that allows you to set permissions on an single item or a document:
   public class Service : System.Web.Services.WebService  
   {  
     public Service()  
     {  
     }  
     [WebMethod]  
     public string ItemPermission(string SitePath, string LibName, string ItemId, string Permissions)  
     {  
       // Permission setting pattern User/Group;#UserLoginName/GroupName;#Reader/Contributor  
       string ReturnVal = "Permission set. OK!";  
       string[] userGroupPermissions;  
       userGroupPermissions = Permissions.Split(new string[] { ";#" }, StringSplitOptions.None);  
       int multiplier = 0;  
       string[][] permissionMatrix = new string[multiplier][];  
       try  
       {  
         multiplier = (userGroupPermissions.Length) / 3;  
         permissionMatrix = new string[multiplier][];  
         for (int i = 1; i <= multiplier; i++)  
         {  
           permissionMatrix[i - 1] = new string[3];  
           for (int a = 0; a < 3; a++)  
           {  
             permissionMatrix[i - 1][a] = userGroupPermissions[((i - 1) * 3) + a];  
           }  
         }  
       }  
       catch (Exception e)  
       {  
         ReturnVal = "Permission string is not valid. Peale use pattern ;#User/Group;#UserLoginName/GroupName;#Reader/Contributor;# .\r\n Exception: " + e.Message + "\r\n StackTrace: " + e.StackTrace;  
       }  
       try  
       {  
         SPSecurity.RunWithElevatedPrivileges(delegate()  
         {  
         SPSite WebApp = new SPSite(SitePath);  
         SPWeb Site = WebApp.OpenWeb();  
         SPList list = Site.Lists[LibName];  
         SPQuery newSPQuery = new SPQuery();  
         newSPQuery.Query = "<Where><Eq><FieldRef Name='ID' /><Value Type='Counter'>" + ItemId + "</Value></Eq></Where>";  
         newSPQuery.ViewAttributes = "Scope=\"RecursiveAll\"";  
         SPListItemCollection listItemCol = list.GetItems(newSPQuery);  
           if (listItemCol.Count > 0)  
           {  
             foreach (SPListItem item in listItemCol)  
             {  
               if (!item.HasUniqueRoleAssignments)  
               {  
                 item.BreakRoleInheritance(true);  
               }  
               Site.EnsureUser(this.User.Identity.Name);  
               SPUser CurrentUser = Site.EnsureUser(this.User.Identity.Name);  
               SPRoleAssignmentCollection SPRoleAssColn = item.RoleAssignments;  
               for (int i = SPRoleAssColn.Count - 1; i >= 0; i--)  
               {  
                   SPRoleAssColn.Remove(i);  
               }  
               WebApp.AllowUnsafeUpdates = true;  
               Site.AllowUnsafeUpdates = true;  
               item.Update();  
               SPRoleDefinition RoleDefinition;  
               for (int i = 0; i < multiplier; i++)  
               {  
                 if (permissionMatrix[i][2] == "Contributor") { RoleDefinition = Site.RoleDefinitions.GetByType(SPRoleType.Contributor); }  
                 else { RoleDefinition = Site.RoleDefinitions.GetByType(SPRoleType.Reader); }  
                 if (permissionMatrix[i][0] == "Group")  
                 {  
                   SPGroup spGroup = Site.SiteGroups[permissionMatrix[i][1]];  
                   SPRoleAssignment RoleAssignment = new SPRoleAssignment(spGroup);  
                   RoleAssignment.RoleDefinitionBindings.Add(RoleDefinition);  
                   item.RoleAssignments.Add(RoleAssignment);  
                 }  
                 else if (permissionMatrix[i][0] == "User")  
                 {  
                   SPUser spUser = Site.EnsureUser(permissionMatrix[i][1]);  
                   SPRoleAssignment RoleAssignment = new SPRoleAssignment(spUser);  
                   RoleAssignment.RoleDefinitionBindings.Add(RoleDefinition);  
                   item.RoleAssignments.Add(RoleAssignment);  
                 }  
               }  
               WebApp.AllowUnsafeUpdates = true;  
               Site.AllowUnsafeUpdates = true;  
               item.Update();  
             }  
           }  
           WebApp.AllowUnsafeUpdates = false;  
           Site.AllowUnsafeUpdates = false;  
         });  
       }  
       catch (Exception ex)  
       {  
         ReturnVal = "Permission not set, reason: " + ex.Message + "\r\n StackTrace: " + ex.StackTrace;  
       }  
       return ReturnVal;  
     }  
   }  

The code is a bit of a mess, but it works. I will walk you trough it.

Because this web service allows you to set permissions for multiple users and groups with single soap request, we are first going to find out how many users or groups are there defined. 

Next we are making a query to find our item we wish to set permissions on.

Then we will loop through all the permissions that the item already has and delete them. It was something that was needed for my integration project and i didn't want to write two more methods for viewing and deleting item permissions.

I had only two permission sets that needed to be set, so next i loop through all the users and groups that are defined in the request and set them right permission sets and finally add these users with right permission to the item.

You can access the web service from
http://{yourservername:port}/_layouts/ItemLevelPermission/ItemLevelPermission.asmx

There you can find a method named ItemPermission. Here is an example of a soap request:

 <soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
  <soap:body>  
   <itempermission xmlns="http://tempuri.org/">  
    <sitepath>http://sharepoint:80</sitepath>  
    <libname>Documents</libname>  
    <itemid>1234</itemid>  
    <permissions>User;#CONTOSO\adm.user;#Reader;#Group;#Administrators;#Contributor</permissions>  
   </itempermission>  
  </soap:body>  
 </soap:envelope>  

Test it with SoapUI. Here are instructions how to Using SoapUI to test SharePoint web services.

Source: http://bit.ly/1uyfM2G
.wsp: http://bit.ly/1nwEL2t

Using SoapUI to test SharePoint web services

SharePoint has number of built-in web services you can use for your integration projects. You can see the list here SharePoint 2010 Web Services .

To test them out you could use tool named SoapUI that allows you to build, send and receiver soap messages. I will give you instructions how to do that. First you should download SoapUI from http://www.soapui.org/  and go ahead and install it.

Launch the program. In the file menu you can create new SOAP project.


It will then ask you to insert a project name and show the location of a initial wsdl. I will name my project as SharePoint Web Services and give it a http://my-server:8000/_vti_bin/Lists.asmx?wsdl as an initial wsdl. List.asmx has several operations to manage your lists and librarys. It also a create idea to let it create sample requests fot the operations. For all the web services you can get their wsdl's by addint ?wsdl at the end of a address.


You are then prompted to insert credentials for your SharePoint site. After successful authentication you should see windows similar to this:


For this example i will use soap 1.2 operation named GetList. If i open up the sample Request 1 under GetList operation i can see the request that SoapUI has generated form me. I will change the "?" under listName value to "Documents", because i know i have such a library on that site. Clicking the arrow in the left upper corner of the windows will send the request.


If i send the request now i get 401 Unauthorized. SoapUI will not do NTLM authentication, so you need to set the right credentials in every request you make. To do that click on "Auth" button in the request window and select "Add New Authorization..." from the dropdown menu. Select NTML type.


After that you are asked for credentials with what you wish to perform that request. If you now hit run and everything goes well you should see the XML response with all the info about fields, views and other settings that the list has.


I have one more tip to save you from too much trouble - if you use operations like UpdateListItem where you need to define fields you are updating, always add Name and DisplayName to describe the field. Most of the time, adding Name is enough, but sometimes you get a nasty uninformative error and you spend an hour trying to understand why your request doesn't work.

Tuesday, September 2, 2014

Custom SharePoint file replacing web service

SharePoint built-in web services do not support replacing files in document libraries. I had an integration project where metadata was sent with dummy file and updated later with the right document. I managed to do everything with SharePoint native web services except updating file and it's extension. So i created a custom web service for that purpose.

I will not cover how to create SharePoint custom web service, you can read about it here Walkthrough: Creating a Custom ASP.NET (ASMX) Web Service in SharePoint 2010

Code for the web service is:

   public class Service : System.Web.Services.WebService  
   {  
     public Service()  
     {  
       //Uncomment the following line if using designed components  
       //InitializeComponent();  
     }  
     [WebMethod]  
     public string ReplaceFile(string SiteUrl, string LibName, int ItemId, string NewDocName, byte[] FileByteArray)  
     {  
       string ReturnVal = "File uploaded and renamed. OK!";  
       try  
       {  
         SPSecurity.RunWithElevatedPrivileges(delegate()  
         {  
           SPSite site = new SPSite(SiteUrl);  
           SPWeb web = site.OpenWeb();  
           web.AllowUnsafeUpdates = true;  
           SPList list = web.Lists[LibName];  
           SPItem item = list.GetItemById(ItemId);  
           string oldFileUrl = item["EncodedAbsUrl"].ToString();  
           SPFile file = web.GetFile(oldFileUrl);  
           item["Title"] = NewDocName;  
           item.Update();  
           if (file.CheckOutType != SPFile.SPCheckOutType.None)  
           {  
             file.UndoCheckOut();  
           }  
           file.CheckOut();  
           file.SaveBinary(FileByteArray);  
           file.MoveTo(SiteUrl + item["FileDirRef"].ToString() + "/" + NewDocName, true);  
           file.Update();  
           file.CheckIn("");  
           web.Dispose();  
         });  
       }  
       catch (Exception ex)  
       {  
         return ex.Message.ToString(); ;  
       }  
       return ReturnVal;  
     }  
   }  

The code runs under elevated privileges, so every user can execute this regardless their permissions. If your solution requires that users could only do this on documents they have permissions for, then you should remove the delegation part. It also checks if the file is checked out and if is, discards it.

So if i depoly this web service i can access it from http://yourserver:80/_ReplaceFileWS/ReplaceFile.asmx
There i can see that i have one method named ReplaceFile and if i click on it i can see my web service request and response examples.


For request i must know the url of a SharePoint site where my replaceable file is located at (in this example it is http://yourserver:80), document library display name where the replaceable file is in, SharePoint ID of the replaceable file, name for the new document with file extension and the new document itself as byte array encoded in base64.
For response we get a string saying "File uploaded and renamed. OK!" if everything was correct or the exception message if there were some problems.

You can test this solution with a great tool named SoapUI. Here are instructions how Using SoapUI to test SharePoint web services

Souce: http://bit.ly/Y8bwNt
.wsp package: http://bit.ly/W5sN8z