{"id":1184,"date":"2014-04-08T17:29:33","date_gmt":"2014-04-08T16:29:33","guid":{"rendered":"http:\/\/blog.repsaj.nl\/?p=1184"},"modified":"2014-04-08T17:29:33","modified_gmt":"2014-04-08T16:29:33","slug":"sp-purging-document-libraries-with-powershell","status":"publish","type":"post","link":"http:\/\/blog.repsaj.nl\/index.php\/2014\/04\/sp-purging-document-libraries-with-powershell\/","title":{"rendered":"SP: Purging document libraries with Powershell"},"content":{"rendered":"<p>I wanted to purge the contents of a huge document library. Because of the 5000 item limit, using the GUI or explorer view wasn&#8217;t posssible any more. There are a couple of scripts out there which can help, based on the ProcessBatchData method to process batches of operations at once. This is often the best scenario, because deleting items one by one takes a lot of time. <\/p>\n<p>Now upon deletion, you can get all kinds of non descriptive errors (operation failed, 0xblabla&#8230;). When your script fails after some items were deleted, there is a good chance check-outs are in the way of deletions. Checked-out files have to be checked-in again before they can be deleted, and folders to be deleted cannot contain any checked-out items either. So I create a purge script which takes care of all of these kinds of things, before deleting the entire contents of a library. I&#8217;m saving it here mainly for my own reference, but feel free to use it when you want to!<br \/>\n<!--more--><\/p>\n<pre class=\"prettyprint\">\r\nAdd-PSSnapIn Microsoft.SharePoint.Powershell -ErrorAction SilentlyContinue\r\n\r\n# Appends a line to the deletion builder and outputs a line every \r\n# 100 items to track progress\r\nfunction AppendLine($line)\r\n{\r\n    $deleteBldr.Append($line) > $null  # global variable declared in PurgeDocumentLibrary\r\n    Set-Variable -Name lines -Value ($lines + 1) -Scope Global\r\n    \r\n    # Write a line every 100 items to track the progress on this library\r\n    if ($lines % 100 -eq 0)\r\n    {\r\n        Write-Host \"Appended line $lines of $itemcount\" \r\n    }\r\n}\r\n\r\n# Helper function which processes an item. This way it doesn't matter \r\n# what type you put in, the correct function is always executed, or \r\n# an error is thrown when the type is unusable\r\nfunction ProcessItem($item)\r\n{\r\n    if ($item -eq $null)\r\n    {\r\n        break\r\n    }\r\n\r\n    if ($item -is [Microsoft.SharePoint.SPFolder])\r\n    {\r\n        ProcessFolder($item)\r\n    }\r\n    elseif ($item.FileSystemObjectType -eq \"Folder\")\r\n    {\r\n        ProcessFolder($item.Folder)\r\n    }\r\n    elseif ($item -is [Microsoft.SharePoint.SPFile])\r\n    {\r\n        ProcessFile($item)\r\n    }\r\n    elseif ($item.FileSystemObjectType -eq \"File\")\r\n    {\r\n        ProcessFile($item.File)\r\n    }\r\n    else\r\n    {\r\n        throw \"Woops, the $item of type $($item.GetType()) cannot be processed\" \r\n    }\r\n}\r\n\r\n# Processes a file; check-in when required and then append to delete\r\nfunction ProcessFile($file)\r\n{\r\n    # When there is no item linked to this file, its a system file; break and ignore\r\n    if ($file.Item -eq $null)\r\n    {\r\n        break\r\n    }\r\n    \r\n    # When the file is checked out in some way; forcibly check it in\r\n    if ($file.CheckOutType -ne \"None\")\r\n    {\r\n        $file.CheckIn(\"Automatic checkin\")\r\n        $file.Update()\r\n    }\r\n        \r\n    $line = [System.String]::Format($command, $file.Item.ID.ToString(), $file.ServerRelativeUrl)\r\n    AppendLine $line\r\n}\r\n\r\n# Processes a folder; first all subfolders and files in the folder and then\r\n# the folder itself. Appends a line for bulk deletion\r\nfunction ProcessFolder($folder)\r\n{\r\n    foreach ($item in $folder.SubFolders)\r\n    {\r\n        ProcessItem($item)\r\n    }\r\n    \r\n    foreach ($item in $folder.Files)\r\n    {\r\n        ProcessItem($item)\r\n    }\r\n    \r\n    # only delete a folder when it has an item linked to it... folders without an item are system folders\r\n    if ($folder.item -ne $null)\r\n    {\r\n        $line = [System.String]::Format($command, $folder.Item.ID.ToString(), $folder.ServerRelativeUrl)\r\n        AppendLine $line\r\n    }\r\n}\r\n\r\n# Processes a document library and purges all of its content. This includes check-in of files\r\n# without a checked-in version, or plain checked-out files (which cannot be deleted)\r\nfunction PurgeDocumentLibrary( [Microsoft.SharePoint.SPWeb] $spweb, [Microsoft.SharePoint.SPList] $splist)\r\n{ \r\n    Write-Host \"Processing library $($splist.Title) with $($splist.ItemCount) items\"\r\n    \r\n    # Create the batch string builder containing the command XML\r\n    [System.Text.StringBuilder] $deleteBldr = New-Object \"System.Text.StringBuilder\"; \r\n    $deleteBldr.Append(\"<?xml version=\"\"1.0\"\" encoding=\"\"UTF-8\"\"?><Batch>\");   \r\n    $command = \"<Method><SetList Scope=\"\"Request\"\">\" + $splist.ID + \"<\/SetList><SetVar Name=\"\"ID\"\">{0}<\/SetVar><SetVar Name=\"\"owsfileref\"\">{1}<\/SetVar><SetVar Name=\"\"Cmd\"\">Delete<\/SetVar><\/Method>\";\r\n    \r\n    # variable used in output to screen, to track progress\r\n    $itemcount = $splist.FolderCount + $splist.ItemCount\r\n\r\n    # forcibly check in any files which do not have a checked-in version... \r\n    # otherwise these will cause errors    \r\n    foreach ($checkedOutFile in $splist.CheckedOutFiles)\r\n    {\r\n        $checkedOutFile.TakeOverCheckOut();\r\n        $listItem = $splist.GetItemById($checkedOutFile.ListItemId);\r\n        $listItem.File.CheckIn(\"Automated checkin\");\r\n    }\r\n\r\n    # Process the root folder which will then recursively process all sub items,    \r\n    ProcessItem $list.RootFolder\r\n    \r\n    # append closing tag for proper XML structure\r\n    $deleteBldr.Append(\"<\/Batch>\")\r\n    \r\n    # Process the batch command and update the web (not strictly necessary)\r\n    $command = $deleteBldr.ToString()\r\n    $spweb.ProcessBatchData($command)\r\n    $spweb.Update()\r\n}    \r\n\r\n# start assignment for disposal\r\nStart-SPAssignment \u2013Global\r\nSet-Variable -Name lines -Value 0 -Scope \"global\"\r\n\r\n# Get the web object, retrieve the list and call the Purge method\r\n$web = Get-SPWeb \"http:\/\/www.contoso.com\/subsite\"\r\n$list = $web.Lists[\"Shared Documents\"]\r\nPurgeDocumentLibrary $web $list\r\n\r\n# stop assignment for disposal\r\nStop-SPAssignment -Global\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I wanted to purge the contents of a huge document library. Because of the 5000 item limit, using the GUI or explorer view wasn&#8217;t posssible any more. There are a couple of scripts out there which can help, based on the ProcessBatchData method to process batches of operations at once. This is often the best<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[34],"tags":[45,7],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p3KFR1-j6","_links":{"self":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/posts\/1184"}],"collection":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/comments?post=1184"}],"version-history":[{"count":0,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/posts\/1184\/revisions"}],"wp:attachment":[{"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/media?parent=1184"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/categories?post=1184"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.repsaj.nl\/index.php\/wp-json\/wp\/v2\/tags?post=1184"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}