Friday, 23 May 2014

Using PowerShell, CSOM, and some Hokey Pokey to copy files between two different SharePoint farms

Recently I needed to migrate a large number of images from a SharePoint 2010 farm, to a new SharePoint 2013 farm. I had local server access to the SharePoint 2013 farm servers, but only browser based access to the SharePoint 2010 farm.

PowerShell remoting wasn't an option, so I couple together a couple methods to come up with a solution. Use CSOM (via PoweShell) to get a list of the files (images) on the remote SharePoint library. Then use the WebClient class to download the file, and finally, the standard server based object model to add the downloaded image to the destination SharePoint farm libary.

What the hell, I hear you say?! But why not just use CSOM all the way?! Because, well, actually, I have no good reason. Probably because I was in a hurry, and this was the fastest way I could achieve my objective in the short amount of time I had! And sometimes, a quick solution to a problem is more valuable than "technically perfect" approach! They're my excuses anyway.

So, here's the PowerShell code I used to do it. It's commented, and if nothing else, it demonstrates that there is usually more than one way to approach a problem!! Oh yeah, it also demonstrates using PowerShell and SharePoint CSOM together!


function Import-FilesFromRemoteLibraryUsingCSOM{            
    [CmdletBinding()]                        
    Param(                           
            [parameter(Mandatory=$true)][string]$SourceWebApplicationUrl,            
            [parameter(Mandatory=$true)][string]$SourceWebUrl,            
            [parameter(Mandatory=$true)][string]$SourceLibrary,            
      [parameter(Mandatory=$true)][string]$DestinationWebUrl,            
            [parameter(Mandatory=$true)][string]$DestinationLibrary            
        )            
    #Load the Microsoft SharePoint Client Dll's            
    Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll";            
    Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll";             
    #Using the CSOM in PowerShell is very similar to using it in C#, with just a few minor differences.            
    #Get the Client Context, using the SourceWeb URL passed to the function. This is the web we will be getting the files from.             
    $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SourceWebUrl)            
    #Load the web            
    $w = $ctx.Web            
    $ctx.Load($w)            
    #Get and load the list that contains the files we want to migrate            
    $l = $w.Lists.GetByTitle($SourceLibrary)            
    $ctx.Load($l)            
    #Create a new query. This is where we could filter the list items (files) that we're going to migrate. In this example, we're just limiting the results to 1000 items.            
    $query = New-Object Microsoft.SharePoint.Client.CamlQuery            
    $query.ViewXml = "1000"            
    $items = $l.GetItems($query)            
    #Load the items            
    $ctx.Load($items)            
    #Execute the loaded queries.             
    $ctx.ExecuteQuery()            
    if($l -eq $null){Write-Host "List not found." -f Red;return;}            
    Write-Host "Attempting to import all library files." -f darkyellow;            
    #Create an array for holding a list of file URL's            
    $fileUrls = @();            
    #Get the destination web and list. Remember that this code is running on the destination SharePoint server, so we can just use the standard SharePoint cmdlet's            
    $dw = Get-SPWeb $DestinationWebUrl;            
    $dl = $dw.Lists[$DestinationLibrary];            
    #Create a WebClient object. We'll use this to download each file to a byte array, which we can upload into SharePoint.             
    #I chose to use this method, as it was quick, I was short on time, and I was having trouble getting a byte array using CSOM.            
    #It demonstrates an alternate approach to a problem!             
    $wc = New-Object System.Net.WebClient;             
    $credentials = [System.Net.CredentialCache]::DefaultCredentials;            
    $wc.Credentials = $credentials;            
    #For each item in the list of items retrieved from our source server (using CSOM), we create the URL, download the file as a byte array, and then upload it to the destination SharePoint server            
    foreach($i in $items){            
        #Load the item and file            
        $item = $l.GetItemById($i.Id);            
        $file = $item.File;            
        $ctx.Load($item);            
        $ctx.Load($file);            
        #Execute the query to get the items we loaded            
        $ctx.ExecuteQuery();            
        #Create the Url for the file            
        $itemUrl = ([String]::Format("{0}{1}",$SourceWebApplicationUrl,$item["FileRef"]));              
        $fileName = $itemUrl.Substring($itemUrl.LastIndexOf("/")+1);            
        Write-Host "Downloading file $itemUrl";            
        #Download the file as a byte array            
        $file = $wc.DownloadData($itemUrl);            
        #Upload the file to the destination SharePoint library            
        $nf = $dw.Files.Add(([String]::Format("{0}/{1}/{2}",$dw.Url,$dl.RootFolder.Url,$fileName)),$file,$true);            
        #If the destination list requires files to be checked in, then check it in!            
        if($nf.RequiresCheckout)            
        {               
         $nf.CheckIn("Checked in during migration","MajorCheckIn")             
        }              
        Write-host "Added file $fileName" -f Green;            
    }                
}

Tuesday, 6 May 2014

Use PowerShell to add and remove items in the Quick Launch menu on a SharePoint 2013 site

Here's a quick snippet of PowerShell that demonstrates adding menu items to the Quick Launch menu in a SharePoint 2013 site.

The example is based on a newly added Business Intelligence site, though the code will work on any site. The goal is to remove the existing quick launch menu items, and replace them with a list of report document libraries, grouped under a heading called, "Reports".

The original site looks like this:



The following PowerShell adds a list of links (to the report lists) onto the quick launch menu. Note that the new menu items are defined in the $listsToAddToNav array.

#SharePoint site url            
$weburl = "http://devhv131/sites/bi"            
#An array of SharePoint lists that you want to add to the navigation menu            
$listsToAddToNav = @('Building Management','Capital Programmes','Employee Services','Financial','General Reports','Payroll','Property','Purchasing');            
            
#Get the SPWeb object for the site url            
$w = get-spweb $weburl            
#Get the quick launch menu            
$ql = $w.Navigation.QuickLaunch;            
#Create the root node that all the lists will be displayed under.            
$n = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode("Reports","",$true);            
#Add the new node to the quick launch menu            
$n = $ql.AddAsFirst($n);             
#Add all the lists as new child nodes, to the "Reports" node            
foreach($list in $listsToAddToNav)            
{            
    #Create a new child node, for the current folder. The URL of the folder is constructed using the name of the folder, and the web URL            
 $cn = New-Object Microsoft.SharePoint.Navigation.SPNavigationNode($list,([String]::Format("{0}/{1}",$w.Url,$list.Replace(" ","%20"))),$true);             
    #Add the childnode to the parent node, which in this case, is the "Reports" navigation node.             
    #Add the child as the "last node". As the nodes being added are in alphabetical order, this will preserve the node order, as A-Z            
    $n.Children.AddAsLast($cn);            
    #Update (save) the navigation            
 $w.Update()            
}

After running the PowerShell above, the new menu items have been added.



This next bit of PowerShell removes the unwanted menu items from the quick launch. Note that the unwanted menu items are defined in the $nodesToDelete array.

#Create an array of nodes that you want to delete. The array contains the node names.        
$nodesToDelete = @('Dashboards','Data Connections','PerformancePoint Content','Recent','Libraries')            
#For each node, delete it from the Quick Launch menu            
foreach($dnname in $nodesToDelete)            
{            
 #Get the node            
 $dn = $w.Navigation.QuickLaunch | where { $_.Title -eq $dnname }            
 if($dn -eq $null){continue;}            
 Write-Host "Deleting navigation node, $dnname"            
    #If the node wasn't null, delete it!             
 $ql.Delete($dn);            
    #Update (save) the navigation            
 $w.Update();            
}

Finally, with the unwanted navigation menu items removed, the site now looks the way we want it!