[CmdletBinding()] param( [Alias("WebUrl")] [string]$SourceUrl, [string]$OutputPath = (Join-Path -Path (Get-Location).Path -ChildPath "SPMigrationOutput"), [switch]$IncludeHiddenLibraries, [switch]$IncludeHiddenLists, [Alias("TargetWebUrl")] [string]$TargetUrl, [Alias("MappingCsvPath")] [string]$MappingTable, [switch]$Export, [switch]$Import, [switch]$ImportFiles, [switch]$ImportLists, [Alias("OverwriteFiles")] [switch]$Overwrite ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $script:SkippedImportFieldWarnings = @{} function Initialize-SharePointPowerShell { if($null -eq $(Get-Module SharePointServer)) { Import-Module SharePointServer }else{ Write-Host "SharePoint Server PowerShell-Modul bereits geladen." } try { [void][System.Reflection.Assembly]::Load("Microsoft.SharePoint.Taxonomy") } catch { try { Add-Type -AssemblyName "Microsoft.SharePoint.Taxonomy" } catch { } } } 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]$MappingTablePath, [switch]$ImportFiles, [switch]$ImportLists, [switch]$Overwrite ) $resolvedInputPath = [System.IO.Path]::GetFullPath($InputPath) $filesRootPath = [System.IO.Path]::Combine($resolvedInputPath, "Files") $listsRootPath = [System.IO.Path]::Combine($resolvedInputPath, "Lists") $resolvedMappingTablePath = [System.IO.Path]::GetFullPath($MappingTablePath) $migrationMappingTable = Get-MigrationMappingTable -Path $resolvedMappingTablePath $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 -MigrationMappingTable $migrationMappingTable -Overwrite:$Overwrite } if ($shouldImportLists) { Import-SPLists -Web $targetWeb -ListsRootPath $listsRootPath -MigrationMappingTable $migrationMappingTable } 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 New-MigrationContainerMappingEntry { param( [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List ) return [PSCustomObject]@{ ObjectType = $ObjectType SourceTitle = $List.Title TargetTitle = $List.Title BaseType = $List.BaseType.ToString() BaseTemplate = [int]$List.BaseTemplate RootFolderUrl = $List.RootFolder.ServerRelativeUrl Hidden = [bool]$List.Hidden } } function Test-SPFieldSupportedForImport { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field ) $unsupportedInternalNames = @( "AccessPolicy", "AppAuthor", "AppEditor", "Attachments", "BannerUrl", "BaseName", "Author", "ComplianceAssetId", "ContentType", "ContentTypeId", "ContentVersion", "Created", "Created_x0020_Date", "DocIcon", "Duration", "Edit", "Editor", "EncodedAbsUrl", "EventCanceled", "EventType", "FileDirRef", "File_x0020_Type", "FileLeafRef", "FileRef", "FolderChildCount", "FreeBusy", "FSObjType", "Geolocation", "GUID", "HTML_x0020_File_x0020_Type", "ID", "InstanceID", "ItemChildCount", "Last_x0020_Modified", "LinkFilename", "LinkFilename2", "LinkFilenameNoMenu", "LinkTitle", "LinkTitle2", "LinkTitleNoMenu", "MasterSeriesItemID", "MetaInfo", "Modified", "NoExecute", "Order", "OriginatorId", "Overbook", "owshiddenversion", "Participants", "PermMask", "ProgId", "RecurrenceData", "RecurrenceID", "Restricted", "ScopeId", "SelectTitle", "ServerUrl", "SMLastModifiedDate", "SMTotalFileCount", "SMTotalFileStreamSize", "SMTotalSize", "SortBehavior", "SyncClientId", "TimeZone", "UID", "UniqueId", "webPartRteVersion", "WorkflowInstanceID", "WorkflowVersion", "Workspace", "WorkspaceLink", "XMLTZone", "_Level", "_ModerationStatus", "_UIVersion", "_UIVersionString" ) $unsupportedFieldTypes = @( "Attachments", "Computed" ) if ($Field.ReadOnlyField) { return $false } if ([string]$Field.InternalName -like "_*") { return $false } if ($unsupportedFieldTypes -contains [string]$Field.TypeAsString) { return $false } return -not ($unsupportedInternalNames -contains [string]$Field.InternalName) } function Resolve-SPFieldById { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFieldCollection]$Fields, [Parameter(Mandatory = $true)] [Guid]$FieldId ) try { return $Fields[$FieldId] } catch { } foreach ($candidateField in $Fields) { if ($candidateField.Id -eq $FieldId) { return $candidateField } } return $null } function Get-SPFieldSupportingInternalNames { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFieldCollection]$Fields ) $supportingInternalNames = @() $typeName = [string]$Field.GetType().FullName if ($typeName -like "*TaxonomyField*") { try { $textFieldId = [Guid]$Field.TextField if ($textFieldId -ne [Guid]::Empty) { $supportingField = Resolve-SPFieldById -Fields $Fields -FieldId $textFieldId if ($null -ne $supportingField -and -not [string]::IsNullOrWhiteSpace([string]$supportingField.InternalName)) { $supportingInternalNames += [string]$supportingField.InternalName } } } catch { } } $internalNamePrefix = "{0}_" -f $Field.InternalName foreach ($candidateField in $Fields) { if ($candidateField.Id -eq $Field.Id) { continue } if (-not [bool]$candidateField.Hidden) { continue } if ([string]$candidateField.InternalName -like ($internalNamePrefix + "*")) { $supportingInternalNames += [string]$candidateField.InternalName } } return @($supportingInternalNames | Sort-Object -Unique) } function Test-SPFieldShownInMappingTable { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field, [hashtable]$SupportingFieldLookup = @{} ) $fieldKey = ([string]$Field.InternalName).ToLowerInvariant() if ($SupportingFieldLookup.ContainsKey($fieldKey)) { return $false } return $true } function Test-SPFieldIsSystemColumn { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field ) try { if ([bool]$Field.FromBaseType) { return $true } } catch { } try { if (-not [bool]$Field.CanBeDeleted) { return $true } } catch { } if ([bool]$Field.Hidden) { return $true } if ([string]$Field.InternalName -like "_*") { return $true } if (-not (Test-SPFieldSupportedForImport -Field $Field)) { return $true } return $false } function Write-SkippedImportFieldWarning { param( [Parameter(Mandatory = $true)] [string]$FieldInternalName, [Parameter(Mandatory = $true)] [string]$Message ) $warningKey = $FieldInternalName.ToLowerInvariant() if ($script:SkippedImportFieldWarnings.ContainsKey($warningKey)) { return } $script:SkippedImportFieldWarnings[$warningKey] = $true Write-Warning $Message } function New-MigrationFieldMappingEntries { param( [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$List ) $rows = @() $supportingFieldLookup = @{} foreach ($field in @($List.Fields)) { foreach ($supportingInternalName in @(Get-SPFieldSupportingInternalNames -Field $field -Fields $List.Fields)) { if (-not [string]::IsNullOrWhiteSpace([string]$supportingInternalName)) { $supportingFieldLookup[[string]$supportingInternalName.ToLowerInvariant()] = $true } } } foreach ($field in @($List.Fields | Sort-Object -Property InternalName)) { if (-not (Test-SPFieldShownInMappingTable -Field $field -SupportingFieldLookup $supportingFieldLookup)) { continue } $rows += [PSCustomObject]@{ ObjectType = $ObjectType ContainerSourceTitle = $List.Title SourceInternalName = $field.InternalName SourceCanonicalInternalName = $field.InternalName SourceSupportingInternalNames = @(Get-SPFieldSupportingInternalNames -Field $field -Fields $List.Fields) TargetInternalName = $field.InternalName DisplayName = $field.Title TypeAsString = $field.TypeAsString Hidden = [bool]$field.Hidden ReadOnly = [bool]$field.ReadOnlyField Sealed = [bool]$field.Sealed IsSystemColumn = (Test-SPFieldIsSystemColumn -Field $field) ImportSupported = (Test-SPFieldSupportedForImport -Field $field) } } return $rows } function New-MigrationMappingTable { param( [Parameter(Mandatory = $true)] [string]$SourceWebUrl, [Microsoft.SharePoint.SPList[]]$DocumentLibraries = @(), [Microsoft.SharePoint.SPList[]]$Lists = @() ) $libraryMappings = @() $listMappings = @() $systemColumnMappings = @() $customColumnMappings = @() foreach ($library in @($DocumentLibraries)) { $libraryMappings += New-MigrationContainerMappingEntry -ObjectType "DocumentLibrary" -List $library $fieldMappings = @(New-MigrationFieldMappingEntries -ObjectType "DocumentLibrary" -List $library) $systemColumnMappings += @($fieldMappings | Where-Object { [bool](Get-ObjectPropertyValue -Object $_ -PropertyName "IsSystemColumn" -DefaultValue $false) }) $customColumnMappings += @($fieldMappings | Where-Object { -not [bool](Get-ObjectPropertyValue -Object $_ -PropertyName "IsSystemColumn" -DefaultValue $false) }) } foreach ($list in @($Lists)) { $listMappings += New-MigrationContainerMappingEntry -ObjectType "List" -List $list $fieldMappings = @(New-MigrationFieldMappingEntries -ObjectType "List" -List $list) $systemColumnMappings += @($fieldMappings | Where-Object { [bool](Get-ObjectPropertyValue -Object $_ -PropertyName "IsSystemColumn" -DefaultValue $false) }) $customColumnMappings += @($fieldMappings | Where-Object { -not [bool](Get-ObjectPropertyValue -Object $_ -PropertyName "IsSystemColumn" -DefaultValue $false) }) } return [PSCustomObject]@{ SchemaVersion = 1 GeneratedAtUtc = [System.DateTime]::UtcNow.ToString("o") SourceWebUrl = $SourceWebUrl LibraryMappings = $libraryMappings ListMappings = $listMappings MetadataColumnMappings = [PSCustomObject]@{ SystemColumns = $systemColumnMappings CustomColumns = $customColumnMappings } } } function Write-MigrationMappingTable { param( [Parameter(Mandatory = $true)] $MappingTable, [Parameter(Mandatory = $true)] [string]$Path ) $json = $MappingTable | ConvertTo-Json -Depth 10 [System.IO.File]::WriteAllText($Path, $json, [System.Text.Encoding]::UTF8) } 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 Get-MigrationMappingTable { param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not [System.IO.File]::Exists($Path)) { throw ("MappingTable nicht gefunden: {0}" -f $Path) } $extension = [System.IO.Path]::GetExtension($Path) if ($extension -ieq ".json") { $mappingTable = Read-JsonFile -Path $Path } elseif ($extension -ieq ".csv") { $fieldMapping = Get-FieldMapping -Path $Path $metadataColumnMappings = @() foreach ($sourceInternalName in $fieldMapping.Keys) { $metadataColumnMappings += [PSCustomObject]@{ ObjectType = "*" ContainerSourceTitle = "*" SourceInternalName = [string]$sourceInternalName SourceCanonicalInternalName = [string]$sourceInternalName SourceSupportingInternalNames = @() TargetInternalName = [string]$fieldMapping[$sourceInternalName] DisplayName = "" TypeAsString = "" Hidden = $false ReadOnly = $false Sealed = $false IsSystemColumn = $false ImportSupported = $true } } $mappingTable = [PSCustomObject]@{ SchemaVersion = 1 GeneratedAtUtc = [System.DateTime]::UtcNow.ToString("o") SourceWebUrl = "" LibraryMappings = @() ListMappings = @() MetadataColumnMappings = [PSCustomObject]@{ SystemColumns = @() CustomColumns = $metadataColumnMappings } } } else { throw "MappingTable muss eine JSON- oder CSV-Datei sein." } if ($null -eq $mappingTable) { throw "MappingTable konnte nicht gelesen werden." } return $mappingTable } function Get-ContainerMappingTableEntryKey { param( [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) return ("{0}|{1}" -f $ObjectType.Trim(), $SourceTitle.Trim()).ToLowerInvariant() } function Get-MetadataColumnMappingRows { param( [Parameter(Mandatory = $true)] $MigrationMappingTable ) $metadataColumnMappings = Get-ObjectPropertyValue -Object $MigrationMappingTable -PropertyName "MetadataColumnMappings" -DefaultValue @() if ($null -eq $metadataColumnMappings) { return @() } if ($null -ne $metadataColumnMappings.PSObject.Properties["SystemColumns"] -or $null -ne $metadataColumnMappings.PSObject.Properties["CustomColumns"]) { return @( @(Get-ObjectPropertyValue -Object $metadataColumnMappings -PropertyName "SystemColumns" -DefaultValue @()) + @(Get-ObjectPropertyValue -Object $metadataColumnMappings -PropertyName "CustomColumns" -DefaultValue @()) ) } return @($metadataColumnMappings) } function Test-MetadataColumnMappingRowImportSupported { param( [Parameter(Mandatory = $true)] $Row ) if (Test-ObjectHasProperty -Object $Row -PropertyName "ImportSupported") { return [bool](Get-ObjectPropertyValue -Object $Row -PropertyName "ImportSupported" -DefaultValue $false) } $sourceInternalName = [string](Get-ObjectPropertyValue -Object $Row -PropertyName "SourceInternalName") $typeAsString = [string](Get-ObjectPropertyValue -Object $Row -PropertyName "TypeAsString") $readOnly = [bool](Get-ObjectPropertyValue -Object $Row -PropertyName "ReadOnly" -DefaultValue $false) $unsupportedInternalNames = @( "AccessPolicy", "AppAuthor", "AppEditor", "Attachments", "BannerUrl", "BaseName", "Author", "ComplianceAssetId", "ContentType", "ContentTypeId", "ContentVersion", "Created", "Created_x0020_Date", "DocIcon", "Duration", "Edit", "Editor", "EncodedAbsUrl", "EventCanceled", "EventType", "FileDirRef", "File_x0020_Type", "FileLeafRef", "FileRef", "FolderChildCount", "FreeBusy", "FSObjType", "Geolocation", "GUID", "HTML_x0020_File_x0020_Type", "ID", "InstanceID", "ItemChildCount", "Last_x0020_Modified", "LinkFilename", "LinkFilename2", "LinkFilenameNoMenu", "LinkTitle", "LinkTitle2", "LinkTitleNoMenu", "MasterSeriesItemID", "MetaInfo", "Modified", "NoExecute", "Order", "OriginatorId", "Overbook", "owshiddenversion", "Participants", "PermMask", "ProgId", "RecurrenceData", "RecurrenceID", "Restricted", "ScopeId", "SelectTitle", "ServerUrl", "SMLastModifiedDate", "SMTotalFileCount", "SMTotalFileStreamSize", "SMTotalSize", "SortBehavior", "SyncClientId", "TimeZone", "UID", "UniqueId", "webPartRteVersion", "WorkflowInstanceID", "WorkflowVersion", "Workspace", "WorkspaceLink", "XMLTZone", "_Level", "_ModerationStatus", "_UIVersion", "_UIVersionString" ) if ($readOnly) { return $false } if ([string]::IsNullOrWhiteSpace($sourceInternalName)) { return $false } if ([string]$sourceInternalName -like "_*") { return $false } if ($typeAsString -in @("Attachments", "Computed")) { return $false } return -not ($unsupportedInternalNames -contains $sourceInternalName) } function Get-ContainerMappingMap { param( [Parameter(Mandatory = $true)] $MigrationMappingTable ) $containerMap = @{} $rows = @( @(Get-ObjectPropertyValue -Object $MigrationMappingTable -PropertyName "LibraryMappings" -DefaultValue @()) + @(Get-ObjectPropertyValue -Object $MigrationMappingTable -PropertyName "ListMappings" -DefaultValue @()) ) foreach ($row in $rows) { $objectType = [string](Get-ObjectPropertyValue -Object $row -PropertyName "ObjectType") $sourceTitle = [string](Get-ObjectPropertyValue -Object $row -PropertyName "SourceTitle") if ([string]::IsNullOrWhiteSpace($objectType) -or [string]::IsNullOrWhiteSpace($sourceTitle)) { continue } $key = Get-ContainerMappingTableEntryKey -ObjectType $objectType -SourceTitle $sourceTitle $containerMap[$key] = $row } return $containerMap } function Get-TargetContainerTitleFromMappingTable { param( [Parameter(Mandatory = $true)] [hashtable]$ContainerMappingMap, [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) $key = Get-ContainerMappingTableEntryKey -ObjectType $ObjectType -SourceTitle $SourceTitle $entry = $ContainerMappingMap[$key] if ($null -eq $entry) { return $SourceTitle } $targetTitle = [string](Get-ObjectPropertyValue -Object $entry -PropertyName "TargetTitle") if ([string]::IsNullOrWhiteSpace($targetTitle)) { return $SourceTitle } return $targetTitle.Trim() } function Test-ContainerHasExplicitTargetMappingInTable { param( [Parameter(Mandatory = $true)] [hashtable]$ContainerMappingMap, [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) $key = Get-ContainerMappingTableEntryKey -ObjectType $ObjectType -SourceTitle $SourceTitle $entry = $ContainerMappingMap[$key] if ($null -eq $entry) { return $false } $targetTitle = [string](Get-ObjectPropertyValue -Object $entry -PropertyName "TargetTitle") return -not [string]::IsNullOrWhiteSpace($targetTitle) } function Get-FieldMappingForContainer { param( [Parameter(Mandatory = $true)] $MigrationMappingTable, [Parameter(Mandatory = $true)] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$SourceTitle ) $rows = @(Get-MetadataColumnMappingRows -MigrationMappingTable $MigrationMappingTable) $mapping = [ordered]@{} $mappingPriority = @{} foreach ($row in $rows) { $rowObjectType = [string](Get-ObjectPropertyValue -Object $row -PropertyName "ObjectType") $rowContainerSourceTitle = [string](Get-ObjectPropertyValue -Object $row -PropertyName "ContainerSourceTitle") $sourceInternalName = [string](Get-ObjectPropertyValue -Object $row -PropertyName "SourceInternalName") $sourceCanonicalInternalName = [string](Get-ObjectPropertyValue -Object $row -PropertyName "SourceCanonicalInternalName") $targetInternalName = [string](Get-ObjectPropertyValue -Object $row -PropertyName "TargetInternalName") $isHiddenRow = [bool](Get-ObjectPropertyValue -Object $row -PropertyName "Hidden" -DefaultValue $false) if ([string]::IsNullOrWhiteSpace($sourceInternalName) -or [string]::IsNullOrWhiteSpace($targetInternalName)) { continue } $objectTypeMatches = ($rowObjectType -eq "*") -or ($rowObjectType -eq $ObjectType) $containerMatches = [string]::IsNullOrWhiteSpace($rowContainerSourceTitle) -or ($rowContainerSourceTitle -eq "*") -or ($rowContainerSourceTitle -eq $SourceTitle) if (-not $objectTypeMatches -or -not $containerMatches) { continue } if (-not (Test-MetadataColumnMappingRowImportSupported -Row $row)) { continue } $normalizedSourceInternalName = $sourceCanonicalInternalName if ([string]::IsNullOrWhiteSpace($normalizedSourceInternalName)) { $normalizedSourceInternalName = $sourceInternalName } if ($isHiddenRow -and $sourceInternalName -match "^(.*)_\d+$") { $normalizedSourceInternalName = $matches[1] } if ([string]::IsNullOrWhiteSpace($normalizedSourceInternalName)) { continue } $priority = if ($isHiddenRow) { 1 } else { 2 } $normalizedSourceInternalName = $normalizedSourceInternalName.Trim() if ($mappingPriority.ContainsKey($normalizedSourceInternalName) -and $mappingPriority[$normalizedSourceInternalName] -gt $priority) { continue } $mapping[$normalizedSourceInternalName] = $targetInternalName.Trim() $mappingPriority[$normalizedSourceInternalName] = $priority } return $mapping } 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 Convert-ToTaxonomyTermEntries { param( $RawValue, [string]$TextValue ) $termEntries = @() $rawTerms = @($RawValue) if (($rawTerms.Length -eq 1) -and $null -eq $rawTerms[0]) { $rawTerms = @() } foreach ($rawTerm in $rawTerms) { $termGuid = [string](Get-ObjectPropertyValue -Object $rawTerm -PropertyName "TermGuid") $label = [string](Get-ObjectPropertyValue -Object $rawTerm -PropertyName "Label") if ([string]::IsNullOrWhiteSpace($termGuid) -and ($rawTerm -is [string])) { $rawTermString = [string]$rawTerm if ($rawTermString -match "^(.*)\|([0-9a-fA-F-]{36})$") { $label = $matches[1] $termGuid = $matches[2] } else { $parsedGuid = [Guid]::Empty if ([guid]::TryParse($rawTermString, [ref]$parsedGuid)) { $termGuid = $rawTermString } } } if ([string]::IsNullOrWhiteSpace($label) -and -not [string]::IsNullOrWhiteSpace($TextValue) -and $rawTerms.Length -le 1) { $label = $TextValue } if ([string]::IsNullOrWhiteSpace($termGuid)) { continue } if ([string]::IsNullOrWhiteSpace($label)) { $label = $termGuid } $termEntries += [PSCustomObject]@{ Label = $label TermGuid = $termGuid } } if ($termEntries.Count -eq 0 -and -not [string]::IsNullOrWhiteSpace($TextValue)) { $segments = @($TextValue -split ";#") foreach ($segment in $segments) { if ([string]::IsNullOrWhiteSpace([string]$segment)) { continue } if ([string]$segment -match "^(.*)\|([0-9a-fA-F-]{36})$") { $termEntries += [PSCustomObject]@{ Label = $matches[1] TermGuid = $matches[2] } } } } return @($termEntries) } function New-TaxonomyFieldValueObject { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field, [Parameter(Mandatory = $true)] $TermEntry ) $label = [string](Get-ObjectPropertyValue -Object $TermEntry -PropertyName "Label") $termGuid = [string](Get-ObjectPropertyValue -Object $TermEntry -PropertyName "TermGuid") $labelGuidPair = "{0}|{1}" -f $label, $termGuid $taxonomyField = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$Field $taxonomyValue = $null try { $taxonomyValue = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue($taxonomyField) } catch { } if ($null -eq $taxonomyValue) { try { $taxonomyValue = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValue } catch { } } if ($null -eq $taxonomyValue) { throw ("Konnte kein TaxonomyFieldValue fuer Feld '{0}' erstellen." -f $Field.InternalName) } $isPopulated = $false try { $taxonomyValue.PopulateFromLabelGuidPair($labelGuidPair) $isPopulated = $true } catch { } if (-not $isPopulated) { try { $taxonomyValue.Label = $label $taxonomyValue.TermGuid = $termGuid $taxonomyValue.WssId = -1 $isPopulated = $true } catch { } } if (-not $isPopulated) { throw ("Konnte TaxonomyFieldValue fuer Feld '{0}' nicht befuellen." -f $Field.InternalName) } return $taxonomyValue } function Set-SPTaxonomyFieldValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field, $RawValue, [string]$TextValue ) $taxonomyField = [Microsoft.SharePoint.Taxonomy.TaxonomyField]$Field $termEntries = @(Convert-ToTaxonomyTermEntries -RawValue $RawValue -TextValue $TextValue) if ($termEntries.Count -eq 0) { $Item[$Field.InternalName] = $null return } if ([string]$Field.TypeAsString -eq "TaxonomyFieldTypeMulti") { $labelGuidPairs = @() foreach ($termEntry in $termEntries) { $label = [string](Get-ObjectPropertyValue -Object $termEntry -PropertyName "Label") $termGuid = [string](Get-ObjectPropertyValue -Object $termEntry -PropertyName "TermGuid") $labelGuidPairs += ("{0}|{1}" -f $label, $termGuid) } $pairString = $labelGuidPairs -join ";#" $taxonomyCollection = $null try { $taxonomyCollection = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection($Item.Web, "", $taxonomyField) } catch { } if ($null -eq $taxonomyCollection) { try { $taxonomyCollection = New-Object Microsoft.SharePoint.Taxonomy.TaxonomyFieldValueCollection($Item.Web, $pairString, $taxonomyField) } catch { } } if ($null -eq $taxonomyCollection) { throw ("Konnte keine TaxonomyFieldValueCollection fuer Feld '{0}' erstellen." -f $Field.InternalName) } $collectionIsPopulated = $false try { $taxonomyCollection.PopulateFromLabelGuidPairs($pairString) $collectionIsPopulated = $true } catch { } if (-not $collectionIsPopulated) { try { foreach ($termEntry in $termEntries) { [void]$taxonomyCollection.Add((New-TaxonomyFieldValueObject -Field $Field -TermEntry $termEntry)) } $collectionIsPopulated = $true } catch { } } if (-not $collectionIsPopulated) { throw ("Konnte die TaxonomyFieldValueCollection fuer Feld '{0}' nicht befuellen." -f $Field.InternalName) } try { $taxonomyField.SetFieldValueByValueCollection($Item, $taxonomyCollection) return } catch { } try { $taxonomyField.SetFieldValue($Item, $taxonomyCollection) return } catch { } $Item[$Field.InternalName] = $taxonomyCollection return } $taxonomyValue = New-TaxonomyFieldValueObject -Field $Field -TermEntry $termEntries[0] try { $taxonomyField.SetFieldValueByValue($Item, $taxonomyValue) return } catch { } try { $taxonomyField.SetFieldValue($Item, $taxonomyValue) return } catch { } $Item[$Field.InternalName] = $taxonomyValue } 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-SkippedImportFieldWarning -FieldInternalName $TargetInternalName -Message ("Zielfeld nicht gefunden: {0}" -f $TargetInternalName) return } if (-not (Test-SPFieldSupportedForImport -Field $field)) { Write-SkippedImportFieldWarning -FieldInternalName $TargetInternalName -Message ("Zielfeld wird nicht importiert und wird uebersprungen: {0}" -f $TargetInternalName) return } try { switch ($field.TypeAsString) { "TaxonomyFieldType" { Set-SPTaxonomyFieldValue -Item $Item -Field $field -RawValue $RawValue -TextValue $TextValue return } "TaxonomyFieldTypeMulti" { Set-SPTaxonomyFieldValue -Item $Item -Field $field -RawValue $RawValue -TextValue $TextValue return } "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 { } } Write-SkippedImportFieldWarning -FieldInternalName $TargetInternalName -Message ("Konnte Feld '{0}' nicht setzen und ueberspringe es. {1}" -f $TargetInternalName, $_.Exception.Message) return } } 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 -and $sourceInternalName -match "^(.*)_\d+$") { $canonicalSourceInternalName = $matches[1] $hasRawValue = Test-ObjectHasProperty -Object $SourceFieldValues -PropertyName $canonicalSourceInternalName $hasTextValue = Test-ObjectHasProperty -Object $SourceFieldTextValues -PropertyName $canonicalSourceInternalName if ($hasRawValue -or $hasTextValue) { $sourceInternalName = $canonicalSourceInternalName } } 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 Get-SPItemFieldRawValue { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPField]$Field ) try { return $Item[$Field.InternalName] } catch { try { return $Item[$Field.Id] } catch { return $null } } } function Test-SPValuePresent { param( $Value ) if ($null -eq $Value) { return $false } if ($Value -is [string]) { return -not [string]::IsNullOrWhiteSpace($Value) } if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) { return @($Value).Count -gt 0 } return $true } function Get-SPItemMissingRequiredFields { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item ) $missingFields = @() foreach ($field in $Item.Fields) { if (-not $field.Required) { continue } if ($field.TypeAsString -eq "Computed" -or $field.TypeAsString -eq "Attachments") { continue } $rawValue = Get-SPItemFieldRawValue -Item $Item -Field $field if (-not (Test-SPValuePresent -Value $rawValue)) { $missingFields += $field.InternalName } } return $missingFields } function Save-SPListItem { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPListItem]$Item, [string]$Context = "" ) $missingRequiredFields = @(Get-SPItemMissingRequiredFields -Item $Item) if ($missingRequiredFields.Count -gt 0) { $message = "Pflichtfelder ohne Wert: {0}" -f ($missingRequiredFields -join ", ") if (-not [string]::IsNullOrWhiteSpace($Context)) { $message = "{0}. {1}" -f $Context, $message } throw $message } $saveErrors = @() try { $Item.UpdateOverwriteVersion() return } catch { $saveErrors += ("UpdateOverwriteVersion: {0}" -f $_.Exception.Message) } try { $Item.SystemUpdate($false) return } catch { $saveErrors += ("SystemUpdate(false): {0}" -f $_.Exception.Message) } try { $Item.Update() return } catch { $saveErrors += ("Update: {0}" -f $_.Exception.Message) } $missingRequiredFields = @(Get-SPItemMissingRequiredFields -Item $Item) $messageParts = @() if (-not [string]::IsNullOrWhiteSpace($Context)) { $messageParts += $Context } if ($missingRequiredFields.Count -gt 0) { $messageParts += ("Pflichtfelder ohne Wert: {0}" -f ($missingRequiredFields -join ", ")) } if ($saveErrors.Count -gt 0) { $messageParts += ("Save-Versuche fehlgeschlagen: {0}" -f ($saveErrors -join " | ")) } throw ("Konnte Listenelement nicht speichern. {0}" -f ($messageParts -join ". ")) } 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 Get-SPFolderFileByName { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFolder]$Folder, [Parameter(Mandatory = $true)] [string]$FileName ) foreach ($existingFile in $Folder.Files) { if ($existingFile.Name -eq $FileName) { return $existingFile } } return $null } function Update-SPExistingFileAsNewVersion { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFile]$ExistingFile, [Parameter(Mandatory = $true)] [byte[]]$FileBytes ) $requiresCheckout = $false try { $requiresCheckout = [bool]$ExistingFile.ParentFolder.DocumentLibrary.ForceCheckout } catch { $requiresCheckout = $false } if ($requiresCheckout -and $ExistingFile.CheckOutType -eq [Microsoft.SharePoint.SPFile+SPCheckOutType]::None) { $ExistingFile.CheckOut() } $existingFile.SaveBinary($FileBytes) if ($ExistingFile.CheckOutType -ne [Microsoft.SharePoint.SPFile+SPCheckOutType]::None) { $checkInType = [Microsoft.SharePoint.SPCheckinType]::MajorCheckIn try { if ([bool]$ExistingFile.ParentFolder.DocumentLibrary.EnableMinorVersions) { $checkInType = [Microsoft.SharePoint.SPCheckinType]::MinorCheckIn } } catch { } $ExistingFile.CheckIn("Imported by Start-SPMigration", $checkInType) } return $ExistingFile } function Import-SPFileToFolder { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPFolder]$TargetFolder, [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPList]$TargetLibrary, [Parameter(Mandatory = $true)] [string]$FileName, [Parameter(Mandatory = $true)] [byte[]]$FileBytes, [switch]$Overwrite ) $existingFile = Get-SPFolderFileByName -Folder $TargetFolder -FileName $FileName if ($null -eq $existingFile) { return $TargetFolder.Files.Add($FileName, $FileBytes, $false) } if ($Overwrite.IsPresent) { return $TargetFolder.Files.Add($FileName, $FileBytes, $true) } if (-not [bool]$TargetLibrary.EnableVersioning) { Write-Warning ("Datei uebersprungen, da sie bereits existiert und in der Zielbibliothek keine Versionierung aktiv ist: {0}/{1}" -f $TargetLibrary.Title, $FileName) return $null } Write-Host ("Datei existiert bereits. Erzeuge neue Version: {0}/{1}" -f $TargetLibrary.Title, $FileName) return Update-SPExistingFileAsNewVersion -ExistingFile $existingFile -FileBytes $FileBytes } function Import-SPDocumentLibraries { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [string]$FilesRootPath, [Parameter(Mandatory = $true)] $MigrationMappingTable, [switch]$Overwrite ) if (-not [System.IO.Directory]::Exists($FilesRootPath)) { Write-Warning ("Kein Files-Ordner gefunden: {0}" -f $FilesRootPath) return } $containerMappingMap = Get-ContainerMappingMap -MigrationMappingTable $MigrationMappingTable $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-TargetContainerTitleFromMappingTable -ContainerMappingMap $containerMappingMap -ObjectType "DocumentLibrary" -SourceTitle $sourceLibraryTitle $targetLibrary = $Web.Lists.TryGetList($targetLibraryTitle) if ($null -eq $targetLibrary) { if (Test-ContainerHasExplicitTargetMappingInTable -ContainerMappingMap $containerMappingMap -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 in der MappingTable gepflegt. Bitte Bibliothek manuell anlegen oder TargetTitle in der MappingTable 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 = Import-SPFileToFolder -TargetFolder $targetFolder -TargetLibrary $targetLibrary -FileName $fileName -FileBytes $fileBytes -Overwrite:$Overwrite if ($null -eq $spFile) { continue } $spItem = $spFile.Item if ($null -ne $itemMetadata) { try { $fieldMapping = Get-FieldMappingForContainer -MigrationMappingTable $MigrationMappingTable -ObjectType "DocumentLibrary" -SourceTitle $sourceLibraryTitle Apply-FieldMappingToItem -Item $spItem -FieldMapping $fieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $itemMetadata -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $itemMetadata -PropertyName "FieldTextValues") Save-SPListItem -Item $spItem -Context ("Bibliothek '{0}', Datei '{1}'" -f $targetLibrary.Title, $fileName) } catch { Write-Warning ("Metadaten fuer Datei konnten nicht gespeichert werden: {0}" -f $_.Exception.Message) } } } } function Import-SPLists { param( [Parameter(Mandatory = $true)] [Microsoft.SharePoint.SPWeb]$Web, [Parameter(Mandatory = $true)] [string]$ListsRootPath, [Parameter(Mandatory = $true)] $MigrationMappingTable ) if (-not [System.IO.Directory]::Exists($ListsRootPath)) { Write-Warning ("Kein Lists-Ordner gefunden: {0}" -f $ListsRootPath) return } $containerMappingMap = Get-ContainerMappingMap -MigrationMappingTable $MigrationMappingTable $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-TargetContainerTitleFromMappingTable -ContainerMappingMap $containerMappingMap -ObjectType "List" -SourceTitle $sourceListTitle $targetList = $Web.Lists.TryGetList($targetListTitle) if ($null -eq $targetList) { if (Test-ContainerHasExplicitTargetMappingInTable -ContainerMappingMap $containerMappingMap -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 in der MappingTable gepflegt. Bitte Liste manuell anlegen oder TargetTitle in der MappingTable 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) $fieldMapping = Get-FieldMappingForContainer -MigrationMappingTable $MigrationMappingTable -ObjectType "List" -SourceTitle $sourceListTitle foreach ($sourceItem in $items) { $sourceItemId = [string](Get-ObjectPropertyValue -Object $sourceItem -PropertyName "Id") $sourceItemTitle = [string](Get-ObjectPropertyValue -Object $sourceItem -PropertyName "Title") $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")) } try { $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 -Context ("Liste '{0}', Quell-ItemId '{1}', Titel '{2}'" -f $targetListTitle, $sourceItemId, $sourceItemTitle) } catch { Write-Warning ("Listenelement wurde uebersprungen: {0}" -f $_.Exception.Message) continue } } } } 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") $mappingTablePath = if ([string]::IsNullOrWhiteSpace($MappingTable)) { [System.IO.Path]::Combine($resolvedOutputPath, "MappingTable.json") } else { [System.IO.Path]::GetFullPath($MappingTable) } $shouldExport = $Export.IsPresent $shouldImport = $Import.IsPresent if (-not $shouldExport -and -not $shouldImport) { $shouldExport = -not [string]::IsNullOrWhiteSpace($SourceUrl) $shouldImport = -not [string]::IsNullOrWhiteSpace($TargetUrl) } if (-not $shouldExport -and -not $shouldImport) { throw "Bitte mindestens -Export oder -Import angeben." } if ($shouldImport -and [string]::IsNullOrWhiteSpace($TargetUrl)) { throw "Fuer den Import muss TargetUrl angegeben werden." } if ($shouldImport -and -not $shouldExport) { if (-not [System.IO.File]::Exists($mappingTablePath)) { throw ("MappingTable nicht gefunden: {0}" -f $mappingTablePath) } Invoke-MigrationImport -InputPath $resolvedOutputPath -TargetWebUrl $TargetUrl -MappingTablePath $mappingTablePath -ImportFiles:$ImportFiles -ImportLists:$ImportLists -Overwrite:$Overwrite return } if ([string]::IsNullOrWhiteSpace($SourceUrl)) { throw "Fuer den Export muss SourceUrl 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 $SourceUrl -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) $migrationMappingTable = New-MigrationMappingTable -SourceWebUrl $web.Url -DocumentLibraries $documentLibraries -Lists $lists Write-MigrationMappingTable -MappingTable $migrationMappingTable -Path $mappingTablePath Write-Host ("MappingTable geschrieben: {0}" -f $mappingTablePath) 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 $SourceUrl) return } Write-Host ("Export abgeschlossen. Ausgabe: {0}" -f $resolvedOutputPath) if (-not $shouldImport) { Write-Host "Import wurde nicht angefordert. Export ist abgeschlossen." return } Write-Host ("Starte Import nach Export. Zielweb: {0}" -f $TargetUrl) Invoke-MigrationImport -InputPath $resolvedOutputPath -TargetWebUrl $TargetUrl -MappingTablePath $mappingTablePath -ImportFiles:$ImportFiles -ImportLists:$ImportLists -Overwrite:$Overwrite } finally { if ($null -ne $web) { $web.Dispose() } }