Thursday 18 September 2014

Using the SPImport events to control SharePoint content import


The SPImport class in the Microsoft.SharePoint.Deployment namespace participates with other classes in the Deployment namespace to support importing, publishing, and migrating Windows SharePoint content, as well as supporting restore capabilities of content.
You can initiate an import operation by first initializing an instance of the Microsoft.SharePoint.Deployment.SPImportSettings class with the required import settings, and then passing the SPImportSettings object to the constructor of SPImport class; you then call the SPImport.Run method. The SPImport type exposes events that can be used to control the process of import. You can utilize the event model in PowerShell to register the events and then make use of this information to control the complete process of importing and exporting SharePoint content. Below is the PowerShell snippet to use the Error event and use the data to fail the execution process after an Import is performed.

$Global:importErrors = @()
$Global:importWarnings = @()  

try{      
     Register-ObjectEvent -InputObject $SPImport -EventName Error -SourceIdentifier Microsoft.SharePoint.Deployment.SPDeployment.Error -Action {
           $arguments = $event.SourceEventArgs
           if(($arguments.ErrorType -eq [Microsoft.SharePoint.Deployment.SPDeploymentErrorType]::Error) -or ($arguments.ErrorType -eq [Microsoft.SharePoint.Deployment.SPDeploymentErrorType]::FatalError)){
                $Global:importErrors += $arguments.ErrorMessage

           }
           if($arguments.ErrorType -eq [Microsoft.SharePoint.Deployment.SPDeploymentErrorType]::Warning){
                $Global:importWarnings += $arguments.ErrorMessage
           }

     } | Out-Null

     $SPImport.Run()
}
finally{

     Unregister-Event -SourceIdentifier Microsoft.SharePoint.Deployment.SPDeployment.Error -ErrorAction SilentlyContinue

} 

if($Global:importWarnings.Length -gt 0){
     Write-Warning ("SPImport generated {0} warnings during import process. Please refer to the SP-Import log file {1} for more details." -f $Global:importWarnings.Length, $logFile)
}

if($Global:importErrors.Length -gt 0){
     Write-Warning ("The below given errors were generated during import process. Please refer to the SP-Import log file {0} for more details." -f $logFile)
     $Global:importErrors | select -Unique | ForEach-Object {
           Write-Host $_
     }

     Write-Error ("SPImport failed with {0} errors during import process. The details of errors can be found in the SP-Import log file {1}" -f $Global:importErrors.Length, $logFile)
}

Write-Host ("SPImport process completed without errors. Please refer to the SP-Import log file {0} for more details" -f $logFile) -ForegroundColor Green

Export and import content types using PowerShell

SharePoint stores the definition schema of content types in an XML file that creates a content type in the element manifest file of a Feature. You can use the information present in the SchemaXML of a content type to migrate it between site collections using PowerShell.
Below is the screen shot of the schema definition of the Article Page content type as from the SharePoint manager application.


You can use this information to export the content type to an XML file as given below. The script expects the site collection url, group name under which the content types should be exported and the output directory location.

param ([string]$SiteUrl = 'http://yoursiteurl',[string] $GroupName = "Your Group Name", [string]$OutPutLocation = "E:\Powershell\SharePoint")
 
$ErrorActionPreference = 'Stop'
If ((Get-PsSnapin |?{$_.Name -eq "Microsoft.SharePoint.PowerShell"})-eq $null)
{
     Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null
}
$site = Get-SPSite $SiteUrl
$web = $site.RootWeb
if([System.IO.Directory]::Exists($OutPutLocation) -ne $true)
{
     Write-Error "Export path " $OutPutLocation " is not valid. Make sure that the directory exists"
}
$fileName = [System.IO.Path]::Combine($OutPutLocation, "content-types.xml")
Write-Host "Starting content type export to $fileName" -ForegroundColor Green
if([System.IO.File]::Exists($fileName) -ne $true)
     New-Item -path $OutPutLocation -name "content-types.xml" -type File
}
Add-Content $fileName "<?xml version=`"1.0`" encoding=`"utf-8`"?>"
Add-Content $fileName "`n<ContentTypes>"
$web.ContentTypes | ForEach-Object {
   if ($_.Group -eq $GroupName) {
           Write-Host -ForegroundColor Blue $_.Name
        Add-Content $fileName $_.SchemaXml
   }
}
Add-Content $fileName "</ContentTypes>"
$web.Dispose()

The exported data looks much simpler as given below.

Later you can use this data to import the content types back to another site collection or the same site collection as given below.
param([string] $SiteUrl = "http://yoursiteurl", [string]$OutPutLocation = "E:\Powershell\SharePoint")

$ErrorActionPreference = 'Stop'
If ((Get-PsSnapin |?{$_.Name -eq "Microsoft.SharePoint.PowerShell"})-eq $null)
{
     Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null
}
$site = Get-SPSite $SiteUrl
$web = $site.RootWeb
$fileName = [System.IO.Path]::Combine($OutPutLocation, "content-types.xml")
if([System.IO.File]::Exists($fileName) -ne $true){
     Write-Error "The content type export file $fileName does not exist"
}
$contentTypeExport = [xml](Get-Content($fileName))
$contentTypeExport.ContentTypes.ContentType | ForEach-Object {

     $name = $_.Name
     $contentType = $web.ContentTypes | Where-Object { $_.Name -eq $name }
     if($contentType -eq $null){
         $spContentType = New-Object Microsoft.SharePoint.SPContentType ($_.ID,$web.ContentTypes,$_.Name)
         $spContentType.Description = $_.Description
         $spContentType.Group = $_.Group
  
         $_.Fields.Field  | ForEach-Object {
             if(!$spContentType.FieldLinks[$_.DisplayName])
             {
                     $field = $web.Fields[$_.DisplayName]
                     if($field -ne $null){
                      $spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink ($web.Fields[$_.DisplayName])
                       if ($_.Required -eq "TRUE") {$spFieldLink.Required = $true}
                       if ($_.Hidden -eq "TRUE") {$spFieldLink.Hidden = $true}
                       $spContentType.FieldLinks.Add($spFieldLink)
                     }
             }
         }
         $newContentType = $web.ContentTypes.Add($spContentType)
         $spContentType.Update()
         Write-Host "Successfully imported content type $name" -ForegroundColor Green
     }  
     else{     
         Write-Host "Content type $name already exists" -ForegroundColor Yellow
     }
}
$web.Dispose()

Thursday 5 June 2014

SharePoint 2010 - Export and import global navigation

I was working on a script to import and export Global navigation including the navigation settings for SharePoint sites.
Here’s the full script.

The first script exports the global navigation settings to the GlobalNavigationSettings.xml file and the navigation nodes with the hierarchy to the GlobalNavigationNodes.xml file

param([string]$siteUrl, [string]$exportPath = 'C:\Temp') 
$ErrorActionPreference = 'Stop'

If ((Get-PsSnapin |?{$_.Name -eq "Microsoft.SharePoint.PowerShell"})-eq $null)
{
      Add-PsSnapin Microsoft.SharePoint.PowerShell - ErrorAction SilentlyContinue | Out-Null
}

function Export-GlobalNavigation-Settings
{
      param([Microsoft.SharePoint.Publishing.PublishingWeb]$publishingWeb, [string]$exportPath)
      if([System.IO.Directory]::Exists($exportPath-ne $true)
      {
            Write-Error "Export path " $exportPath " is not valid. Make sure that the directory exists"
      }
      $fileName = [System.IO.Path]::Combine($exportPath"GlobalNavigationSettings.xml")
      Write-Host "Exporting global navigation settings to " $fileName
      ($publishingWeb.Navigation | ConvertTo-Xml).Save($fileName)
      Write-Host -ForegroundColor Green "Successfully exported global navigation settings.."
}

function Add-To-XmlDocument
{
      param([Xml.XmlNode]$xnode, [Microsoft.SharePoint.Navigation.SPNavigationNodeCollection$nodes, [int$level)  
      foreach($node in $nodes)
      {
            Write-Host -ForegroundColor Blue "Adding new navigation node" $node.Title
            [Xml.XmlNode$navigationNodeElement = $siteNavigationDoc.CreateElement("NavigationNode")
            [System.Xml.XmlAttribute$titleAttribute = $siteNavigationDoc.CreateAttribute("title")
            $titleAttribute.Value = $node.Title
            [System.Xml.XmlAttribute$urlAttribute = $siteNavigationDoc.CreateAttribute("url")
            $urlAttribute.Value = $node.Url
            [System.Xml.XmlAttribute$isVisibleAttribute = $siteNavigationDoc.CreateAttribute("isVisible")
            $isVisibleAttribute.Value = [string]$node.IsVisible
            [System.Xml.XmlAttribute$idAttribute = $siteNavigationDoc.CreateAttribute("id")
            $idAttribute.Value = [string]$node.Id
            [System.Xml.XmlAttribute$levelAttribute = $siteNavigationDoc.CreateAttribute("level")
            $levelAttribute.Value = [string]$level
            $navigationNodeElement.Attributes.Append($titleAttribute)
            $navigationNodeElement.Attributes.Append($urlAttribute)
            $navigationNodeElement.Attributes.Append($isVisibleAttribute)
            $navigationNodeElement.Attributes.Append($idAttribute)
            $navigationNodeElement.Attributes.Append($levelAttribute)
           
            if($node.Properties.Count -gt 0)
            {          
                  foreach($property in $node.Properties.GetEnumerator())
                  {
                        [Xml.XmlNode$propertyNode = $siteNavigationDoc.CreateElement("Property")
                        [Xml.XmlAttribute$keyAttribute = $siteNavigationDoc.CreateAttribute("key")
                        $keyAttribute.Value = [string]$property.Key
                        [Xml.XmlAttribute$valueAttribute = $siteNavigationDoc.CreateAttribute("value")
                        $valueAttribute.Value = [string]$property.Value
                        $propertyNode.Attributes.Append($keyAttribute)
                        $propertyNode.Attributes.Append($valueAttribute)
                        $navigationNodeElement.AppendChild($propertyNode)
                  }
            }
           
            $xnode.AppendChild($navigationNodeElement)
           
            Write-Host -ForegroundColor Green "Successfully added new navigation node" $node.Title
            if ($node.Children.Count -gt 0)
        {
                  $newLevel = $level + 1
            Add-To-XmlDocument $navigationNodeElement $node.Children $newLevel
        }                          
      }
}

function Export-GlobalNavigation-Nodes
{
      param([Microsoft.SharePoint.Publishing.PublishingWeb]$publishingWeb, [string]$exportPath)
      if([System.IO.Directory]::Exists($exportPath-ne $true)
      {
            Write-Error "Export path " $exportPath " is not valid. Make sure that the directory exists"
      }
      $fileName = [System.IO.Path]::Combine($exportPath"GlobalNavigationNodes.xml")
      Write-Host "Exporting global navigation settings to " $fileName
     
    [System.Xml.XmlNode$declarationNode = $siteNavigationDoc.CreateXmlDeclaration("1.0""UTF-8"$null)
    $siteNavigationDoc.AppendChild($declarationNode)

    [System.Xml.XmlNode$globalNavigationNode = $siteNavigationDoc.CreateElement("NavigationNodes")
    $siteNavigationDoc.AppendChild($globalNavigationNode)
     
      Add-To-XmlDocument $globalNavigationNode $publishingWeb.Navigation.GlobalNavigationNodes $nodeLevel
      $siteNavigationDoc.Save($fileName)
      Write-Host -ForegroundColor Green "Successfully exported global navigation settings.."
}


$site = Get-SPSite $siteUrl
$rootWeb = $site.RootWeb
$pubWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($rootWeb)
$nodeLevel = 1
Export-GlobalNavigation-Settings $pubWeb $exportPath

[System.Xml.XmlDocument$siteNavigationDoc = New-Object System.XML.XMLDocument    
Export-GlobalNavigation-Nodes $pubWeb $exportPath

The script creates two xml files, one with the navigation settings and one with the node hierarchy.




The second script is used to import the settings from the GlobalNavigationSettings.xml file and create navigation nodes from the GlobalNavigationNodes.xml file

param([string]$siteUrl, [string]$exportPath = 'C:\Temp')
$ErrorActionPreference = 'Stop'

If ((Get-PsSnapin |?{$_.Name -eq "Microsoft.SharePoint.PowerShell"})-eq $null)
{
      Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null
}

function Import-GlobalNavigation-Settings
{
      param([Microsoft.SharePoint.Publishing.PublishingWeb]$publishingWeb, [string]$exportPath)
      if([System.IO.Directory]::Exists($exportPath-ne $true)
      {
            Write-Error "Export path " $exportPath " is not valid. Make sure that the directory exists"
      }
      $fileName = [System.IO.Path]::Combine($exportPath"GlobalNavigationSettings.xml")
      if([System.IO.File]::Exists($fileName-ne $true)
      {
            Write-Error "Unable to find site navigation settings file " $fileName
      }
      [xml]$settings = Get-Content $fileName
      $root = $settings.DocumentElement  
     
      Write-Host "Updating global navigation settings"
      Write-Host $root.SelectSingleNode('descendant::Object/Property[@Name="InheritGlobal"]').'#text'
      $var = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="GlobalIncludeSubSites"]').'#text')
      Write-Host $var
     
      $publishingWeb.Navigation.InheritGlobal = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="InheritGlobal"]').'#text')
      $publishingWeb.Navigation.GlobalIncludeSubSites = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="GlobalIncludeSubSites"]').'#text')
      $publishingWeb.Navigation.GlobalIncludePages = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="GlobalIncludePages"]').'#text')
      $publishingWeb.Navigation.GlobalDynamicChildLimit = [int]$root.SelectSingleNode('descendant::Object/Property[@Name="GlobalDynamicChildLimit"]').'#text'

      Write-Host "Updating current navigation settings"
      $publishingWeb.Navigation.InheritCurrent = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="InheritCurrent"]').'#text')
      $publishingWeb.Navigation.ShowSiblings = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="ShowSiblings"]').'#text')
      $publishingWeb.Navigation.CurrentIncludeSubSites = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="CurrentIncludeSubSites"]').'#text')
      $publishingWeb.Navigation.CurrentIncludePages = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="CurrentIncludePages"]').'#text')
      $publishingWeb.Navigation.CurrentDynamicChildLimit = [int]$root.SelectSingleNode('descendant::Object/Property[@Name="CurrentDynamicChildLimit"]').'#text'

      Write-Host "Updating rootweb navigation sorting settings"
      $publishingWeb.Navigation.OrderingMethod = [Microsoft.SharePoint.Publishing.OrderingMethod]$root.SelectSingleNode('descendant::Object/Property[@Name="OrderingMethod"]').'#text'
      $publishingWeb.Navigation.AutomaticSortingMethod = [Microsoft.SharePoint.Publishing.AutomaticSortingMethod]$root.SelectSingleNode('descendant::Object/Property[@Name="AutomaticSortingMethod"]').'#text'
      $publishingWeb.Navigation.SortAscending = [System.Convert]::ToBoolean($root.SelectSingleNode('descendant::Object/Property[@Name="SortAscending"]').'#text')

      #Update the Publishing Web Navigation Settings
      $publishingWeb.Update()
      Write-Host -ForegroundColor Green "Successfully imported global navigation settings.."
}


function Populate-NavigationNodes
{
      param ([Microsoft.SharePoint.Navigation.SPNavigationNodeCollection$nodes, [Xml.XmlNodeList$xNodes, [int$level)
      $level = $level + 1;
      foreach($xNode in $xNodes)
      {
            $title = $xNode.Title
            $url = $xNode.Url
            $isVisible =[Boolean]$xNode.IsVisible    
            $CreateNavigationNodeMethod = [Microsoft.SharePoint.Publishing.Navigation.SPNavigationSiteMapNode]::CreateSPNavigationNode
                 
            $nodeTypePropertyElement = $xNode.SelectSingleNode('descendant::Property[@key="NodeType"]')
            $nodeType = [Microsoft.SharePoint.Publishing.NodeTypes]::AuthoredLinkPlain
            if($nodeTypePropertyElement -ne $null)
            {
                  $nodeType = [System.Enum]::Parse([Microsoft.SharePoint.Publishing.NodeTypes], $nodeTypePropertyElement.value)
            }
            Write-Host -ForegroundColor Blue "Creating navigation node " $title
            $spNavigationNode = $CreateNavigationNodeMethod.Invoke($title$url$nodeType$nodes)
            $spNavigationNode.IsVisible = $isVisible
            $spNavigationNode.Update()
            Write-Host -ForegroundColor Green "Successfully created navigation node " $title
           
            $xPath = 'descendant::NavigationNode[@level="' + [string]$level + '"]'
           
            $navigationNodeElements = $xNode.SelectNodes($xPath)
            if($navigationNodeElements.Count -gt 0)
            {
                  Populate-NavigationNodes $spNavigationNode.Children $navigationNodeElements $level
            }
      }
}

function Import-GlobalNavigation-Nodes
{
      param([Microsoft.SharePoint.Publishing.PublishingWeb]$publishingWeb, [string]$exportPath)
      if([System.IO.Directory]::Exists($exportPath-ne $true)
      {
            Write-Error "Export path " $exportPath " is not valid. Make sure that the directory exists"
      }
      $fileName = [System.IO.Path]::Combine($exportPath"GlobalNavigationNodes.xml")
      if([System.IO.File]::Exists($fileName-ne $true)
      {
            Write-Error "Unable to find site navigation nodes file " $fileName
      }
     
      Write-Host "Removing existing global navigation nodes"
      $sharePointGlobalNodes = @(1000, 1002, 1025)
      for($i = $publishingWeb.Navigation.GlobalNavigationNodes.Count-1; $i -ge 0; $i--)
      {
            $node = $publishingWeb.Navigation.GlobalNavigationNodes[$i]
            if(($sharePointGlobalNodes -contains $node.Id-ne $true)
            {
                  Write-Host -ForegroundColor Blue $node.Title
                  $node.Delete()
                  $node.Update
            }
      }    
      $publishingWeb.Update()
     
      [xml]$nodesContent = Get-Content $fileName
      $root = $nodesContent.DocumentElement
      $spNavigationNodeElements = $root.SelectNodes('descendant::NavigationNode[@level="1"]')
     
      Populate-NavigationNodes $publishingWeb.Navigation.GlobalNavigationNodes $spNavigationNodeElements 1
      $publishingWeb.Update()
}

$site = Get-SPSite $siteUrl
$rootWeb = $site.RootWeb
$pubWeb = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($rootWeb)
Import-GlobalNavigation-Settings $pubWeb $exportPath
Import-GlobalNavigation-Nodes $pubWeb $exportPath