[CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$WebUrl, [Parameter(Mandatory = $true)] [string]$OutputPath, [switch]$IncludeHiddenLibraries ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" function Initialize-SharePointPowerShell { if (-not (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue)) { Add-PSSnapin Microsoft.SharePoint.PowerShell } } function Ensure-Directory { param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not [System.IO.Directory]::Exists($Path)) { [System.IO.Directory]::CreateDirectory($Path) | Out-Null } } function Get-SafePathSegment { param( [Parameter(Mandatory = $true)] [string]$Name ) $invalidChars = [System.IO.Path]::GetInvalidFileNameChars() $safeName = $Name foreach ($char in $invalidChars) { $safeName = $safeName.Replace($char, "_") } $safeName = $safeName.Trim() if ([string]::IsNullOrWhiteSpace($safeName)) { return "_" } return $safeName } function Combine-PathSegments { param( [Parameter(Mandatory = $true)] [string]$BasePath, [string[]]$Segments = @() ) $combinedPath = $BasePath foreach ($segment in $Segments) { if (-not [string]::IsNullOrWhiteSpace($segment)) { $combinedPath = [System.IO.Path]::Combine($combinedPath, $segment) } } return $combinedPath } function Convert-SPFieldValue { param( [Parameter(ValueFromPipeline = $true)] $Value ) if ($null -eq $Value) { return $null } if ($Value -is [System.DateTime]) { return $Value.ToString("o") } if ($Value -is [Microsoft.SharePoint.SPFieldUserValue]) { return @{ LookupId = $Value.LookupId LookupValue = $Value.LookupValue Email = $Value.User.Email LoginName = $Value.User.LoginName } } if ($Value -is [Microsoft.SharePoint.SPFieldUserValueCollection]) { return @($Value | ForEach-Object { Convert-SPFieldValue $_ }) } if ($Value -is [Microsoft.SharePoint.SPFieldLookupValue]) { return @{ LookupId = $Value.LookupId LookupValue = $Value.LookupValue } } if ($Value -is [Microsoft.SharePoint.SPFieldLookupValueCollection]) { return @($Value | ForEach-Object { Convert-SPFieldValue $_ }) } if ($Value -is [Microsoft.SharePoint.SPFieldUrlValue]) { return @{ Url = $Value.Url Description = $Value.Description } } if ($Value -is [System.Guid]) { return $Value.ToString() } if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { return @($Value | ForEach-Object { Convert-SPFieldValue $_ }) } $typeName = $Value.GetType().FullName if ($typeName -like "*TaxonomyFieldValue") { return @{ Label = $Value.Label TermGuid = $Value.TermGuid WssId = $Value.WssId } } if ($typeName -like "*TaxonomyFieldValueCollection") { return @($Value | ForEach-Object { @{ Label = $_.Label TermGuid = $_.TermGuid WssId = $_.WssId } }) } return $Value } function Get-FieldTextValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field, $RawValue ) try { return $Field.GetFieldValueAsText($RawValue) } catch { return $null } } function Get-ItemFieldTextValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [Parameter(Mandatory = $true)] [string]$InternalName ) if (-not $Item.Fields.ContainsField($InternalName)) { return $null } return Get-FieldTextValue -Field $Item.Fields[$InternalName] -RawValue $Item[$InternalName] } function Get-FileMetadataObject { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFile]$File ) $item = $File.Item $fieldMetadata = @() if ($null -ne $item) { foreach ($field in $item.Fields) { $rawValue = $item[$field.InternalName] $fieldMetadata += [PSCustomObject]@{ InternalName = $field.InternalName Title = $field.Title TypeAsString = $field.TypeAsString TextValue = Get-FieldTextValue -Field $field -RawValue $rawValue Value = Convert-SPFieldValue $rawValue } } } return [PSCustomObject]@{ ExportedAtUtc = [System.DateTime]::UtcNow.ToString("o") Web = [PSCustomObject]@{ Title = $Web.Title Url = $Web.Url ServerRelativeUrl = $Web.ServerRelativeUrl Id = $Web.ID.ToString() } Library = [PSCustomObject]@{ Title = $List.Title Id = $List.ID.ToString() RootFolderUrl = $List.RootFolder.ServerRelativeUrl BaseTemplate = [int]$List.BaseTemplate } File = [PSCustomObject]@{ Name = $File.Name Url = $File.Url ServerRelativeUrl = $File.ServerRelativeUrl UniqueId = $File.UniqueId.ToString() Length = $File.Length TimeCreated = $File.TimeCreated.ToString("o") TimeLastModified = $File.TimeLastModified.ToString("o") CheckOutType = $File.CheckOutType.ToString() Level = $File.Level.ToString() MajorVersion = $File.MajorVersion MinorVersion = $File.MinorVersion } Item = if ($null -ne $item) { [PSCustomObject]@{ Id = $item.ID UniqueId = $item.UniqueId.ToString() ContentTypeId = $item.ContentTypeId.ToString() ContentTypeName = $item.ContentType.Name Created = if ($null -ne $item["Created"]) { ([datetime]$item["Created"]).ToString("o") } else { $null } Modified = if ($null -ne $item["Modified"]) { ([datetime]$item["Modified"]).ToString("o") } else { $null } CreatedBy = Get-ItemFieldTextValue -Item $item -InternalName "Author" ModifiedBy = Get-ItemFieldTextValue -Item $item -InternalName "Editor" Fields = $fieldMetadata } } else { $null } } } function Write-MetadataJson { param( [Parameter(Mandatory = $true)] $MetadataObject, [Parameter(Mandatory = $true)] [string]$Path ) $json = $MetadataObject | ConvertTo-Json -Depth 10 [System.IO.File]::WriteAllText($Path, $json, [System.Text.Encoding]::UTF8) } function Write-ManifestRow { param( [Parameter(Mandatory = $true)] [pscustomobject]$Row, [Parameter(Mandatory = $true)] [string]$ManifestPath ) $append = [System.IO.File]::Exists($ManifestPath) $Row | Export-Csv -Path $ManifestPath -NoTypeInformation -Delimiter ";" -Encoding UTF8 -Append:$append } function Get-RelativeFilePathSegments { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFile]$File ) $rootUrl = $List.RootFolder.ServerRelativeUrl.TrimEnd("/") $fileUrl = $File.ServerRelativeUrl $relativeUrl = $fileUrl.Substring($rootUrl.Length).TrimStart("/") return @($relativeUrl.Split("/") | ForEach-Object { Get-SafePathSegment $_ }) } function Export-SPFile { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFile]$File, [Parameter(Mandatory = $true)] [string]$FilesRootPath, [Parameter(Mandatory = $true)] [string]$MetadataRootPath, [Parameter(Mandatory = $true)] [string]$ManifestPath ) $relativeSegments = Get-RelativeFilePathSegments -List $List -File $File $relativeDirectorySegments = @() if ($relativeSegments.Count -gt 1) { $relativeDirectorySegments = $relativeSegments[0..($relativeSegments.Count - 2)] } $safeLibraryName = Get-SafePathSegment $List.Title $fileDirectory = Combine-PathSegments -BasePath ([System.IO.Path]::Combine($FilesRootPath, $safeLibraryName)) -Segments $relativeDirectorySegments $metadataDirectory = Combine-PathSegments -BasePath ([System.IO.Path]::Combine($MetadataRootPath, $safeLibraryName)) -Segments $relativeDirectorySegments Ensure-Directory -Path $fileDirectory Ensure-Directory -Path $metadataDirectory $fileName = $relativeSegments[-1] $localFilePath = [System.IO.Path]::Combine($fileDirectory, $fileName) $metadataPath = [System.IO.Path]::Combine($metadataDirectory, "$fileName.metadata.json") [System.IO.File]::WriteAllBytes($localFilePath, $File.OpenBinary()) $metadata = Get-FileMetadataObject -Web $Web -List $List -File $File Write-MetadataJson -MetadataObject $metadata -Path $metadataPath $manifestRow = [PSCustomObject]@{ WebUrl = $Web.Url LibraryTitle = $List.Title FileName = $File.Name FileServerRelativeUrl = $File.ServerRelativeUrl FileUniqueId = $File.UniqueId.ToString() FileLength = $File.Length LocalFilePath = $localFilePath LocalMetadataPath = $metadataPath ExportedAtUtc = [System.DateTime]::UtcNow.ToString("o") } Write-ManifestRow -Row $manifestRow -ManifestPath $ManifestPath } function Export-SPFolder { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFolder]$Folder, [Parameter(Mandatory = $true)] [string]$FilesRootPath, [Parameter(Mandatory = $true)] [string]$MetadataRootPath, [Parameter(Mandatory = $true)] [string]$ManifestPath, [ref]$FileCount ) foreach ($file in $Folder.Files) { Export-SPFile -Web $Web -List $List -File $file -FilesRootPath $FilesRootPath -MetadataRootPath $MetadataRootPath -ManifestPath $ManifestPath $FileCount.Value++ } foreach ($subFolder in $Folder.SubFolders) { if ($subFolder.Name -eq "Forms") { continue } Export-SPFolder -Web $Web -List $List -Folder $subFolder -FilesRootPath $FilesRootPath -MetadataRootPath $MetadataRootPath -ManifestPath $ManifestPath -FileCount $FileCount } } function Export-SPDocumentLibrary { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [string]$FilesRootPath, [Parameter(Mandatory = $true)] [string]$MetadataRootPath, [Parameter(Mandatory = $true)] [string]$ManifestPath ) Write-Host ("Exportiere Bibliothek: {0}" -f $List.Title) $fileCount = 0 Export-SPFolder -Web $Web -List $List -Folder $List.RootFolder -FilesRootPath $FilesRootPath -MetadataRootPath $MetadataRootPath -ManifestPath $ManifestPath -FileCount ([ref]$fileCount) Write-Host (" Dateien exportiert: {0}" -f $fileCount) } Initialize-SharePointPowerShell $resolvedOutputPath = [System.IO.Path]::GetFullPath($OutputPath) $filesRootPath = [System.IO.Path]::Combine($resolvedOutputPath, "Files") $metadataRootPath = [System.IO.Path]::Combine($resolvedOutputPath, "Metadata") $manifestPath = [System.IO.Path]::Combine($resolvedOutputPath, "manifest.csv") Ensure-Directory -Path $resolvedOutputPath Ensure-Directory -Path $filesRootPath Ensure-Directory -Path $metadataRootPath if ([System.IO.File]::Exists($manifestPath)) { [System.IO.File]::Delete($manifestPath) } $web = $null try { $web = Get-SPWeb -Identity $WebUrl -ErrorAction Stop $documentLibraries = @( $web.Lists | Where-Object { $_.BaseType -eq [Microsoft.SharePoint.SPBaseType]::DocumentLibrary -and ($IncludeHiddenLibraries -or -not $_.Hidden) -and -not $_.IsCatalog } ) if ($documentLibraries.Count -eq 0) { Write-Warning ("Keine Dokumentbibliotheken im Web gefunden: {0}" -f $WebUrl) return } Write-Host ("Gefundene Bibliotheken: {0}" -f $documentLibraries.Count) foreach ($library in $documentLibraries) { Export-SPDocumentLibrary -Web $web -List $library -FilesRootPath $filesRootPath -MetadataRootPath $metadataRootPath -ManifestPath $manifestPath } Write-Host ("Export abgeschlossen. Ausgabe: {0}" -f $resolvedOutputPath) } finally { if ($null -ne $web) { $web.Dispose() } }