2893 lines
83 KiB
PowerShell
2893 lines
83 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Alias("WebUrl")]
|
|
[string]$SourceUrl,
|
|
|
|
[string]$OutputPath = (Join-Path -Path (Get-Location).Path -ChildPath "SPMigrationOutput"),
|
|
|
|
[switch]$IncludeHiddenLibraries,
|
|
|
|
[switch]$IncludeHiddenLists,
|
|
|
|
[string[]]$SelectedContainerUrls = @(),
|
|
|
|
[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 Test-SPContainerSelected {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[Microsoft.SharePoint.SPList]$List,
|
|
|
|
[string[]]$SelectedContainerUrls = @()
|
|
)
|
|
|
|
if ($null -eq $SelectedContainerUrls -or $SelectedContainerUrls.Count -eq 0) {
|
|
return $true
|
|
}
|
|
|
|
$selectedLookup = @{}
|
|
foreach ($selectedUrl in @($SelectedContainerUrls)) {
|
|
if (-not [string]::IsNullOrWhiteSpace([string]$selectedUrl)) {
|
|
$selectedLookup[[string]$selectedUrl.Trim().ToLowerInvariant()] = $true
|
|
}
|
|
}
|
|
|
|
if ($selectedLookup.Count -eq 0) {
|
|
return $true
|
|
}
|
|
|
|
$candidateUrls = @(
|
|
[string]$List.RootFolder.ServerRelativeUrl,
|
|
[string]$List.DefaultViewUrl,
|
|
[string]$List.Title
|
|
)
|
|
|
|
foreach ($candidateUrl in $candidateUrls) {
|
|
if ([string]::IsNullOrWhiteSpace([string]$candidateUrl)) {
|
|
continue
|
|
}
|
|
|
|
$normalizedCandidateUrl = [string]$candidateUrl.Trim().ToLowerInvariant()
|
|
if ($selectedLookup.ContainsKey($normalizedCandidateUrl)) {
|
|
return $true
|
|
}
|
|
}
|
|
|
|
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]
|
|
}
|
|
elseif ([guid]::TryParse($rawTermString, [ref]([guid]::Empty))) {
|
|
$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) {
|
|
"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
|
|
(Test-SPContainerSelected -List $_ -SelectedContainerUrls $SelectedContainerUrls) -and
|
|
-not (Test-IsCatalogList -List $_)
|
|
}
|
|
)
|
|
|
|
$lists = @(
|
|
$web.Lists | Where-Object {
|
|
$_.BaseType -ne [Microsoft.SharePoint.SPBaseType]::DocumentLibrary -and
|
|
($IncludeHiddenLists -or -not $_.Hidden) -and
|
|
(Test-SPContainerSelected -List $_ -SelectedContainerUrls $SelectedContainerUrls) -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()
|
|
}
|
|
}
|