Moving a DocumentSet through code – pt1

Moving a DocumentSet through code – pt1 header image

One of the new features in SharePoint 2010 are the so called DocumentSets, a sort of folders-like option that allows you to manage multiple documents as they where a set, setting global metadata or capturing versions and downloading multiple documents. Since those options sounds pretty cool I recon it’s going to be a much used option within SharePoint 2010, however once you created a DocumentSet moving it around can be pretty hard.

By default there are a few ways to move around your environment:

  • You can move items around in the Explorer view of your libraries, and doing so with a DocumentSet results in the copying of a folder, losing all metadata for your DocumentSet (and as you might have noticed once you set the ContentType back to DocumentSet it still shows as a folder).
  • On the other hand you can use the “send-to” option you get, but that will results in the sending of a ZIP package, still losing the information as it seems.
  • The only way by default moving around works, is by using the Site Content and Structure.

So we decided to check if we could manage it through code. In this first part there will be some PowerShell examples on how to move a DocumentSet and where you might find yourself trying to do so, in the next part there will be some focus on how to move the DocumentSet through the ribbon, including all the metadata of it.

According to TechNet a DocumentSet is a special type of folder, and checking out MSDN shows that the the DocumentSet object does not have any options to move it to another location. The export function results in a packaged file (zip), but importing is a crime, and so far i didn’t get that to work.

$moveFromUrl = "aSiteUrl"
$newfolder = $site.OpenWeb().GetFolder("aFolderUrl")
$movefromList=$site.OpenWeb().GetList($moveFromUrl)
$docSet = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]::GetDocumentSet($newfolder)
$compressedFile = $x.Export()
$compressedFile.GetType()

$docsetID = [Microsoft.SharePoint.SPBuiltInContentTypeId]::DocumentSet
$targetFolder = $movefromList.RootFolder
$targetFolder.GetType()
$user = $site.OpenWeb().EnsureUser("aUser")
$user.GetType()

$properties = new-object System.Collections.Hashtable
$properties.Add("DocumentSetDescription", "Description")

$z = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]::Import($compressedFile, "DocsetRestore",$targetFolder, $docsetID, $properties, $user);

Results in a nice error telling me nothing.

IsPublic IsSerial Name                 BaseType
-------- -------- ----                 --------
True     True     Byte[]               System.Array
True     False    SPFolder             System.Object
True     False    SPUser               Microsoft.SharePoint.SPPrincipal
Exception calling "Import" with "6" argument(s): "DocID: Site prefix not set."
At :line:94 char:76
+ $z = [Microsoft.Office.DocumentManagement.DocumentSets.DocumentSet]
   ::Import <<<< ($compressedFile, "Docset1Backup",$targetFolder,
   $docsetID, $properties, $user);

So I came up with a new approach, it is a Folder with a ContentType set to it, so I used the folder ‘MoveTo’ and updated the ContentType of the folder back to a ‘DocumentSet’, that lead to the same error I would do that through the browser, it still was a folder, without the DocumentSet welcome page set to it. After some debugging I found out that there was a field that differs between a folder and a DocumentSet: HTML File Type. So i made some changes and was able to move a DocumentSet with PowerShell. Below you can find the code, where you can see how we update the folder,in pt2 there will be some more info on how you can do so with a nice custom ribbon command, and also move all custom fields.

param([string]$moveFromUrl, [string]$moveToUrl, [string]$moveItem)
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
##########################################################################
###                                                                    ###
###                            Functions                               ###
###                                                                    ###
##########################################################################
function checkItemToMove ([Microsoft.SharePoint.SPList]$movefromList, [Microsoft.SharePoint.SPList]$movetoList, [string]$moveItem)
{
  $docsetID = [Microsoft.SharePoint.SPBuiltInContentTypeId]::DocumentSet
  [Microsoft.SharePoint.SPFolder]$docsetToMove = $null
  foreach($docset in $movefromList.Folders)
  {
    if($docset.ContentType.ID.ToString().StartsWith($docsetID.ToString()) -and $docset.Name -eq $moveItem)
    {
      $docsetToMove = $docset.Folder
      $docsetContentTypeId = $docset.ContentType.Parent.Id
      break;
    }
  }
  if($docsetToMove -ne $null -and $docsetContentTypeId -ne $null)
  {
    Write-Host -ForegroundColor Green "Found a docset: " $docsetToMove.Name " Lets move it"
    moveDocSet $docsetToMove $movetoList $docsetContentTypeId
  }
  else { Write-Host -ForegroundColor Red "No document set of desired name found:" $moveItem }
}

function moveDocSet ([Microsoft.SharePoint.SPFolder]$docset, [Microsoft.SharePoint.SPList]$movetoList, [string]$docsetContentTypeId)
{
  $moveurl = $movetoList.RootFolder.ToString() + "/" + $docset.Name
  $docset.MoveTo($moveurl)
  #retrieve it at new location
  [Microsoft.SharePoint.SPFolder]$newDocset=$site.OpenWeb().GetFolder($moveurl)
  if($newDocset.Exists)
  {
    #update it so it is a doc set and set CT right
    $newDocset.Item["ContentTypeId"] = $docsetContentTypeId
    $newDocset.Item["HTML File Type"] = "SharePoint.DocumentSet"
    #TODO update all custom fields ..
    $newDocset.Item.Update()
    Write-Host -ForegroundColor Green " Docset moved succesfully ... parteh "
  }
  else {Write-Host -ForegroundColor Red " Failed moving the docset or setting ... "}
}

##########################################################################
###                                                                    ###
###                           /Functions                               ###
###                                                                    ###
##########################################################################
Write-Host ""
Write-Host -ForegroundColor Green "Move DocSet Script v1.0 - Albert-Jan Schot "
Write-Host ""
if($moveFromUrl -eq $null -or $moveFromUrl -eq "") {Write-Host -ForegroundColor Red "No folder to move from"; Exit}
if($moveToUrl -eq $null -or $moveToUrl -eq "") {Write-Host -ForegroundColor Red "No folder to move to"; Exit}
if($moveItem -eq $null -or $moveItem -eq "") {Write-Host -ForegroundColor Red "No DocumentSet name set"; Exit}

#Retrieves the desired objects
$site=new-object Microsoft.SharePoint.SPSite($moveFromUrl)
[Microsoft.SharePoint.SPList]$movefromList=$site.OpenWeb().GetList($moveFromUrl)
[Microsoft.SharePoint.SPList]$movetoList=$site.OpenWeb().GetList($moveToUrl)

#Move a docset
checkItemToMove $movefromList $movetoList $moveItem

There is one remark, if you try to move a DocumentSet this way to a library that does not have all the ContentTypes available that are used in the DocumentSet you will have a problem with the Version. If all ContentTypes are available it will take the version history of the DocumentSet and move it, if the ContentTypes aren’t available it will try to copy the version history, but if you try to display it, it will fail, giving a field not present error, so keep that in mind.

Loading comments…