[CmdletBinding()] param( [string]$WebUrl, [Parameter(Mandatory = $true)] [string]$OutputPath, [switch]$IncludeHiddenLibraries, [switch]$IncludeHiddenLists, [string]$TargetWebUrl, [string]$MappingCsvPath, [switch]$ImportFiles, [switch]$ImportLists, [switch]$OverwriteFiles, [switch]$ExportOnly, [switch]$ImportOnly ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" function Initialize-SharePointPowerShell { Import-Module SharePointServer } function Ensure-Directory { param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not [System.IO.Directory]::Exists($Path)) { [System.IO.Directory]::CreateDirectory($Path) | Out-Null } } function Invoke-MigrationImport { param( [Parameter(Mandatory = $true)] [string]$InputPath, [Parameter(Mandatory = $true)] [string]$TargetWebUrl, [Parameter(Mandatory = $true)] [string]$MappingCsvPath, [switch]$ImportFiles, [switch]$ImportLists, [switch]$OverwriteFiles ) $resolvedInputPath = [System.IO.Path]::GetFullPath($InputPath) $filesRootPath = [System.IO.Path]::Combine($resolvedInputPath, "Files") $listsRootPath = [System.IO.Path]::Combine($resolvedInputPath, "Lists") $manifestPath = [System.IO.Path]::Combine($resolvedInputPath, "manifest.csv") $fieldMapping = Get-FieldMapping -Path $MappingCsvPath $containerManifestMap = Get-ContainerManifestMap -ManifestRows (Read-ManifestRows -ManifestPath $manifestPath) if ($fieldMapping.Count -eq 0) { throw "Die Mapping-CSV enthaelt keine gueltigen Zuordnungen." } $shouldImportFiles = $ImportFiles.IsPresent $shouldImportLists = $ImportLists.IsPresent if (-not $shouldImportFiles -and -not $shouldImportLists) { $shouldImportFiles = $true $shouldImportLists = $true } $targetWeb = $null try { $targetWeb = Get-SPWeb -Identity $TargetWebUrl -ErrorAction Stop if ($shouldImportFiles) { Import-SPDocumentLibraries -Web $targetWeb -FilesRootPath $filesRootPath -FieldMapping $fieldMapping -ContainerManifestMap $containerManifestMap -OverwriteFiles:$OverwriteFiles } if ($shouldImportLists) { Import-SPLists -Web $targetWeb -ListsRootPath $listsRootPath -FieldMapping $fieldMapping -ContainerManifestMap $containerManifestMap } Write-Host ("Import abgeschlossen. Quelle: {0}" -f $resolvedInputPath) } finally { if ($null -ne $targetWeb) { $targetWeb.Dispose() } } } 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 Test-IsCatalogList { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List ) try { return [bool]$List.IsCatalog } catch { return $false } } function Get-SafeCollectionCount { param( $Value ) if ($null -eq $Value) { return 0 } try { return [int]$Value.Count } catch { } try { return [int]$Value.Length } catch { } return @($Value).Length } 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 ) $field = $null try { $field = $Item.Fields.GetFieldByInternalName($InternalName) } catch { $field = $null } if ($null -eq $field) { try { $field = $Item.Fields.GetFieldByStaticName($InternalName) } catch { $field = $null } } if ($null -eq $field) { return $null } $rawValue = $null try { $rawValue = $Item[$field.InternalName] } catch { try { $rawValue = $Item[$field.Id] } catch { $rawValue = $null } } return Get-FieldTextValue -Field $field -RawValue $rawValue } function Get-ListItemFieldMetadata { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item ) $fieldMetadata = @() foreach ($field in $Item.Fields) { $rawValue = $null try { $rawValue = $Item[$field.InternalName] } catch { try { $rawValue = $Item[$field.Id] } catch { $rawValue = $null } } $fieldMetadata += [PSCustomObject]@{ InternalName = $field.InternalName TypeAsString = $field.TypeAsString TextValue = Get-FieldTextValue -Field $field -RawValue $rawValue Value = Convert-SPFieldValue $rawValue } } return $fieldMetadata } function Get-ListItemFieldValueMap { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [switch]$AsText ) $fieldValues = [ordered]@{} foreach ($field in $Item.Fields) { $rawValue = $null try { $rawValue = $Item[$field.InternalName] } catch { try { $rawValue = $Item[$field.Id] } catch { $rawValue = $null } } if ($AsText) { $fieldValues[$field.InternalName] = Get-FieldTextValue -Field $field -RawValue $rawValue } else { $fieldValues[$field.InternalName] = Convert-SPFieldValue $rawValue } } return [PSCustomObject]$fieldValues } function Get-ListItemAttachmentMetadata { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item ) $attachments = @() $attachmentCollection = $Item.Attachments if ((Get-SafeCollectionCount $attachmentCollection) -eq 0) { return $attachments } foreach ($attachmentName in @($attachmentCollection)) { $attachments += [PSCustomObject]@{ FileName = $attachmentName Url = $attachmentCollection.UrlPrefix + $attachmentName } } return $attachments } function Get-ListItemMetadataObject { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item ) $attachmentCollection = $Item.Attachments return [PSCustomObject]@{ Id = $Item.ID UniqueId = $Item.UniqueId.ToString() Title = if ($Item.Fields.ContainsField("Title")) { $Item["Title"] } else { $null } ContentTypeId = $Item.ContentTypeId.ToString() FileSystemObjectType = $Item.FileSystemObjectType.ToString() DisplayName = $Item.DisplayName Name = if ($Item.Name) { $Item.Name } else { $null } Url = if ($Item.Url) { $Item.Url } else { $null } ServerRelativeUrl = if ($Item.Folder) { $Item.Folder.ServerRelativeUrl } else { $null } 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" HasAttachments = (Get-SafeCollectionCount $attachmentCollection) -gt 0 Attachments = Get-ListItemAttachmentMetadata -Item $Item FieldValues = Get-ListItemFieldValueMap -Item $Item FieldTextValues = Get-ListItemFieldValueMap -Item $Item -AsText Fields = Get-ListItemFieldMetadata -Item $Item } } 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 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) { Get-ListItemMetadataObject -Item $item } 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 New-ManifestRow { param( [string]$RecordType = "", [string]$ObjectType = "", [string]$SourceWebUrl = "", [string]$SourceTitle = "", [string]$TargetTitle = "", [string]$BaseType = "", [string]$BaseTemplate = "", [string]$RootFolderUrl = "", [string]$SourceServerRelativeUrl = "", [string]$FileName = "", [string]$FileUniqueId = "", [string]$FileLength = "", [string]$LocalFilePath = "", [string]$LocalMetadataPath = "", [string]$ExportedAtUtc = "" ) return [PSCustomObject]@{ RecordType = $RecordType ObjectType = $ObjectType SourceWebUrl = $SourceWebUrl SourceTitle = $SourceTitle TargetTitle = $TargetTitle BaseType = $BaseType BaseTemplate = $BaseTemplate RootFolderUrl = $RootFolderUrl SourceServerRelativeUrl = $SourceServerRelativeUrl FileName = $FileName FileUniqueId = $FileUniqueId FileLength = $FileLength LocalFilePath = $LocalFilePath LocalMetadataPath = $LocalMetadataPath ExportedAtUtc = $ExportedAtUtc } } function Write-ContainerManifestRow { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [string]$ManifestPath ) $objectType = if ($List.BaseType -eq [Microsoft.SharePoint.SPBaseType]::DocumentLibrary) { "DocumentLibrary" } else { "List" } $manifestRow = New-ManifestRow ` -RecordType "Container" ` -ObjectType $objectType ` -SourceWebUrl $Web.Url ` -SourceTitle $List.Title ` -TargetTitle "" ` -BaseType $List.BaseType.ToString() ` -BaseTemplate ([string][int]$List.BaseTemplate) ` -RootFolderUrl $List.RootFolder.ServerRelativeUrl ` -SourceServerRelativeUrl $List.RootFolder.ServerRelativeUrl ` -ExportedAtUtc ([System.DateTime]::UtcNow.ToString("o")) Write-ManifestRow -Row $manifestRow -ManifestPath $ManifestPath } 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 Get-SPListItemsRecursive { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List ) $query = New-Object Microsoft.SharePoint.SPQuery $query.Query = "" $query.Folder = $List.RootFolder $query.ViewAttributes = "Scope='RecursiveAll'" $query.RowLimit = 0 $query.ViewFieldsOnly = $false return @($List.GetItems($query)) } 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]$ManifestPath ) $relativeSegments = @(Get-RelativeFilePathSegments -List $List -File $File) $relativeDirectorySegments = @() if ($relativeSegments.Length -gt 1) { $relativeDirectorySegments = $relativeSegments[0..($relativeSegments.Length - 2)] } $safeLibraryName = Get-SafePathSegment $List.Title $fileDirectory = Combine-PathSegments -BasePath ([System.IO.Path]::Combine($FilesRootPath, $safeLibraryName)) -Segments $relativeDirectorySegments Ensure-Directory -Path $fileDirectory if ($relativeSegments.Length -eq 0) { throw ("Konnte keinen relativen Dateipfad fuer '{0}' ermitteln." -f $File.ServerRelativeUrl) } $fileName = $relativeSegments[-1] $localFilePath = [System.IO.Path]::Combine($fileDirectory, $fileName) $metadataPath = [System.IO.Path]::Combine($fileDirectory, "$fileName.properties.json") [System.IO.File]::WriteAllBytes($localFilePath, $File.OpenBinary()) $metadata = Get-FileMetadataObject -Web $Web -List $List -File $File Write-MetadataJson -MetadataObject $metadata -Path $metadataPath $manifestRow = New-ManifestRow ` -RecordType "File" ` -ObjectType "DocumentLibrary" ` -SourceWebUrl $Web.Url ` -SourceTitle $List.Title ` -TargetTitle "" ` -BaseType $List.BaseType.ToString() ` -BaseTemplate ([string][int]$List.BaseTemplate) ` -RootFolderUrl $List.RootFolder.ServerRelativeUrl ` -SourceServerRelativeUrl $File.ServerRelativeUrl ` -FileName $File.Name ` -FileUniqueId $File.UniqueId.ToString() ` -FileLength ([string]$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]$ManifestPath, [ref]$FileCount ) foreach ($file in $Folder.Files) { Export-SPFile -Web $Web -List $List -File $file -FilesRootPath $FilesRootPath -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 -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]$ManifestPath ) Write-Host ("Exportiere Bibliothek: {0}" -f $List.Title) Write-ContainerManifestRow -Web $Web -List $List -ManifestPath $ManifestPath $fileCount = 0 Export-SPFolder -Web $Web -List $List -Folder $List.RootFolder -FilesRootPath $FilesRootPath -ManifestPath $ManifestPath -FileCount ([ref]$fileCount) Write-Host (" Dateien exportiert: {0}" -f $fileCount) } function Export-SPList { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [Parameter(Mandatory = $true)] [string]$ListsRootPath, [Parameter(Mandatory = $true)] [string]$ManifestPath ) Write-Host ("Exportiere Liste: {0}" -f $List.Title) Write-ContainerManifestRow -Web $Web -List $List -ManifestPath $ManifestPath $safeListName = Get-SafePathSegment $List.Title $listPath = [System.IO.Path]::Combine($ListsRootPath, "$safeListName.json") $items = @() foreach ($item in @(Get-SPListItemsRecursive -List $List)) { $items += Get-ListItemMetadataObject -Item $item } $listExport = [PSCustomObject]@{ ExportedAtUtc = [System.DateTime]::UtcNow.ToString("o") Web = [PSCustomObject]@{ Title = $Web.Title Url = $Web.Url ServerRelativeUrl = $Web.ServerRelativeUrl Id = $Web.ID.ToString() } List = [PSCustomObject]@{ Title = $List.Title Id = $List.ID.ToString() BaseType = $List.BaseType.ToString() BaseTemplate = [int]$List.BaseTemplate DefaultViewUrl = $List.DefaultViewUrl RootFolderUrl = $List.RootFolder.ServerRelativeUrl ItemCount = $List.ItemCount Hidden = $List.Hidden } Items = $items } Write-MetadataJson -MetadataObject $listExport -Path $listPath Write-Host (" Listeneintraege exportiert: {0}" -f $items.Length) } function Get-ObjectPropertyValue { param( $Object, [Parameter(Mandatory = $true)] [string]$PropertyName, $DefaultValue = $null ) if ($null -eq $Object) { return $DefaultValue } $property = $Object.PSObject.Properties[$PropertyName] if ($null -eq $property) { return $DefaultValue } return $property.Value } function Read-ManifestRows { param( [Parameter(Mandatory = $true)] [string]$ManifestPath ) if (-not [System.IO.File]::Exists($ManifestPath)) { return @() } return @(Import-Csv -Path $ManifestPath -Delimiter ";") } function Get-ContainerManifestEntryKey { param( [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) return ("{0}|{1}" -f $ObjectType.Trim(), $SourceTitle.Trim()).ToLowerInvariant() } function Get-ContainerManifestMap { param( [Parameter(Mandatory = $true)] [object[]]$ManifestRows ) $containerMap = @{} foreach ($row in $ManifestRows) { $recordType = [string](Get-ObjectPropertyValue -Object $row -PropertyName "RecordType") $sourceTitle = [string](Get-ObjectPropertyValue -Object $row -PropertyName "SourceTitle") $objectType = [string](Get-ObjectPropertyValue -Object $row -PropertyName "ObjectType") if ($recordType -ne "Container" -or [string]::IsNullOrWhiteSpace($sourceTitle) -or [string]::IsNullOrWhiteSpace($objectType)) { continue } $key = Get-ContainerManifestEntryKey -ObjectType $objectType -SourceTitle $sourceTitle $containerMap[$key] = $row } return $containerMap } function Get-TargetContainerTitle { param( [Parameter(Mandatory = $true)] [hashtable]$ContainerManifestMap, [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) $key = Get-ContainerManifestEntryKey -ObjectType $ObjectType -SourceTitle $SourceTitle $manifestEntry = $ContainerManifestMap[$key] if ($null -eq $manifestEntry) { return $SourceTitle } $targetTitle = [string](Get-ObjectPropertyValue -Object $manifestEntry -PropertyName "TargetTitle") if ([string]::IsNullOrWhiteSpace($targetTitle)) { return $SourceTitle } return $targetTitle.Trim() } function Test-ContainerHasExplicitTargetMapping { param( [Parameter(Mandatory = $true)] [hashtable]$ContainerManifestMap, [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) $key = Get-ContainerManifestEntryKey -ObjectType $ObjectType -SourceTitle $SourceTitle $manifestEntry = $ContainerManifestMap[$key] if ($null -eq $manifestEntry) { return $false } $targetTitle = [string](Get-ObjectPropertyValue -Object $manifestEntry -PropertyName "TargetTitle") return -not [string]::IsNullOrWhiteSpace($targetTitle) } function Test-ObjectHasProperty { param( $Object, [Parameter(Mandatory = $true)] [string]$PropertyName ) if ($null -eq $Object) { return $false } return $null -ne $Object.PSObject.Properties[$PropertyName] } function Read-JsonFile { param( [Parameter(Mandatory = $true)] [string]$Path ) return Get-Content -Path $Path -Raw -Encoding UTF8 | ConvertFrom-Json } function Resolve-SPField { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFieldCollection]$Fields, [Parameter(Mandatory = $true)] [string]$InternalName ) try { return $Fields.GetFieldByInternalName($InternalName) } catch { } try { return $Fields.GetFieldByStaticName($InternalName) } catch { } foreach ($field in $Fields) { if ($field.InternalName -eq $InternalName -or $field.StaticName -eq $InternalName) { return $field } } return $null } function Get-FieldMapping { param( [Parameter(Mandatory = $true)] [string]$Path ) $rows = Import-Csv -Path $Path -Delimiter ";" $mapping = [ordered]@{} foreach ($row in $rows) { $sourceInternalName = [string](Get-ObjectPropertyValue -Object $row -PropertyName "SourceInternalName") $targetInternalName = [string](Get-ObjectPropertyValue -Object $row -PropertyName "TargetInternalName") if ([string]::IsNullOrWhiteSpace($sourceInternalName) -or [string]::IsNullOrWhiteSpace($targetInternalName)) { continue } $mapping[$sourceInternalName.Trim()] = $targetInternalName.Trim() } return $mapping } function Convert-ToBoolean { param( $RawValue, $TextValue ) if ($RawValue -is [bool]) { return $RawValue } $candidate = $RawValue if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { $candidate = $TextValue } if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { return $null } return [System.Convert]::ToBoolean($candidate) } function Convert-ToInt32 { param( $RawValue, $TextValue ) $candidate = $RawValue if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { $candidate = $TextValue } if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { return $null } return [int]$candidate } function Convert-ToDouble { param( $RawValue, $TextValue ) $candidate = $RawValue if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { $candidate = $TextValue } if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { return $null } return [double]::Parse( [string]$candidate, [System.Globalization.NumberStyles]::Any, [System.Globalization.CultureInfo]::InvariantCulture ) } function Convert-ToDateTimeValue { param( $RawValue, $TextValue ) $candidate = $RawValue if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { $candidate = $TextValue } if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate)) { return $null } return [datetime]::Parse( [string]$candidate, [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::RoundtripKind ) } function Resolve-SPUser { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, $RawUserValue, [string]$TextValue ) $candidates = @() if ($null -ne $RawUserValue) { $loginName = Get-ObjectPropertyValue -Object $RawUserValue -PropertyName "LoginName" $email = Get-ObjectPropertyValue -Object $RawUserValue -PropertyName "Email" $lookupValue = Get-ObjectPropertyValue -Object $RawUserValue -PropertyName "LookupValue" if (-not [string]::IsNullOrWhiteSpace([string]$loginName)) { $candidates += [string]$loginName } if (-not [string]::IsNullOrWhiteSpace([string]$email)) { $candidates += [string]$email } if (-not [string]::IsNullOrWhiteSpace([string]$lookupValue)) { $candidates += [string]$lookupValue } } if (-not [string]::IsNullOrWhiteSpace([string]$TextValue)) { $candidates += [string]$TextValue } foreach ($candidate in $candidates | Select-Object -Unique) { try { return $Web.EnsureUser($candidate) } catch { } } return $null } function Convert-ToUserFieldValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, $RawValue, [string]$TextValue ) $user = Resolve-SPUser -Web $Item.Web -RawUserValue $RawValue -TextValue $TextValue if ($null -eq $user) { return $null } return New-Object Microsoft.SharePoint.SPFieldUserValue($Item.Web, $user.ID, $user.LoginName) } function Convert-ToUserMultiFieldValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, $RawValue, [string]$TextValue ) $collection = New-Object Microsoft.SharePoint.SPFieldUserValueCollection $rawUsers = @($RawValue) if (($rawUsers.Length -eq 1) -and $null -eq $rawUsers[0] -and -not [string]::IsNullOrWhiteSpace($TextValue)) { $rawUsers = @($TextValue.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries)) } foreach ($rawUser in $rawUsers) { $userValue = Convert-ToUserFieldValue -Item $Item -RawValue $rawUser -TextValue ([string]$rawUser) if ($null -ne $userValue) { [void]$collection.Add($userValue) } } return $collection } function Convert-ToUrlFieldValue { param( $RawValue, [string]$TextValue ) $url = Get-ObjectPropertyValue -Object $RawValue -PropertyName "Url" $description = Get-ObjectPropertyValue -Object $RawValue -PropertyName "Description" if (-not [string]::IsNullOrWhiteSpace([string]$url)) { $fieldValue = New-Object Microsoft.SharePoint.SPFieldUrlValue $fieldValue.Url = [string]$url $fieldValue.Description = [string]$description return $fieldValue } if (-not [string]::IsNullOrWhiteSpace([string]$TextValue)) { $fieldValue = New-Object Microsoft.SharePoint.SPFieldUrlValue try { $fieldValue.FromString([string]$TextValue) return $fieldValue } catch { } } return $null } function Set-SPItemFieldValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [Parameter(Mandatory = $true)] [string]$TargetInternalName, $RawValue, [string]$TextValue ) $field = Resolve-SPField -Fields $Item.Fields -InternalName $TargetInternalName if ($null -eq $field) { Write-Warning ("Zielfeld nicht gefunden: {0}" -f $TargetInternalName) return } if ($field.ReadOnlyField -or $field.Sealed) { Write-Warning ("Zielfeld ist schreibgeschuetzt und wird uebersprungen: {0}" -f $TargetInternalName) return } try { switch ($field.TypeAsString) { "Boolean" { $Item[$field.InternalName] = Convert-ToBoolean -RawValue $RawValue -TextValue $TextValue return } "Integer" { $Item[$field.InternalName] = Convert-ToInt32 -RawValue $RawValue -TextValue $TextValue return } "Counter" { return } "Number" { $Item[$field.InternalName] = Convert-ToDouble -RawValue $RawValue -TextValue $TextValue return } "Currency" { $Item[$field.InternalName] = Convert-ToDouble -RawValue $RawValue -TextValue $TextValue return } "DateTime" { $Item[$field.InternalName] = Convert-ToDateTimeValue -RawValue $RawValue -TextValue $TextValue return } "URL" { $Item[$field.InternalName] = Convert-ToUrlFieldValue -RawValue $RawValue -TextValue $TextValue return } "User" { $Item[$field.InternalName] = Convert-ToUserFieldValue -Item $Item -RawValue $RawValue -TextValue $TextValue return } "UserMulti" { $Item[$field.InternalName] = Convert-ToUserMultiFieldValue -Item $Item -RawValue $RawValue -TextValue $TextValue return } default { if ($null -eq $RawValue) { $Item[$field.InternalName] = $TextValue } else { $Item[$field.InternalName] = $RawValue } return } } } catch { if (-not [string]::IsNullOrWhiteSpace($TextValue)) { try { $field.ParseAndSetValue($Item, $TextValue) return } catch { } } throw ("Konnte Feld '{0}' nicht setzen. {1}" -f $TargetInternalName, $_.Exception.Message) } } function Apply-FieldMappingToItem { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [Parameter(Mandatory = $true)] [System.Collections.IDictionary]$FieldMapping, $SourceFieldValues, $SourceFieldTextValues ) foreach ($sourceInternalName in $FieldMapping.Keys) { $targetInternalName = [string]$FieldMapping[$sourceInternalName] $hasRawValue = Test-ObjectHasProperty -Object $SourceFieldValues -PropertyName $sourceInternalName $hasTextValue = Test-ObjectHasProperty -Object $SourceFieldTextValues -PropertyName $sourceInternalName if (-not $hasRawValue -and -not $hasTextValue) { continue } $rawValue = if ($hasRawValue) { Get-ObjectPropertyValue -Object $SourceFieldValues -PropertyName $sourceInternalName } else { $null } $textValue = if ($hasTextValue) { [string](Get-ObjectPropertyValue -Object $SourceFieldTextValues -PropertyName $sourceInternalName) } else { $null } Set-SPItemFieldValue -Item $Item -TargetInternalName $targetInternalName -RawValue $rawValue -TextValue $textValue } } function Save-SPListItem { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item ) try { $Item.UpdateOverwriteVersion() return } catch { } $Item.Update() } function Get-RelativeSegmentsFromMetadataPath { param( [Parameter(Mandatory = $true)] [string]$MetadataFilePath, [Parameter(Mandatory = $true)] [string]$FilesRootPath ) $relativePath = $MetadataFilePath.Substring($FilesRootPath.Length).TrimStart("\") return @($relativePath.Split("\") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) } function Ensure-SPFolderHierarchy { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List, [string[]]$FolderSegments = @() ) $currentFolder = $List.RootFolder foreach ($folderSegment in $FolderSegments) { $nextFolder = $null foreach ($existingFolder in $currentFolder.SubFolders) { if ($existingFolder.Name -eq $folderSegment) { $nextFolder = $existingFolder break } } if ($null -eq $nextFolder) { $nextFolder = $currentFolder.SubFolders.Add($folderSegment) } $currentFolder = $nextFolder } return $currentFolder } function Import-SPDocumentLibraries { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [string]$FilesRootPath, [Parameter(Mandatory = $true)] [System.Collections.IDictionary]$FieldMapping, [Parameter(Mandatory = $true)] [hashtable]$ContainerManifestMap, [switch]$OverwriteFiles ) if (-not [System.IO.Directory]::Exists($FilesRootPath)) { Write-Warning ("Kein Files-Ordner gefunden: {0}" -f $FilesRootPath) return } $metadataFiles = @(Get-ChildItem -Path $FilesRootPath -Recurse -File -Filter *.properties.json) Write-Host ("Gefundene Datei-Metadaten: {0}" -f $metadataFiles.Length) foreach ($metadataFile in $metadataFiles) { $sourceFilePath = $metadataFile.FullName.Substring(0, $metadataFile.FullName.Length - ".properties.json".Length) if (-not [System.IO.File]::Exists($sourceFilePath)) { Write-Warning ("Quelldatei zur Metadatei nicht gefunden: {0}" -f $metadataFile.FullName) continue } $metadata = Read-JsonFile -Path $metadataFile.FullName $libraryMetadata = Get-ObjectPropertyValue -Object $metadata -PropertyName "Library" $itemMetadata = Get-ObjectPropertyValue -Object $metadata -PropertyName "Item" $sourceLibraryTitle = [string](Get-ObjectPropertyValue -Object $libraryMetadata -PropertyName "Title") if ([string]::IsNullOrWhiteSpace($sourceLibraryTitle)) { Write-Warning ("Bibliothekstitel fehlt in: {0}" -f $metadataFile.FullName) continue } $targetLibraryTitle = Get-TargetContainerTitle -ContainerManifestMap $ContainerManifestMap -ObjectType "DocumentLibrary" -SourceTitle $sourceLibraryTitle $targetLibrary = $Web.Lists.TryGetList($targetLibraryTitle) if ($null -eq $targetLibrary) { if (Test-ContainerHasExplicitTargetMapping -ContainerManifestMap $ContainerManifestMap -ObjectType "DocumentLibrary" -SourceTitle $sourceLibraryTitle) { Write-Warning ("Zielbibliothek '{0}' fuer Quellbibliothek '{1}' nicht gefunden. Bitte diese Bibliothek im Ziel manuell anlegen." -f $targetLibraryTitle, $sourceLibraryTitle) } else { Write-Warning ("Zielbibliothek '{0}' nicht gefunden und kein TargetTitle im manifest.csv gepflegt. Bitte Bibliothek manuell anlegen oder TargetTitle im Manifest setzen." -f $sourceLibraryTitle) } continue } if ($targetLibrary.BaseType -ne [Microsoft.SharePoint.SPBaseType]::DocumentLibrary) { Write-Warning ("Zielliste ist keine Dokumentbibliothek: {0}" -f $targetLibraryTitle) continue } $relativeSegments = @(Get-RelativeSegmentsFromMetadataPath -MetadataFilePath $metadataFile.FullName -FilesRootPath $FilesRootPath) if ($relativeSegments.Length -lt 2) { Write-Warning ("Dateipfad konnte nicht relativ zur Bibliothek ermittelt werden: {0}" -f $metadataFile.FullName) continue } $folderSegments = @() if ($relativeSegments.Length -gt 2) { $folderSegments = $relativeSegments[1..($relativeSegments.Length - 2)] } $targetFolder = Ensure-SPFolderHierarchy -List $targetLibrary -FolderSegments $folderSegments $fileName = [System.IO.Path]::GetFileName($sourceFilePath) $fileBytes = [System.IO.File]::ReadAllBytes($sourceFilePath) Write-Host ("Importiere Datei: {0} -> {1}" -f $sourceFilePath, $targetLibrary.Title) $spFile = $targetFolder.Files.Add($fileName, $fileBytes, $OverwriteFiles.IsPresent) $spItem = $spFile.Item if ($null -ne $itemMetadata) { Apply-FieldMappingToItem -Item $spItem -FieldMapping $FieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $itemMetadata -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $itemMetadata -PropertyName "FieldTextValues") Save-SPListItem -Item $spItem } } } function Import-SPLists { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [string]$ListsRootPath, [Parameter(Mandatory = $true)] [System.Collections.IDictionary]$FieldMapping, [Parameter(Mandatory = $true)] [hashtable]$ContainerManifestMap ) if (-not [System.IO.Directory]::Exists($ListsRootPath)) { Write-Warning ("Kein Lists-Ordner gefunden: {0}" -f $ListsRootPath) return } $listFiles = @(Get-ChildItem -Path $ListsRootPath -File -Filter *.json) Write-Host ("Gefundene Listen-JSONs: {0}" -f $listFiles.Length) foreach ($listFile in $listFiles) { $listExport = Read-JsonFile -Path $listFile.FullName $listMetadata = Get-ObjectPropertyValue -Object $listExport -PropertyName "List" $items = @(Get-ObjectPropertyValue -Object $listExport -PropertyName "Items" -DefaultValue @()) $sourceListTitle = [string](Get-ObjectPropertyValue -Object $listMetadata -PropertyName "Title") if ([string]::IsNullOrWhiteSpace($sourceListTitle)) { Write-Warning ("Listentitel fehlt in: {0}" -f $listFile.FullName) continue } $targetListTitle = Get-TargetContainerTitle -ContainerManifestMap $ContainerManifestMap -ObjectType "List" -SourceTitle $sourceListTitle $targetList = $Web.Lists.TryGetList($targetListTitle) if ($null -eq $targetList) { if (Test-ContainerHasExplicitTargetMapping -ContainerManifestMap $ContainerManifestMap -ObjectType "List" -SourceTitle $sourceListTitle) { Write-Warning ("Zielliste '{0}' fuer Quellliste '{1}' nicht gefunden. Bitte diese Liste im Ziel manuell anlegen." -f $targetListTitle, $sourceListTitle) } else { Write-Warning ("Zielliste '{0}' nicht gefunden und kein TargetTitle im manifest.csv gepflegt. Bitte Liste manuell anlegen oder TargetTitle im Manifest setzen." -f $sourceListTitle) } continue } if ($targetList.BaseType -eq [Microsoft.SharePoint.SPBaseType]::DocumentLibrary) { Write-Warning ("Liste ist eine Dokumentbibliothek und wird im Listenimport uebersprungen: {0}" -f $targetListTitle) continue } Write-Host ("Importiere Liste: {0}" -f $targetListTitle) foreach ($sourceItem in $items) { $fileSystemObjectType = [string](Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FileSystemObjectType") if ($fileSystemObjectType -eq "Folder") { Write-Warning ("Ordner in normalen Listen werden in dieser Version uebersprungen: {0}" -f $targetListTitle) continue } if ((Get-ObjectPropertyValue -Object $sourceItem -PropertyName "HasAttachments" -DefaultValue $false)) { Write-Warning ("Listenanlagen wurden im Export nicht physisch gesichert und werden uebersprungen. Liste: {0}, ItemId: {1}" -f $targetListTitle, (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "Id")) } $targetItem = $targetList.Items.Add() Apply-FieldMappingToItem -Item $targetItem -FieldMapping $FieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldTextValues") Save-SPListItem -Item $targetItem } } } Initialize-SharePointPowerShell $resolvedOutputPath = [System.IO.Path]::GetFullPath($OutputPath) $filesRootPath = [System.IO.Path]::Combine($resolvedOutputPath, "Files") $listsRootPath = [System.IO.Path]::Combine($resolvedOutputPath, "Lists") $manifestPath = [System.IO.Path]::Combine($resolvedOutputPath, "manifest.csv") $importConfigurationProvided = (-not [string]::IsNullOrWhiteSpace($TargetWebUrl)) -or (-not [string]::IsNullOrWhiteSpace($MappingCsvPath)) -or $ImportFiles -or $ImportLists -or $OverwriteFiles if ($ExportOnly -and $ImportOnly) { throw "ExportOnly und ImportOnly koennen nicht gleichzeitig gesetzt werden." } if ($ImportOnly) { if ([string]::IsNullOrWhiteSpace($TargetWebUrl) -or [string]::IsNullOrWhiteSpace($MappingCsvPath)) { throw "Fuer ImportOnly muessen TargetWebUrl und MappingCsvPath angegeben werden." } Invoke-MigrationImport -InputPath $resolvedOutputPath -TargetWebUrl $TargetWebUrl -MappingCsvPath $MappingCsvPath -ImportFiles:$ImportFiles -ImportLists:$ImportLists -OverwriteFiles:$OverwriteFiles return } if ([string]::IsNullOrWhiteSpace($WebUrl)) { throw "Fuer den Export muss WebUrl angegeben werden." } Ensure-Directory -Path $resolvedOutputPath Ensure-Directory -Path $filesRootPath Ensure-Directory -Path $listsRootPath 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 (Test-IsCatalogList -List $_) } ) $lists = @( $web.Lists | Where-Object { $_.BaseType -ne [Microsoft.SharePoint.SPBaseType]::DocumentLibrary -and ($IncludeHiddenLists -or -not $_.Hidden) -and -not (Test-IsCatalogList -List $_) } ) Write-Host ("Gefundene Bibliotheken: {0}" -f $documentLibraries.Length) Write-Host ("Gefundene Listen: {0}" -f $lists.Length) foreach ($library in $documentLibraries) { Export-SPDocumentLibrary -Web $web -List $library -FilesRootPath $filesRootPath -ManifestPath $manifestPath } foreach ($list in $lists) { Export-SPList -Web $web -List $list -ListsRootPath $listsRootPath -ManifestPath $manifestPath } if ($documentLibraries.Length -eq 0 -and $lists.Length -eq 0) { Write-Warning ("Keine exportierbaren Bibliotheken oder Listen im Web gefunden: {0}" -f $WebUrl) return } Write-Host ("Export abgeschlossen. Ausgabe: {0}" -f $resolvedOutputPath) if ($ExportOnly) { Write-Host "ExportOnly ist gesetzt. Import wird uebersprungen." return } if (-not $importConfigurationProvided) { return } if ([string]::IsNullOrWhiteSpace($TargetWebUrl) -or [string]::IsNullOrWhiteSpace($MappingCsvPath)) { throw "Fuer den automatischen Import muessen TargetWebUrl und MappingCsvPath angegeben werden." } Write-Host ("Starte Import nach Export. Zielweb: {0}" -f $TargetWebUrl) Invoke-MigrationImport -InputPath $resolvedOutputPath -TargetWebUrl $TargetWebUrl -MappingCsvPath $MappingCsvPath -ImportFiles:$ImportFiles -ImportLists:$ImportLists -OverwriteFiles:$OverwriteFiles } finally { if ($null -ne $web) { $web.Dispose() } }