From bc3c72f64a6e0f7541d3738ed11c0b6cea7371bb Mon Sep 17 00:00:00 2001 From: Torsten Brendgen Date: Wed, 15 Jul 2026 21:30:15 +0200 Subject: [PATCH] =?UTF-8?q?Erweitere=20README=20mit=20Informationen=20zu?= =?UTF-8?q?=20WssId=20und=20Taxonomie-Feldverarbeitung;=20f=C3=BCge=20Kont?= =?UTF-8?q?extmen=C3=BC=20zum=20L=C3=B6schen=20von=20Mapping-Zeilen=20in?= =?UTF-8?q?=20der=20GUI=20hinzu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++ Start-SPMigration.ps1 | 205 +++++++++++++++++++++++++++++++-------- Start-SPMigrationGUI.ps1 | 137 +++++++++++++++++++++++--- 3 files changed, 294 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 66995dd..954cf01 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ Fuer das Mapping ist vor allem relevant: - `SourceSupportingInternalNames`: technische Begleitspalten wie `_0` werden nur noch informativ aufgefuehrt und nicht separat gemappt - `ImportSupported`: steuert, ob eine Spalte beim Import ueberhaupt beruecksichtigt wird - Taxonomy-Felder werden beim Import ueber den exportierten `TermGuid` gesetzt; dabei wird angenommen, dass derselbe Term im Ziel bereits mit identischer GUID existiert +- Die exportierte `WssId` wird bewusst nicht uebernommen: Sie ist nur innerhalb der Quell-SiteCollection gueltig. SharePoint loest beim Setzen des Feldwerts die zielseitige `WssId` auf und legt den Eintrag in der `TaxonomyHiddenList` bei Bedarf selbst an. Die Hidden List soll deshalb nicht separat migriert oder vorab befuellt werden. Wenn eine Ziel-Liste oder Ziel-Bibliothek nicht existiert, gibt das Skript eine Warnung aus, dass dieser Container manuell angelegt werden soll. @@ -311,6 +312,11 @@ Anschliessend: - Listeneintraege werden neu angelegt oder bei erneutem Import ueber `StartSPMigrationSourceUniqueId` wiedergefunden und aktualisiert. - Feldwerte werden ueber `FieldValues`, `FieldTextValues` und `MetadataColumnMappings` gemappt. +### GUI-Logging und Mapping-Bearbeitung + +- Beim Import protokolliert die GUI fuer jeden verarbeiteten Columnwert, ob die Zuweisung am Item erfolgreich war oder mit welchem Fehler sie uebersprungen wurde. Das Log nennt Container bzw. Item sowie Quell- und Zielspalte, schreibt aber nicht den eigentlichen Feldinhalt mit. +- In den Mapping-Tabs `Bibliotheken`, `Listen`, `System Columns` und `Custom Columns` kann eine Zeile per Rechtsklick und `Zeile loeschen` vollstaendig entfernt werden. Beim Speichern wird sie entsprechend nicht mehr in die `MappingTable.json` geschrieben. + ## Bekannte Einschraenkungen - Die Zielaufloesung kann ueber `MappingTable.json` mit `TargetTitle` ueberschrieben werden. diff --git a/Start-SPMigration.ps1 b/Start-SPMigration.ps1 index 72ac2b5..a4360e7 100644 --- a/Start-SPMigration.ps1 +++ b/Start-SPMigration.ps1 @@ -255,6 +255,28 @@ function Convert-SPFieldValue { return $Value.ToString("o") } + # Taxonomy values must be handled before the generic lookup/enumerable + # branches. Their WssId is only local to the source site collection; the + # portable part of the value is the label plus TermGuid. + $typeName = $Value.GetType().FullName + if ($typeName -like "*TaxonomyFieldValueCollection") { + return @($Value | ForEach-Object { + @{ + Label = $_.Label + TermGuid = $_.TermGuid + WssId = $_.WssId + } + }) + } + + if ($typeName -like "*TaxonomyFieldValue") { + return @{ + Label = $Value.Label + TermGuid = $Value.TermGuid + WssId = $Value.WssId + } + } + if ($Value -is [Microsoft.SharePoint.SPFieldUserValue]) { return @{ LookupId = $Value.LookupId @@ -294,25 +316,6 @@ function Convert-SPFieldValue { 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 } @@ -486,6 +489,7 @@ function Get-ListItemMetadataObject { UniqueId = $Item.UniqueId.ToString() Title = if ($Item.Fields.ContainsField("Title")) { $Item["Title"] } else { $null } ContentTypeId = $Item.ContentTypeId.ToString() + ContentTypeName = if ($null -ne $Item.ContentType) { $Item.ContentType.Name } else { "" } FileSystemObjectType = $Item.FileSystemObjectType.ToString() DisplayName = $Item.DisplayName Name = if ($Item.Name) { $Item.Name } else { $null } @@ -976,6 +980,8 @@ function Test-SPFieldSupportedForImport { "SMTotalSize", "SortBehavior", "SyncClientId", + "TaxCatchAll", + "TaxCatchAllLabel", "TimeZone", "UID", "UniqueId", @@ -1530,6 +1536,8 @@ function Test-MetadataColumnMappingRowImportSupported { "SMTotalSize", "SortBehavior", "SyncClientId", + "TaxCatchAll", + "TaxCatchAllLabel", "TimeZone", "UID", "UniqueId", @@ -1737,6 +1745,86 @@ function Resolve-SPField { return $null } +function Resolve-SPAvailableContentTypeForImport { + param( + [Parameter(Mandatory = $true)] + [Microsoft.SharePoint.SPWeb]$Web, + + [string]$SourceContentTypeId, + + [string]$SourceContentTypeName + ) + + $sourceId = $null + if (-not [string]::IsNullOrWhiteSpace($SourceContentTypeId)) { + try { + $sourceId = New-Object Microsoft.SharePoint.SPContentTypeId($SourceContentTypeId) + } + catch { + throw ("Ungueltige Source-ContentTypeId: {0}" -f $SourceContentTypeId) + } + } + + $idMatches = @() + if ($null -ne $sourceId) { + foreach ($contentType in @($Web.AvailableContentTypes)) { + if ($contentType.Id.Equals($sourceId) -or $contentType.Id.IsParentOf($sourceId)) { + $idMatches += $contentType + } + } + } + + if ($idMatches.Count -gt 0) { + return @($idMatches | Sort-Object { $_.Id.ToString().Length } -Descending)[0] + } + + if (-not [string]::IsNullOrWhiteSpace($SourceContentTypeName)) { + foreach ($contentType in @($Web.AvailableContentTypes)) { + if ([string]$contentType.Name -eq $SourceContentTypeName) { + return $contentType + } + } + } + + return $null +} + +function Ensure-SPListContentTypeForImport { + param( + [Parameter(Mandatory = $true)] + [Microsoft.SharePoint.SPList]$List, + + [string]$SourceContentTypeId, + + [string]$SourceContentTypeName + ) + + $siteContentType = Resolve-SPAvailableContentTypeForImport -Web $List.ParentWeb -SourceContentTypeId $SourceContentTypeId -SourceContentTypeName $SourceContentTypeName + if ($null -eq $siteContentType) { + throw ("Kein passender publizierter Site-Content-Type gefunden. SourceContentTypeId='{0}', SourceContentTypeName='{1}'" -f $SourceContentTypeId, $SourceContentTypeName) + } + + foreach ($listContentType in @($List.ContentTypes)) { + if ($listContentType.Id.Equals($siteContentType.Id) -or $siteContentType.Id.IsParentOf($listContentType.Id)) { + return $listContentType + } + } + + if (-not $List.ContentTypesEnabled) { + $List.ContentTypesEnabled = $true + $List.Update() + } + + if (-not $List.IsContentTypeAllowed($siteContentType)) { + throw ("Content-Type '{0}' ({1}) ist fuer die Zielliste '{2}' nicht zulaessig." -f $siteContentType.Name, $siteContentType.Id, $List.Title) + } + + $listContentType = $List.ContentTypes.Add($siteContentType) + $List.Update() + Write-Host ("Content-Type zur Zielliste hinzugefuegt: Liste='{0}'; ContentType='{1}'; SiteContentTypeId='{2}'; ListContentTypeId='{3}'" -f $List.Title, $siteContentType.Name, $siteContentType.Id, $listContentType.Id) + return $listContentType +} + function Get-FieldMapping { param( [Parameter(Mandatory = $true)] @@ -1989,7 +2077,7 @@ function Convert-ToTaxonomyTermEntries { $label = $matches[1] $termGuid = $matches[2] } - elseif ([guid]::TryParse($rawTermString, [ref]([guid]::Empty))) { + elseif ($rawTermString -match "^[0-9a-fA-F-]{36}$") { $termGuid = $rawTermString } } @@ -1998,10 +2086,13 @@ function Convert-ToTaxonomyTermEntries { $label = $TextValue } - if ([string]::IsNullOrWhiteSpace($termGuid)) { + $parsedTermGuid = [Guid]::Empty + if ([string]::IsNullOrWhiteSpace($termGuid) -or -not [Guid]::TryParse($termGuid, [ref]$parsedTermGuid)) { continue } + $termGuid = $parsedTermGuid.ToString() + if ([string]::IsNullOrWhiteSpace($label)) { $label = $termGuid } @@ -2070,6 +2161,10 @@ function New-TaxonomyFieldValueObject { try { $taxonomyValue.PopulateFromLabelGuidPair($labelGuidPair) + # WssIds belong to the site collection and must never be carried over + # from the source TaxonomyHiddenList. -1 lets SharePoint resolve (and, + # when necessary, create) the target-site entry from the TermGuid. + $taxonomyValue.WssId = -1 $isPopulated = $true } catch { @@ -2110,6 +2205,10 @@ function Set-SPTaxonomyFieldValue { $termEntries = @(Convert-ToTaxonomyTermEntries -RawValue $RawValue -TextValue $TextValue) if ($termEntries.Count -eq 0) { + if ((Test-SPValuePresent -Value $RawValue) -or -not [string]::IsNullOrWhiteSpace($TextValue)) { + throw ("Taxonomy-Wert fuer Feld '{0}' enthaelt keine gueltige TermGuid. Der Wert kann nicht in die Ziel-SiteCollection aufgeloest werden." -f $Field.InternalName) + } + $Item[$Field.InternalName] = $null return } @@ -2220,51 +2319,57 @@ function Set-SPItemFieldValue { $field = Resolve-SPField -Fields $Item.Fields -InternalName $TargetInternalName if ($null -eq $field) { - Write-SkippedImportFieldWarning -FieldInternalName $TargetInternalName -Message ("Zielfeld nicht gefunden: {0}" -f $TargetInternalName) - return + throw ("Zielfeld nicht gefunden: {0}" -f $TargetInternalName) } if (-not (Test-SPFieldSupportedForImport -Field $field)) { - Write-SkippedImportFieldWarning -FieldInternalName $TargetInternalName -Message ("Zielfeld wird nicht importiert und wird uebersprungen: {0}" -f $TargetInternalName) - return + throw ("Zielfeld wird nicht importiert und wird uebersprungen: {0}" -f $TargetInternalName) } try { switch ($field.TypeAsString) { "Boolean" { $Item[$field.InternalName] = Convert-ToBoolean -RawValue $RawValue -TextValue $TextValue - return + return $true } "Integer" { $Item[$field.InternalName] = Convert-ToInt32 -RawValue $RawValue -TextValue $TextValue - return + return $true } "Counter" { - return + throw ("Counter-Feld kann nicht gesetzt werden: {0}" -f $TargetInternalName) } "Number" { $Item[$field.InternalName] = Convert-ToDouble -RawValue $RawValue -TextValue $TextValue - return + return $true } "Currency" { $Item[$field.InternalName] = Convert-ToDouble -RawValue $RawValue -TextValue $TextValue - return + return $true } "DateTime" { $Item[$field.InternalName] = Convert-ToDateTimeValue -RawValue $RawValue -TextValue $TextValue - return + return $true } "URL" { $Item[$field.InternalName] = Convert-ToUrlFieldValue -RawValue $RawValue -TextValue $TextValue - return + return $true } "User" { $Item[$field.InternalName] = Convert-ToUserFieldValue -Item $Item -RawValue $RawValue -TextValue $TextValue - return + return $true } "UserMulti" { $Item[$field.InternalName] = Convert-ToUserMultiFieldValue -Item $Item -RawValue $RawValue -TextValue $TextValue - return + return $true + } + "TaxonomyFieldType" { + Set-SPTaxonomyFieldValue -Item $Item -Field $field -RawValue $RawValue -TextValue $TextValue + return $true + } + "TaxonomyFieldTypeMulti" { + Set-SPTaxonomyFieldValue -Item $Item -Field $field -RawValue $RawValue -TextValue $TextValue + return $true } default { if ($null -eq $RawValue) { @@ -2274,22 +2379,28 @@ function Set-SPItemFieldValue { $Item[$field.InternalName] = $RawValue } - return + return $true } } } catch { + $setErrorMessage = $_.Exception.Message + + if ($field.TypeAsString -in @("TaxonomyFieldType", "TaxonomyFieldTypeMulti")) { + throw ("Konnte Taxonomy-Feld '{0}' nicht ueber die TermGuid setzen. {1}" -f $TargetInternalName, $setErrorMessage) + } + if (-not [string]::IsNullOrWhiteSpace($TextValue)) { try { $field.ParseAndSetValue($Item, $TextValue) - return + return $true } catch { + throw ("Konnte Feld '{0}' weder direkt noch ueber ParseAndSetValue setzen. Direkt: {1}; ParseAndSetValue: {2}" -f $TargetInternalName, $setErrorMessage, $_.Exception.Message) } } - Write-SkippedImportFieldWarning -FieldInternalName $TargetInternalName -Message ("Konnte Feld '{0}' nicht setzen und ueberspringe es. {1}" -f $TargetInternalName, $_.Exception.Message) - return + throw ("Konnte Feld '{0}' nicht setzen. {1}" -f $TargetInternalName, $setErrorMessage) } } @@ -2303,7 +2414,9 @@ function Apply-FieldMappingToItem { $SourceFieldValues, - $SourceFieldTextValues + $SourceFieldTextValues, + + [string]$Context = "" ) foreach ($sourceInternalName in $FieldMapping.Keys) { @@ -2340,7 +2453,15 @@ function Apply-FieldMappingToItem { $null } - Set-SPItemFieldValue -Item $Item -TargetInternalName $targetInternalName -RawValue $rawValue -TextValue $textValue + try { + $fieldWasSet = Set-SPItemFieldValue -Item $Item -TargetInternalName $targetInternalName -RawValue $rawValue -TextValue $textValue + if ($fieldWasSet) { + Write-Host ("Columnwert erfolgreich gesetzt: {0}; SourceColumn='{1}'; TargetColumn='{2}'" -f $Context, $sourceInternalName, $targetInternalName) + } + } + catch { + Write-Warning ("Fehler beim Setzen eines Columnwerts: {0}; SourceColumn='{1}'; TargetColumn='{2}'; Fehler={3}" -f $Context, $sourceInternalName, $targetInternalName, $_.Exception.Message) + } } } @@ -2696,7 +2817,7 @@ function Import-SPDocumentLibraries { 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") + Apply-FieldMappingToItem -Item $spItem -FieldMapping $fieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $itemMetadata -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $itemMetadata -PropertyName "FieldTextValues") -Context ("Bibliothek='{0}'; Datei='{1}'" -f $targetLibrary.Title, $fileName) Save-SPListItem -Item $spItem -Context ("Bibliothek '{0}', Datei '{1}'" -f $targetLibrary.Title, $fileName) } catch { @@ -2846,7 +2967,7 @@ function Import-SPLists { $targetItem = $targetList.Items.Add() } - Apply-FieldMappingToItem -Item $targetItem -FieldMapping $fieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldTextValues") + Apply-FieldMappingToItem -Item $targetItem -FieldMapping $fieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldTextValues") -Context ("Liste='{0}'; SourceItemId='{1}'; Titel='{2}'" -f $targetListTitle, $sourceItemId, $sourceItemTitle) if (-not [string]::IsNullOrWhiteSpace($sourceItemUniqueId)) { $targetItem[$trackingField.InternalName] = $sourceItemUniqueId diff --git a/Start-SPMigrationGUI.ps1 b/Start-SPMigrationGUI.ps1 index ede7a46..0609e42 100644 --- a/Start-SPMigrationGUI.ps1 +++ b/Start-SPMigrationGUI.ps1 @@ -808,6 +808,7 @@ $script:MigrationStreamPositions = @{} $script:MigrationCompletedHandler = $null $script:MigrationActionLabel = "" $script:LastMigrationErrorMessage = "" +$script:PendingExportMappingPath = "" function Get-ObjectPropertyValue { param( @@ -1257,6 +1258,7 @@ function Complete-MigrationScriptAsync { } } else { + $script:PendingExportMappingPath = "" Show-UiMessage -Message $errorMessage -Caption ("{0} fehlgeschlagen" -f $actionLabel) -Icon ([System.Windows.Forms.MessageBoxIcon]::Error) } } @@ -1791,6 +1793,103 @@ function Set-DataTableRows { } } +function Add-MappingGridRowDeleteContextMenu { + param( + [Parameter(Mandatory = $true)] + [System.Windows.Forms.DataGridView]$Grid, + + [Parameter(Mandatory = $true)] + [System.Data.DataTable]$Table, + + [Parameter(Mandatory = $true)] + [string]$MappingName + ) + + $contextMenu = New-Object System.Windows.Forms.ContextMenuStrip + $deleteRowItem = New-Object System.Windows.Forms.ToolStripMenuItem("Zeile loeschen") + [void]$contextMenu.Items.Add($deleteRowItem) + + $context = [PSCustomObject]@{ + Grid = $Grid + Table = $Table + MappingName = $MappingName + ClickedRowIndex = -1 + } + $contextMenu.Tag = $context + + $Grid.Add_MouseDown({ + param($sender, $e) + + if ($e.Button -ne [System.Windows.Forms.MouseButtons]::Right) { + return + } + + $menuContext = $sender.ContextMenuStrip.Tag + $hit = $sender.HitTest($e.X, $e.Y) + $menuContext.ClickedRowIndex = $hit.RowIndex + + if ($hit.RowIndex -lt 0) { + $sender.ClearSelection() + return + } + + $columnIndex = if ($hit.ColumnIndex -ge 0) { $hit.ColumnIndex } else { 0 } + $sender.ClearSelection() + $sender.CurrentCell = $sender.Rows[$hit.RowIndex].Cells[$columnIndex] + $sender.Rows[$hit.RowIndex].Selected = $true + }) + + $contextMenu.Add_Opening({ + param($sender, $e) + + $menuContext = $sender.Tag + if ($menuContext.ClickedRowIndex -lt 0 -or $menuContext.ClickedRowIndex -ge $menuContext.Grid.Rows.Count) { + $e.Cancel = $true + } + }) + + $deleteRowItem.Add_Click({ + param($sender, $e) + + $menuContext = $sender.Owner.Tag + $rowIndex = [int]$menuContext.ClickedRowIndex + if ($rowIndex -lt 0 -or $rowIndex -ge $menuContext.Grid.Rows.Count) { + return + } + + $gridRow = $menuContext.Grid.Rows[$rowIndex] + if ($gridRow.IsNewRow) { + return + } + + $rowLabel = "Zeile {0}" -f ($rowIndex + 1) + $dataRowView = $gridRow.DataBoundItem -as [System.Data.DataRowView] + if ($null -ne $dataRowView) { + foreach ($candidateColumn in @("SourceTitle", "SourceInternalName", "DisplayName")) { + if (-not $menuContext.Table.Columns.Contains($candidateColumn)) { + continue + } + + $candidateValue = [string]$dataRowView.Row[$candidateColumn] + if (-not [string]::IsNullOrWhiteSpace($candidateValue)) { + $rowLabel = "{0}='{1}'" -f $candidateColumn, $candidateValue + break + } + } + + $menuContext.Table.Rows.Remove($dataRowView.Row) + } + else { + $menuContext.Grid.Rows.Remove($gridRow) + } + + $menuContext.ClickedRowIndex = -1 + Write-UILog -Message ("Mapping-Zeile geloescht: Bereich='{0}'; {1}" -f $menuContext.MappingName, $rowLabel) + }) + + $Grid.ContextMenuStrip = $contextMenu +} + function Configure-MappingGrid { param( [Parameter(Mandatory = $true)] @@ -1800,7 +1899,10 @@ function Configure-MappingGrid { [System.Data.DataTable]$Table, [Parameter(Mandatory = $true)] - [object[]]$Schema + [object[]]$Schema, + + [Parameter(Mandatory = $true)] + [string]$MappingName ) $Grid.DataSource = $Table @@ -1816,6 +1918,8 @@ function Configure-MappingGrid { } } } + + Add-MappingGridRowDeleteContextMenu -Grid $Grid -Table $Table -MappingName $MappingName } function Convert-DataTableToObjects { @@ -2418,18 +2522,27 @@ function Invoke-ExportFromGui { } $mappingPath = Join-Path -Path $outputPath -ChildPath "MappingTable.json" + $script:PendingExportMappingPath = $mappingPath $onCompleted = { - if (Test-Path -LiteralPath $mappingPath) { - Load-MappingTableFromPath -Path $mappingPath - Apply-ColumnDefaultsIfAvailable - Save-MappingTableToPath -Path $mappingPath - Show-MappingTab + $completedMappingPath = [string]$script:PendingExportMappingPath + + try { + if (Test-Path -LiteralPath $completedMappingPath) { + Load-MappingTableFromPath -Path $completedMappingPath + Apply-ColumnDefaultsIfAvailable + Save-MappingTableToPath -Path $completedMappingPath + Show-MappingTab + } } - }.GetNewClosure() + finally { + $script:PendingExportMappingPath = "" + } + } Invoke-MigrationScript -Parameters $parameters -ActionLabel "Export" -OnCompleted $onCompleted } catch { + $script:PendingExportMappingPath = "" Write-UILog -Message $_.Exception.Message -Level "ERROR" Show-UiMessage -Message $_.Exception.Message -Caption "Export fehlgeschlagen" -Icon ([System.Windows.Forms.MessageBoxIcon]::Error) } @@ -2653,7 +2766,7 @@ $mappingLayout.ColumnCount = 1 [void]$mappingLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 34))) [void]$mappingLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100))) -$mappingInfoLabel = ([LabelBuilder]::new("TargetTitle und TargetInternalName koennen hier direkt angepasst werden. Vor dem Import wird die MappingTable automatisch gespeichert.")).Build() +$mappingInfoLabel = ([LabelBuilder]::new("TargetTitle und TargetInternalName koennen hier direkt angepasst werden. Mit Rechtsklick kann eine ganze Mapping-Zeile geloescht werden. Vor dem Import wird automatisch gespeichert.")).Build() $mappingInfoLabel.Dock = [System.Windows.Forms.DockStyle]::Fill $mappingTabs = ([TabControlBuilder]::new()).SetDock("Fill").Build() @@ -2670,10 +2783,10 @@ $script:gridCustomColumns = ([DataGridViewBuilder]::new()).SetDock("Fill").SetAl Initialize-MappingTables -Configure-MappingGrid -Grid $script:gridLibraryMappings -Table $script:LibraryMappingsTable -Schema $script:GridSchemas.LibraryMappings -Configure-MappingGrid -Grid $script:gridListMappings -Table $script:ListMappingsTable -Schema $script:GridSchemas.ListMappings -Configure-MappingGrid -Grid $script:gridSystemColumns -Table $script:SystemColumnsTable -Schema $script:GridSchemas.SystemColumns -Configure-MappingGrid -Grid $script:gridCustomColumns -Table $script:CustomColumnsTable -Schema $script:GridSchemas.CustomColumns +Configure-MappingGrid -Grid $script:gridLibraryMappings -Table $script:LibraryMappingsTable -Schema $script:GridSchemas.LibraryMappings -MappingName "Bibliotheken" +Configure-MappingGrid -Grid $script:gridListMappings -Table $script:ListMappingsTable -Schema $script:GridSchemas.ListMappings -MappingName "Listen" +Configure-MappingGrid -Grid $script:gridSystemColumns -Table $script:SystemColumnsTable -Schema $script:GridSchemas.SystemColumns -MappingName "System Columns" +Configure-MappingGrid -Grid $script:gridCustomColumns -Table $script:CustomColumnsTable -Schema $script:GridSchemas.CustomColumns -MappingName "Custom Columns" $libraryMappingTab.Controls.Add($script:gridLibraryMappings) $listMappingTab.Controls.Add($script:gridListMappings)