Add column mapping defaults functionality and example JSON file
This commit is contained in:
40
README.md
40
README.md
@@ -28,6 +28,7 @@ Import-Module SharePointServer
|
||||
|
||||
- `Start-SPMigration.ps1`: Hauptskript fuer Export und optionalen Import
|
||||
- `FieldMapping.sample.csv`: altes CSV-Beispiel fuer Feldmapping, weiterhin als Fallback lesbar
|
||||
- `column-mapping-defaults.sample.json`: Beispiel fuer wiederverwendbare Column-Mapping-Defaults
|
||||
|
||||
## Exportierte Struktur
|
||||
|
||||
@@ -164,6 +165,42 @@ Fuer das Mapping ist vor allem relevant:
|
||||
|
||||
Wenn eine Ziel-Liste oder Ziel-Bibliothek nicht existiert, gibt das Skript eine Warnung aus, dass dieser Container manuell angelegt werden soll.
|
||||
|
||||
## Column Mapping Defaults
|
||||
|
||||
Wenn Bibliotheks- und Listen-Mappings je Migration unterschiedlich sind, die Spalten-Mappings aber gleich bleiben, kann die GUI eine separate Column-Defaults-Datei verwenden.
|
||||
|
||||
- Die `MappingTable.json` bleibt migrationsspezifisch und enthaelt weiterhin `LibraryMappings`, `ListMappings` und die konkret angewendeten `MetadataColumnMappings`.
|
||||
- Die Defaults-Datei enthaelt nur wiederverwendbare Spaltenregeln unter `Rules`.
|
||||
- In der GUI kann ein Defaults-Pfad im Feld `ColumnDefaults` gepflegt werden. Nach einem Export werden vorhandene Defaults automatisch auf die frisch geladene `MappingTable.json` angewendet und gespeichert.
|
||||
- Mit `Anwenden` koennen Defaults erneut auf die aktuelle MappingTable gelegt werden. Danach bleiben manuelle Anpassungen pro Migration weiterhin moeglich.
|
||||
- Mit `Speichern` erzeugt die GUI aus den aktuellen Column-Mappings ein Defaults-Template. Gleichartige Spalten werden dabei auf `ContainerSourceTitle = "*"` generalisiert; widerspruechliche Regeln bleiben container-spezifisch.
|
||||
|
||||
Regeln werden von spezifisch nach allgemein angewendet:
|
||||
|
||||
- `ObjectType + ContainerSourceTitle + SourceInternalName`
|
||||
- `ObjectType + * + SourceInternalName`
|
||||
- `* + ContainerSourceTitle + SourceInternalName`
|
||||
- `* + * + SourceInternalName`
|
||||
|
||||
Beispiel:
|
||||
|
||||
```json
|
||||
{
|
||||
"SchemaVersion": 1,
|
||||
"TemplateType": "ColumnMappings",
|
||||
"Rules": [
|
||||
{
|
||||
"ObjectType": "*",
|
||||
"ContainerSourceTitle": "*",
|
||||
"SourceInternalName": "SecurityClearance",
|
||||
"TargetInternalName": "GMNSecurityClearance",
|
||||
"ImportSupported": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Parameter
|
||||
|
||||
### Pflichtparameter
|
||||
@@ -196,6 +233,7 @@ Hinweis:
|
||||
- Wenn `Overwrite` nicht gesetzt ist und eine Datei bereits existiert, wird bei aktiver Versionierung eine neue Version geschrieben; ohne Versionierung wird die Datei uebersprungen.
|
||||
- Nicht importierbare Systemfelder wie `Attachments`, `Created`, `Modified`, `Author` oder `Editor` werden automatisch aus der MappingTable herausgehalten bzw. beim Import uebersprungen.
|
||||
- Vor dem Speichern prueft das Skript fehlende Pflichtfelder. Wenn ein einzelnes Listen- oder Datei-Metadatenobjekt trotzdem nicht gespeichert werden kann, wird es mit Kontext-Warnung uebersprungen und der Import laeuft weiter.
|
||||
- Beim Listenimport legt das Skript bei Bedarf ein verstecktes Textfeld `StartSPMigrationSourceUniqueId` in der Zielliste an. Wiederholte Imports aktualisieren damit vorhandene Elemente anhand der exportierten Source-`UniqueId` statt sie erneut anzulegen.
|
||||
|
||||
## Beispiele
|
||||
|
||||
@@ -270,7 +308,7 @@ Anschliessend:
|
||||
|
||||
### Listen
|
||||
|
||||
- Listeneintraege werden neu angelegt.
|
||||
- Listeneintraege werden neu angelegt oder bei erneutem Import ueber `StartSPMigrationSourceUniqueId` wiedergefunden und aktualisiert.
|
||||
- Feldwerte werden ueber `FieldValues`, `FieldTextValues` und `MetadataColumnMappings` gemappt.
|
||||
|
||||
## Bekannte Einschraenkungen
|
||||
|
||||
@@ -32,6 +32,8 @@ param(
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$script:SkippedImportFieldWarnings = @{}
|
||||
$script:ListImportTrackingFieldInternalName = "StartSPMigrationSourceUniqueId"
|
||||
$script:ListImportTrackingFieldDisplayName = "Start-SPMigration Source UniqueId"
|
||||
|
||||
function Initialize-SharePointPowerShell {
|
||||
if($null -eq $(Get-Module SharePointServer)) {
|
||||
@@ -2704,6 +2706,68 @@ function Import-SPDocumentLibraries {
|
||||
}
|
||||
}
|
||||
|
||||
function Ensure-SPMigrationListTrackingField {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[Microsoft.SharePoint.SPList]$List
|
||||
)
|
||||
|
||||
$field = Resolve-SPField -Fields $List.Fields -InternalName $script:ListImportTrackingFieldInternalName
|
||||
if ($null -ne $field) {
|
||||
return $field
|
||||
}
|
||||
|
||||
[void]$List.Fields.Add($script:ListImportTrackingFieldInternalName, [Microsoft.SharePoint.SPFieldType]::Text, $false)
|
||||
$List.Update()
|
||||
|
||||
$field = Resolve-SPField -Fields $List.Fields -InternalName $script:ListImportTrackingFieldInternalName
|
||||
if ($null -eq $field) {
|
||||
throw ("Konnte Tracking-Feld fuer idempotenten Listenimport nicht anlegen: {0}" -f $List.Title)
|
||||
}
|
||||
|
||||
$field.Title = $script:ListImportTrackingFieldDisplayName
|
||||
$field.Hidden = $true
|
||||
|
||||
try {
|
||||
$field.Indexed = $true
|
||||
}
|
||||
catch {
|
||||
Write-Warning ("Tracking-Feld konnte nicht indiziert werden: {0}. {1}" -f $List.Title, $_.Exception.Message)
|
||||
}
|
||||
|
||||
$field.Update()
|
||||
$List.Update()
|
||||
|
||||
return $field
|
||||
}
|
||||
|
||||
function Get-SPListItemByMigrationSourceUniqueId {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[Microsoft.SharePoint.SPList]$List,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$FieldInternalName,
|
||||
|
||||
[string]$SourceUniqueId
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($SourceUniqueId)) {
|
||||
return $null
|
||||
}
|
||||
|
||||
$escapedValue = [System.Security.SecurityElement]::Escape($SourceUniqueId)
|
||||
$query = New-Object Microsoft.SharePoint.SPQuery
|
||||
$query.RowLimit = 1
|
||||
$query.Query = "<Where><Eq><FieldRef Name='$FieldInternalName' /><Value Type='Text'>$escapedValue</Value></Eq></Where>"
|
||||
|
||||
$matchingItems = $List.GetItems($query)
|
||||
if ($matchingItems.Count -eq 0) {
|
||||
return $null
|
||||
}
|
||||
|
||||
return $matchingItems[0]
|
||||
}
|
||||
function Import-SPLists {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@@ -2755,10 +2819,12 @@ function Import-SPLists {
|
||||
|
||||
Write-Host ("Importiere Liste: {0}" -f $targetListTitle)
|
||||
$fieldMapping = Get-FieldMappingForContainer -MigrationMappingTable $MigrationMappingTable -ObjectType "List" -SourceTitle $sourceListTitle
|
||||
$trackingField = Ensure-SPMigrationListTrackingField -List $targetList
|
||||
|
||||
foreach ($sourceItem in $items) {
|
||||
$sourceItemId = [string](Get-ObjectPropertyValue -Object $sourceItem -PropertyName "Id")
|
||||
$sourceItemTitle = [string](Get-ObjectPropertyValue -Object $sourceItem -PropertyName "Title")
|
||||
$sourceItemUniqueId = [string](Get-ObjectPropertyValue -Object $sourceItem -PropertyName "UniqueId")
|
||||
$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)
|
||||
@@ -2770,8 +2836,22 @@ function Import-SPLists {
|
||||
}
|
||||
|
||||
try {
|
||||
$targetItem = $targetList.Items.Add()
|
||||
$targetItem = Get-SPListItemByMigrationSourceUniqueId -List $targetList -FieldInternalName $trackingField.InternalName -SourceUniqueId $sourceItemUniqueId
|
||||
$isExistingItem = $null -ne $targetItem
|
||||
|
||||
if ($isExistingItem) {
|
||||
Write-Host ("Aktualisiere vorhandenes Listenelement: Liste '{0}', Quell-ItemId '{1}'" -f $targetListTitle, $sourceItemId)
|
||||
}
|
||||
else {
|
||||
$targetItem = $targetList.Items.Add()
|
||||
}
|
||||
|
||||
Apply-FieldMappingToItem -Item $targetItem -FieldMapping $fieldMapping -SourceFieldValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldValues") -SourceFieldTextValues (Get-ObjectPropertyValue -Object $sourceItem -PropertyName "FieldTextValues")
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($sourceItemUniqueId)) {
|
||||
$targetItem[$trackingField.InternalName] = $sourceItemUniqueId
|
||||
}
|
||||
|
||||
Save-SPListItem -Item $targetItem -Context ("Liste '{0}', Quell-ItemId '{1}', Titel '{2}'" -f $targetListTitle, $sourceItemId, $sourceItemTitle)
|
||||
}
|
||||
catch {
|
||||
|
||||
@@ -54,6 +54,7 @@ class SettingsManager {
|
||||
LastTargetUrl = ""
|
||||
LastOutputPath = (Join-Path -Path (Get-Location).Path -ChildPath "SPMigrationOutput")
|
||||
LastMappingTablePath = ""
|
||||
LastColumnDefaultsPath = ""
|
||||
}
|
||||
|
||||
foreach ($key in $defaults.Keys) {
|
||||
@@ -797,6 +798,14 @@ $script:CurrentMappingMeta = [ordered]@{
|
||||
GeneratedAtUtc = ""
|
||||
SourceWebUrl = ""
|
||||
}
|
||||
$script:IsMigrationRunning = $false
|
||||
$script:MigrationPowerShell = $null
|
||||
$script:MigrationAsyncResult = $null
|
||||
$script:MigrationOutput = $null
|
||||
$script:MigrationTimer = $null
|
||||
$script:MigrationStreamPositions = @{}
|
||||
$script:MigrationCompletedHandler = $null
|
||||
$script:MigrationActionLabel = ""
|
||||
|
||||
function Get-ObjectPropertyValue {
|
||||
param(
|
||||
@@ -872,7 +881,6 @@ function Write-UILog {
|
||||
$script:txtLog.AppendText($line)
|
||||
$script:txtLog.SelectionStart = $script:txtLog.TextLength
|
||||
$script:txtLog.ScrollToCaret()
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
}
|
||||
|
||||
switch ($Level) {
|
||||
@@ -1001,38 +1009,229 @@ function Convert-RecordToLogText {
|
||||
return [string]$Record
|
||||
}
|
||||
|
||||
function Set-MigrationUiBusy {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[bool]$IsBusy
|
||||
)
|
||||
|
||||
$script:IsMigrationRunning = $IsBusy
|
||||
|
||||
if ($null -ne $script:MainForm) {
|
||||
$script:MainForm.UseWaitCursor = $IsBusy
|
||||
}
|
||||
|
||||
$controlNames = @(
|
||||
"btnRunExport",
|
||||
"btnRunImport",
|
||||
"btnLoadContainers",
|
||||
"btnLoadMapping",
|
||||
"btnSaveMappingGlobal",
|
||||
"btnBrowseOutputPath",
|
||||
"btnSelectAllContainers",
|
||||
"btnClearContainerSelection",
|
||||
"btnLoadColumnDefaults",
|
||||
"btnApplyColumnDefaults",
|
||||
"btnSaveColumnDefaults"
|
||||
)
|
||||
|
||||
foreach ($controlName in $controlNames) {
|
||||
$variable = Get-Variable -Name $controlName -Scope Script -ErrorAction SilentlyContinue
|
||||
if ($null -eq $variable -or $null -eq $variable.Value) {
|
||||
continue
|
||||
}
|
||||
|
||||
$variable.Value.Enabled = -not $IsBusy
|
||||
}
|
||||
}
|
||||
|
||||
function Reset-MigrationStreamPositions {
|
||||
$script:MigrationStreamPositions = @{
|
||||
Output = 0
|
||||
Error = 0
|
||||
Warning = 0
|
||||
Verbose = 0
|
||||
Debug = 0
|
||||
Information = 0
|
||||
}
|
||||
}
|
||||
|
||||
function Write-NewPowerShellRecords {
|
||||
param(
|
||||
$Collection,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PositionKey,
|
||||
|
||||
[ValidateSet("INFO", "WARN", "ERROR")]
|
||||
[string]$Level = "INFO"
|
||||
)
|
||||
|
||||
if ($null -eq $Collection -or -not $script:MigrationStreamPositions.ContainsKey($PositionKey)) {
|
||||
return
|
||||
}
|
||||
|
||||
while ($script:MigrationStreamPositions[$PositionKey] -lt $Collection.Count) {
|
||||
$index = [int]$script:MigrationStreamPositions[$PositionKey]
|
||||
$script:MigrationStreamPositions[$PositionKey] = $index + 1
|
||||
|
||||
try {
|
||||
$record = $Collection[$index]
|
||||
}
|
||||
catch {
|
||||
return
|
||||
}
|
||||
|
||||
$text = Convert-RecordToLogText -Record $record
|
||||
if (-not [string]::IsNullOrWhiteSpace($text)) {
|
||||
Write-UILog -Message $text -Level $Level
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Write-NewMigrationPowerShellRecords {
|
||||
if ($null -eq $script:MigrationPowerShell) {
|
||||
return
|
||||
}
|
||||
|
||||
Write-NewPowerShellRecords -Collection $script:MigrationOutput -PositionKey "Output" -Level "INFO"
|
||||
Write-NewPowerShellRecords -Collection $script:MigrationPowerShell.Streams.Warning -PositionKey "Warning" -Level "WARN"
|
||||
Write-NewPowerShellRecords -Collection $script:MigrationPowerShell.Streams.Error -PositionKey "Error" -Level "ERROR"
|
||||
Write-NewPowerShellRecords -Collection $script:MigrationPowerShell.Streams.Verbose -PositionKey "Verbose" -Level "INFO"
|
||||
Write-NewPowerShellRecords -Collection $script:MigrationPowerShell.Streams.Debug -PositionKey "Debug" -Level "INFO"
|
||||
Write-NewPowerShellRecords -Collection $script:MigrationPowerShell.Streams.Information -PositionKey "Information" -Level "INFO"
|
||||
}
|
||||
|
||||
function Clear-MigrationRunState {
|
||||
if ($null -ne $script:MigrationPowerShell) {
|
||||
$script:MigrationPowerShell.Dispose()
|
||||
}
|
||||
|
||||
if ($null -ne $script:MigrationOutput) {
|
||||
try {
|
||||
$script:MigrationOutput.Dispose()
|
||||
}
|
||||
catch {
|
||||
}
|
||||
}
|
||||
|
||||
$script:MigrationPowerShell = $null
|
||||
$script:MigrationAsyncResult = $null
|
||||
$script:MigrationOutput = $null
|
||||
$script:MigrationCompletedHandler = $null
|
||||
$script:MigrationActionLabel = ""
|
||||
Reset-MigrationStreamPositions
|
||||
}
|
||||
|
||||
function Complete-MigrationScriptAsync {
|
||||
if ($null -eq $script:MigrationPowerShell -or $null -eq $script:MigrationAsyncResult) {
|
||||
return
|
||||
}
|
||||
|
||||
$actionLabel = $script:MigrationActionLabel
|
||||
$completedHandler = $script:MigrationCompletedHandler
|
||||
$success = $false
|
||||
$errorMessage = $null
|
||||
|
||||
Write-NewMigrationPowerShellRecords
|
||||
|
||||
try {
|
||||
[void]$script:MigrationPowerShell.EndInvoke($script:MigrationAsyncResult)
|
||||
Write-NewMigrationPowerShellRecords
|
||||
$success = $true
|
||||
}
|
||||
catch {
|
||||
Write-NewMigrationPowerShellRecords
|
||||
$errorMessage = $_.Exception.Message
|
||||
Write-UILog -Message $errorMessage -Level "ERROR"
|
||||
}
|
||||
finally {
|
||||
Clear-MigrationRunState
|
||||
Set-MigrationUiBusy -IsBusy $false
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
Write-UILog -Message ("{0} abgeschlossen." -f $actionLabel)
|
||||
|
||||
if ($null -ne $completedHandler) {
|
||||
try {
|
||||
& $completedHandler
|
||||
}
|
||||
catch {
|
||||
Write-UILog -Message $_.Exception.Message -Level "ERROR"
|
||||
Show-UiMessage -Message $_.Exception.Message -Caption ("{0} abgeschlossen, Nachbereitung fehlgeschlagen" -f $actionLabel) -Icon ([System.Windows.Forms.MessageBoxIcon]::Warning)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Show-UiMessage -Message $errorMessage -Caption ("{0} fehlgeschlagen" -f $actionLabel) -Icon ([System.Windows.Forms.MessageBoxIcon]::Error)
|
||||
}
|
||||
}
|
||||
|
||||
function Ensure-MigrationTimer {
|
||||
if ($null -ne $script:MigrationTimer) {
|
||||
return
|
||||
}
|
||||
|
||||
$script:MigrationTimer = New-Object System.Windows.Forms.Timer
|
||||
$script:MigrationTimer.Interval = 250
|
||||
$script:MigrationTimer.Add_Tick({
|
||||
Write-NewMigrationPowerShellRecords
|
||||
|
||||
if ($null -eq $script:MigrationAsyncResult -or -not $script:MigrationAsyncResult.IsCompleted) {
|
||||
return
|
||||
}
|
||||
|
||||
$script:MigrationTimer.Stop()
|
||||
Complete-MigrationScriptAsync
|
||||
})
|
||||
}
|
||||
|
||||
function Invoke-MigrationScript {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[hashtable]$Parameters,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ActionLabel
|
||||
[string]$ActionLabel,
|
||||
|
||||
[scriptblock]$OnCompleted = $null
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $script:MigrationScriptPath)) {
|
||||
throw ("Start-SPMigration.ps1 nicht gefunden: {0}" -f $script:MigrationScriptPath)
|
||||
}
|
||||
|
||||
$script:MainForm.UseWaitCursor = $true
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
if ($script:IsMigrationRunning) {
|
||||
throw "Es laeuft bereits ein Export oder Import. Bitte warten, bis der aktuelle Vorgang abgeschlossen ist."
|
||||
}
|
||||
|
||||
Reset-MigrationStreamPositions
|
||||
$script:MigrationPowerShell = [powershell]::Create()
|
||||
$script:MigrationOutput = [System.Management.Automation.PSDataCollection[psobject]]::new()
|
||||
$script:MigrationCompletedHandler = $OnCompleted
|
||||
$script:MigrationActionLabel = $ActionLabel
|
||||
|
||||
try {
|
||||
[void]$script:MigrationPowerShell.AddCommand($script:MigrationScriptPath)
|
||||
[void]$script:MigrationPowerShell.AddParameters($Parameters)
|
||||
|
||||
Set-MigrationUiBusy -IsBusy $true
|
||||
Write-UILog -Message ("Starte {0}..." -f $ActionLabel)
|
||||
& $script:MigrationScriptPath @Parameters *>&1 | ForEach-Object {
|
||||
$text = Convert-RecordToLogText -Record $_
|
||||
if (-not [string]::IsNullOrWhiteSpace($text)) {
|
||||
Write-UILog -Message $text
|
||||
}
|
||||
}
|
||||
Write-UILog -Message ("{0} abgeschlossen." -f $ActionLabel)
|
||||
|
||||
$inputCollection = [System.Management.Automation.PSDataCollection[psobject]]::new()
|
||||
$inputCollection.Complete()
|
||||
$script:MigrationAsyncResult = $script:MigrationPowerShell.BeginInvoke($inputCollection, $script:MigrationOutput)
|
||||
|
||||
Ensure-MigrationTimer
|
||||
$script:MigrationTimer.Start()
|
||||
}
|
||||
finally {
|
||||
$script:MainForm.UseWaitCursor = $false
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
catch {
|
||||
Clear-MigrationRunState
|
||||
Set-MigrationUiBusy -IsBusy $false
|
||||
throw
|
||||
}
|
||||
}
|
||||
|
||||
function Get-SourceContainers {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@@ -1465,7 +1664,7 @@ function Convert-DataTableToObjects {
|
||||
|
||||
foreach ($columnDefinition in $Schema) {
|
||||
$value = $row[$columnDefinition.Name]
|
||||
if ($value -eq [System.DBNull]::Value) {
|
||||
if ($value -is [System.DBNull]) {
|
||||
$value = $null
|
||||
}
|
||||
|
||||
@@ -1647,6 +1846,305 @@ function Save-MappingTableToPath {
|
||||
Write-UILog -Message ("MappingTable gespeichert: {0}" -f $resolvedPath)
|
||||
}
|
||||
|
||||
function Test-ObjectHasProperty {
|
||||
param(
|
||||
$Object,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PropertyName
|
||||
)
|
||||
|
||||
if ($null -eq $Object) {
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($Object -is [System.Collections.IDictionary]) {
|
||||
return $Object.Contains($PropertyName)
|
||||
}
|
||||
|
||||
return $null -ne $Object.PSObject.Properties[$PropertyName]
|
||||
}
|
||||
|
||||
function Get-ColumnDefaultRulesFromObject {
|
||||
param(
|
||||
$Template
|
||||
)
|
||||
|
||||
if ($null -eq $Template) {
|
||||
return @()
|
||||
}
|
||||
|
||||
$rules = Get-ObjectPropertyValue -Object $Template -PropertyName "Rules" -DefaultValue $null
|
||||
if ($null -ne $rules) {
|
||||
return @($rules)
|
||||
}
|
||||
|
||||
$rules = Get-ObjectPropertyValue -Object $Template -PropertyName "ColumnMappings" -DefaultValue $null
|
||||
if ($null -ne $rules) {
|
||||
return @($rules)
|
||||
}
|
||||
|
||||
$metadataColumnMappings = Get-ObjectPropertyValue -Object $Template -PropertyName "MetadataColumnMappings" -DefaultValue $null
|
||||
if ($null -ne $metadataColumnMappings) {
|
||||
return @(
|
||||
@(Get-ObjectPropertyValue -Object $metadataColumnMappings -PropertyName "SystemColumns" -DefaultValue @()) +
|
||||
@(Get-ObjectPropertyValue -Object $metadataColumnMappings -PropertyName "CustomColumns" -DefaultValue @())
|
||||
)
|
||||
}
|
||||
|
||||
return @()
|
||||
}
|
||||
|
||||
function Get-ColumnDefaultRuleMatchPriority {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$Rule,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[System.Data.DataRow]$Row
|
||||
)
|
||||
|
||||
$ruleSourceInternalName = [string](Get-ObjectPropertyValue -Object $Rule -PropertyName "SourceInternalName")
|
||||
if ([string]::IsNullOrWhiteSpace($ruleSourceInternalName)) {
|
||||
return -1
|
||||
}
|
||||
|
||||
$rowSourceInternalName = [string]$Row["SourceInternalName"]
|
||||
$rowCanonicalInternalName = [string]$Row["SourceCanonicalInternalName"]
|
||||
if ($ruleSourceInternalName -ine $rowSourceInternalName -and $ruleSourceInternalName -ine $rowCanonicalInternalName) {
|
||||
return -1
|
||||
}
|
||||
|
||||
$ruleObjectType = [string](Get-ObjectPropertyValue -Object $Rule -PropertyName "ObjectType" -DefaultValue "*")
|
||||
$ruleContainerSourceTitle = [string](Get-ObjectPropertyValue -Object $Rule -PropertyName "ContainerSourceTitle" -DefaultValue "*")
|
||||
$rowObjectType = [string]$Row["ObjectType"]
|
||||
$rowContainerSourceTitle = [string]$Row["ContainerSourceTitle"]
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($ruleObjectType)) {
|
||||
$ruleObjectType = "*"
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($ruleContainerSourceTitle)) {
|
||||
$ruleContainerSourceTitle = "*"
|
||||
}
|
||||
|
||||
if ($ruleObjectType -ne "*" -and $ruleObjectType -ine $rowObjectType) {
|
||||
return -1
|
||||
}
|
||||
|
||||
if ($ruleContainerSourceTitle -ne "*" -and $ruleContainerSourceTitle -ine $rowContainerSourceTitle) {
|
||||
return -1
|
||||
}
|
||||
|
||||
$priority = 0
|
||||
if ($ruleObjectType -ne "*") {
|
||||
$priority += 2
|
||||
}
|
||||
|
||||
if ($ruleContainerSourceTitle -ne "*") {
|
||||
$priority += 1
|
||||
}
|
||||
|
||||
return $priority
|
||||
}
|
||||
|
||||
function Get-BestColumnDefaultRule {
|
||||
param(
|
||||
[object[]]$Rules = @(),
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[System.Data.DataRow]$Row
|
||||
)
|
||||
|
||||
$bestRule = $null
|
||||
$bestPriority = -1
|
||||
|
||||
foreach ($rule in @($Rules)) {
|
||||
$priority = Get-ColumnDefaultRuleMatchPriority -Rule $rule -Row $Row
|
||||
if ($priority -gt $bestPriority) {
|
||||
$bestPriority = $priority
|
||||
$bestRule = $rule
|
||||
}
|
||||
}
|
||||
|
||||
return $bestRule
|
||||
}
|
||||
|
||||
function Apply-ColumnDefaultRulesToTable {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[System.Data.DataTable]$Table,
|
||||
|
||||
[object[]]$Rules = @()
|
||||
)
|
||||
|
||||
$updatedCount = 0
|
||||
|
||||
foreach ($row in $Table.Rows) {
|
||||
$rule = Get-BestColumnDefaultRule -Rules $Rules -Row $row
|
||||
if ($null -eq $rule) {
|
||||
continue
|
||||
}
|
||||
|
||||
$rowWasUpdated = $false
|
||||
|
||||
if (Test-ObjectHasProperty -Object $rule -PropertyName "TargetInternalName") {
|
||||
$targetInternalName = [string](Get-ObjectPropertyValue -Object $rule -PropertyName "TargetInternalName" -DefaultValue "")
|
||||
if (-not [string]::IsNullOrWhiteSpace($targetInternalName)) {
|
||||
$row["TargetInternalName"] = $targetInternalName
|
||||
$rowWasUpdated = $true
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-ObjectHasProperty -Object $rule -PropertyName "ImportSupported") {
|
||||
$row["ImportSupported"] = [bool](Get-ObjectPropertyValue -Object $rule -PropertyName "ImportSupported" -DefaultValue $false)
|
||||
$rowWasUpdated = $true
|
||||
}
|
||||
|
||||
if ($rowWasUpdated) {
|
||||
$updatedCount++
|
||||
}
|
||||
}
|
||||
|
||||
return $updatedCount
|
||||
}
|
||||
|
||||
function Apply-ColumnDefaultsFromPath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Path)) {
|
||||
throw "Bitte einen ColumnDefaults-Pfad angeben."
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw ("ColumnDefaults nicht gefunden: {0}" -f $Path)
|
||||
}
|
||||
|
||||
if ([System.IO.Path]::GetExtension($Path) -ine ".json") {
|
||||
throw "ColumnDefaults muessen als JSON-Datei vorliegen."
|
||||
}
|
||||
|
||||
$template = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
$rules = @(Get-ColumnDefaultRulesFromObject -Template $template)
|
||||
if ($rules.Count -eq 0) {
|
||||
throw "ColumnDefaults enthalten keine Rules."
|
||||
}
|
||||
|
||||
$systemCount = Apply-ColumnDefaultRulesToTable -Table $script:SystemColumnsTable -Rules $rules
|
||||
$customCount = Apply-ColumnDefaultRulesToTable -Table $script:CustomColumnsTable -Rules $rules
|
||||
$script:txtColumnDefaultsPath.Text = (Get-FullPathSafe -Path $Path)
|
||||
|
||||
Write-UILog -Message ("ColumnDefaults angewendet: {0} System Columns, {1} Custom Columns." -f $systemCount, $customCount)
|
||||
}
|
||||
|
||||
function New-ColumnDefaultRuleObject {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
$RowObject,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[bool]$UseWildcardContainer
|
||||
)
|
||||
|
||||
$containerSourceTitle = if ($UseWildcardContainer) { "*" } else { [string]$RowObject.ContainerSourceTitle }
|
||||
|
||||
return [PSCustomObject]@{
|
||||
ObjectType = [string]$RowObject.ObjectType
|
||||
ContainerSourceTitle = $containerSourceTitle
|
||||
SourceInternalName = [string]$RowObject.SourceInternalName
|
||||
SourceCanonicalInternalName = [string]$RowObject.SourceCanonicalInternalName
|
||||
TargetInternalName = [string]$RowObject.TargetInternalName
|
||||
ImportSupported = [bool]$RowObject.ImportSupported
|
||||
DisplayName = [string]$RowObject.DisplayName
|
||||
TypeAsString = [string]$RowObject.TypeAsString
|
||||
}
|
||||
}
|
||||
|
||||
function Get-ColumnDefaultRowsForExport {
|
||||
$rows = @(
|
||||
@(Convert-DataTableToObjects -Table $script:SystemColumnsTable -Schema $script:GridSchemas.SystemColumns) +
|
||||
@(Convert-DataTableToObjects -Table $script:CustomColumnsTable -Schema $script:GridSchemas.CustomColumns)
|
||||
)
|
||||
|
||||
return @($rows | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_.SourceInternalName) })
|
||||
}
|
||||
|
||||
function New-ColumnDefaultsTemplateFromUi {
|
||||
$rows = @(Get-ColumnDefaultRowsForExport)
|
||||
$groups = @{}
|
||||
|
||||
foreach ($row in $rows) {
|
||||
$key = ("{0}|{1}" -f ([string]$row.ObjectType).Trim(), ([string]$row.SourceInternalName).Trim()).ToLowerInvariant()
|
||||
if (-not $groups.ContainsKey($key)) {
|
||||
$groups[$key] = New-Object System.Collections.ArrayList
|
||||
}
|
||||
|
||||
[void]$groups[$key].Add($row)
|
||||
}
|
||||
|
||||
$rules = @()
|
||||
|
||||
foreach ($key in $groups.Keys) {
|
||||
$groupRows = @($groups[$key])
|
||||
$targetKeys = @(
|
||||
$groupRows | ForEach-Object {
|
||||
"{0}|{1}" -f ([string]$_.TargetInternalName).Trim(), ([bool]$_.ImportSupported)
|
||||
} | Sort-Object -Unique
|
||||
)
|
||||
|
||||
if ($targetKeys.Count -le 1) {
|
||||
$rules += New-ColumnDefaultRuleObject -RowObject $groupRows[0] -UseWildcardContainer $true
|
||||
}
|
||||
else {
|
||||
foreach ($row in $groupRows) {
|
||||
$rules += New-ColumnDefaultRuleObject -RowObject $row -UseWildcardContainer $false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [PSCustomObject]@{
|
||||
SchemaVersion = 1
|
||||
TemplateType = "ColumnMappings"
|
||||
GeneratedAtUtc = [System.DateTime]::UtcNow.ToString("o")
|
||||
Rules = @($rules | Sort-Object -Property ObjectType, ContainerSourceTitle, SourceInternalName)
|
||||
}
|
||||
}
|
||||
|
||||
function Save-ColumnDefaultsToPath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Path)) {
|
||||
throw "Bitte einen Pfad fuer ColumnDefaults angeben."
|
||||
}
|
||||
|
||||
$resolvedPath = Get-FullPathSafe -Path $Path
|
||||
Ensure-ParentDirectory -Path $resolvedPath
|
||||
$template = New-ColumnDefaultsTemplateFromUi
|
||||
$json = $template | ConvertTo-Json -Depth 10
|
||||
[System.IO.File]::WriteAllText($resolvedPath, $json, [System.Text.Encoding]::UTF8)
|
||||
$script:txtColumnDefaultsPath.Text = $resolvedPath
|
||||
Write-UILog -Message ("ColumnDefaults gespeichert: {0}" -f $resolvedPath)
|
||||
}
|
||||
|
||||
function Apply-ColumnDefaultsIfAvailable {
|
||||
$path = [string]$script:txtColumnDefaultsPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($path)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
Write-UILog -Message ("ColumnDefaults nicht gefunden und wurden nicht angewendet: {0}" -f $path) -Level "WARN"
|
||||
return
|
||||
}
|
||||
|
||||
Apply-ColumnDefaultsFromPath -Path $path
|
||||
}
|
||||
function Save-UiSettings {
|
||||
$script:SettingsManager.Set("WindowWidth", $script:MainForm.Width)
|
||||
$script:SettingsManager.Set("WindowHeight", $script:MainForm.Height)
|
||||
@@ -1654,6 +2152,7 @@ function Save-UiSettings {
|
||||
$script:SettingsManager.Set("LastTargetUrl", [string]$script:txtTargetUrl.Text)
|
||||
$script:SettingsManager.Set("LastOutputPath", [string]$script:txtOutputPath.Text)
|
||||
$script:SettingsManager.Set("LastMappingTablePath", [string]$script:txtMappingPath.Text)
|
||||
$script:SettingsManager.Set("LastColumnDefaultsPath", [string]$script:txtColumnDefaultsPath.Text)
|
||||
}
|
||||
|
||||
function Load-UiSettings {
|
||||
@@ -1661,6 +2160,7 @@ function Load-UiSettings {
|
||||
$script:txtTargetUrl.Text = [string]$script:SettingsManager.Get("LastTargetUrl", "")
|
||||
$script:txtOutputPath.Text = [string]$script:SettingsManager.Get("LastOutputPath", (Join-Path -Path (Get-Location).Path -ChildPath "SPMigrationOutput"))
|
||||
$script:txtMappingPath.Text = [string]$script:SettingsManager.Get("LastMappingTablePath", "")
|
||||
$script:txtColumnDefaultsPath.Text = [string]$script:SettingsManager.Get("LastColumnDefaultsPath", "")
|
||||
$script:MainForm.Width = [int]$script:SettingsManager.Get("WindowWidth", 1400)
|
||||
$script:MainForm.Height = [int]$script:SettingsManager.Get("WindowHeight", 940)
|
||||
Sync-MappingPathWithOutputPath -Force
|
||||
@@ -1692,7 +2192,6 @@ function Invoke-LoadContainers {
|
||||
}
|
||||
|
||||
$script:MainForm.UseWaitCursor = $true
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
|
||||
try {
|
||||
Write-UILog -Message ("Lese Listen und Bibliotheken aus: {0}" -f $sourceUrl)
|
||||
@@ -1706,7 +2205,6 @@ function Invoke-LoadContainers {
|
||||
}
|
||||
finally {
|
||||
$script:MainForm.UseWaitCursor = $false
|
||||
[System.Windows.Forms.Application]::DoEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1749,13 +2247,17 @@ function Invoke-ExportFromGui {
|
||||
$parameters.IncludeHiddenLists = $true
|
||||
}
|
||||
|
||||
Invoke-MigrationScript -Parameters $parameters -ActionLabel "Export"
|
||||
|
||||
$mappingPath = Join-Path -Path $outputPath -ChildPath "MappingTable.json"
|
||||
if (Test-Path -LiteralPath $mappingPath) {
|
||||
Load-MappingTableFromPath -Path $mappingPath
|
||||
$script:MainTabControl.SelectedTab = $script:tabMapping
|
||||
}
|
||||
$onCompleted = {
|
||||
if (Test-Path -LiteralPath $mappingPath) {
|
||||
Load-MappingTableFromPath -Path $mappingPath
|
||||
Apply-ColumnDefaultsIfAvailable
|
||||
Save-MappingTableToPath -Path $mappingPath
|
||||
$script:MainTabControl.SelectedTab = $script:tabMapping
|
||||
}
|
||||
}.GetNewClosure()
|
||||
|
||||
Invoke-MigrationScript -Parameters $parameters -ActionLabel "Export" -OnCompleted $onCompleted
|
||||
}
|
||||
catch {
|
||||
Write-UILog -Message $_.Exception.Message -Level "ERROR"
|
||||
@@ -1823,19 +2325,21 @@ $mainLayout = New-Object System.Windows.Forms.TableLayoutPanel
|
||||
$mainLayout.Dock = [System.Windows.Forms.DockStyle]::Fill
|
||||
$mainLayout.RowCount = 3
|
||||
$mainLayout.ColumnCount = 1
|
||||
[void]$mainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 110)))
|
||||
[void]$mainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 146)))
|
||||
[void]$mainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Percent, 100)))
|
||||
[void]$mainLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 230)))
|
||||
|
||||
$workspaceGroup = ([GroupBoxBuilder]::new("Arbeitsbereich")).Build()
|
||||
$workspaceLayout = New-Object System.Windows.Forms.TableLayoutPanel
|
||||
$workspaceLayout.Dock = [System.Windows.Forms.DockStyle]::Fill
|
||||
$workspaceLayout.RowCount = 2
|
||||
$workspaceLayout.ColumnCount = 4
|
||||
$workspaceLayout.RowCount = 3
|
||||
$workspaceLayout.ColumnCount = 5
|
||||
[void]$workspaceLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 105)))
|
||||
[void]$workspaceLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Percent, 100)))
|
||||
[void]$workspaceLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 110)))
|
||||
[void]$workspaceLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 110)))
|
||||
[void]$workspaceLayout.ColumnStyles.Add((New-Object System.Windows.Forms.ColumnStyle([System.Windows.Forms.SizeType]::Absolute, 110)))
|
||||
[void]$workspaceLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 36)))
|
||||
[void]$workspaceLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 36)))
|
||||
[void]$workspaceLayout.RowStyles.Add((New-Object System.Windows.Forms.RowStyle([System.Windows.Forms.SizeType]::Absolute, 36)))
|
||||
|
||||
@@ -1858,6 +2362,18 @@ $script:txtMappingPath.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
$btnLoadMapping = ([ButtonBuilder]::new("Laden...")).SetSize(95, 28).Build()
|
||||
$btnLoadMapping.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
|
||||
$lblColumnDefaultsPath = ([LabelBuilder]::new("ColumnDefaults")).SetTextAlign([System.Drawing.ContentAlignment]::MiddleLeft).Build()
|
||||
$lblColumnDefaultsPath.Dock = [System.Windows.Forms.DockStyle]::Fill
|
||||
$script:txtColumnDefaultsPath = New-Object System.Windows.Forms.TextBox
|
||||
$script:txtColumnDefaultsPath.Dock = [System.Windows.Forms.DockStyle]::Fill
|
||||
$script:txtColumnDefaultsPath.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
$btnApplyColumnDefaults = ([ButtonBuilder]::new("Anwenden")).SetSize(95, 28).Build()
|
||||
$btnApplyColumnDefaults.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
$btnLoadColumnDefaults = ([ButtonBuilder]::new("Laden...")).SetSize(95, 28).Build()
|
||||
$btnLoadColumnDefaults.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
$btnSaveColumnDefaults = ([ButtonBuilder]::new("Speichern")).SetSize(95, 28).Build()
|
||||
$btnSaveColumnDefaults.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
|
||||
[void]$workspaceLayout.Controls.Add($lblOutputPath, 0, 0)
|
||||
[void]$workspaceLayout.Controls.Add($script:txtOutputPath, 1, 0)
|
||||
[void]$workspaceLayout.Controls.Add($btnBrowseOutputPath, 2, 0)
|
||||
@@ -1865,6 +2381,11 @@ $btnLoadMapping.Margin = New-Object System.Windows.Forms.Padding(6)
|
||||
[void]$workspaceLayout.Controls.Add($lblMappingPath, 0, 1)
|
||||
[void]$workspaceLayout.Controls.Add($script:txtMappingPath, 1, 1)
|
||||
[void]$workspaceLayout.Controls.Add($btnLoadMapping, 2, 1)
|
||||
[void]$workspaceLayout.Controls.Add($lblColumnDefaultsPath, 0, 2)
|
||||
[void]$workspaceLayout.Controls.Add($script:txtColumnDefaultsPath, 1, 2)
|
||||
[void]$workspaceLayout.Controls.Add($btnApplyColumnDefaults, 2, 2)
|
||||
[void]$workspaceLayout.Controls.Add($btnLoadColumnDefaults, 3, 2)
|
||||
[void]$workspaceLayout.Controls.Add($btnSaveColumnDefaults, 4, 2)
|
||||
$workspaceGroup.Controls.Add($workspaceLayout)
|
||||
|
||||
$script:MainTabControl = ([TabControlBuilder]::new()).SetDock("Fill").Build()
|
||||
@@ -2139,6 +2660,58 @@ $btnLoadMapping.Add_Click({
|
||||
}
|
||||
})
|
||||
|
||||
$btnApplyColumnDefaults.Add_Click({
|
||||
try {
|
||||
Apply-ColumnDefaultsFromPath -Path ([string]$script:txtColumnDefaultsPath.Text)
|
||||
}
|
||||
catch {
|
||||
Write-UILog -Message $_.Exception.Message -Level "ERROR"
|
||||
Show-UiMessage -Message $_.Exception.Message -Caption "ColumnDefaults konnten nicht angewendet werden" -Icon ([System.Windows.Forms.MessageBoxIcon]::Error)
|
||||
}
|
||||
})
|
||||
|
||||
$btnLoadColumnDefaults.Add_Click({
|
||||
try {
|
||||
$dialog = [DialogBuilder]::OpenFile().SetTitle("ColumnDefaults laden").SetFilter("JSON (*.json)|*.json|Alle Dateien (*.*)|*.*").SetCheckFileExists($true)
|
||||
$initialDirectory = Get-ParentDirectorySafe -Path $script:txtColumnDefaultsPath.Text
|
||||
if ([string]::IsNullOrWhiteSpace($initialDirectory)) {
|
||||
$initialDirectory = Get-ParentDirectorySafe -Path $script:txtMappingPath.Text
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($initialDirectory)) {
|
||||
$initialDirectory = Get-ParentDirectorySafe -Path $script:txtOutputPath.Text
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($initialDirectory)) {
|
||||
$dialog.SetInitialDirectory($initialDirectory) | Out-Null
|
||||
}
|
||||
|
||||
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
||||
Apply-ColumnDefaultsFromPath -Path $dialog.GetPath()
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-UILog -Message $_.Exception.Message -Level "ERROR"
|
||||
Show-UiMessage -Message $_.Exception.Message -Caption "ColumnDefaults konnten nicht geladen werden" -Icon ([System.Windows.Forms.MessageBoxIcon]::Error)
|
||||
}
|
||||
})
|
||||
|
||||
$btnSaveColumnDefaults.Add_Click({
|
||||
try {
|
||||
$currentDefaultsPath = [string]$script:txtColumnDefaultsPath.Text
|
||||
$dialog = [DialogBuilder]::SaveFile().SetTitle("ColumnDefaults speichern").SetFilter("JSON (*.json)|*.json|Alle Dateien (*.*)|*.*").SetOverwritePrompt($true)
|
||||
if (-not [string]::IsNullOrWhiteSpace($currentDefaultsPath)) {
|
||||
$dialog.SetInitialDirectory((Get-ParentDirectorySafe -Path $currentDefaultsPath)) | Out-Null
|
||||
$dialog.SetFileName([System.IO.Path]::GetFileName($currentDefaultsPath)) | Out-Null
|
||||
}
|
||||
|
||||
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
|
||||
Save-ColumnDefaultsToPath -Path $dialog.GetPath()
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-UILog -Message $_.Exception.Message -Level "ERROR"
|
||||
Show-UiMessage -Message $_.Exception.Message -Caption "ColumnDefaults konnten nicht gespeichert werden" -Icon ([System.Windows.Forms.MessageBoxIcon]::Error)
|
||||
}
|
||||
})
|
||||
$script:tvContainers.Add_AfterCheck({
|
||||
param($sender, $e)
|
||||
|
||||
@@ -2185,6 +2758,14 @@ $script:MainForm.Add_Shown({
|
||||
})
|
||||
|
||||
$script:MainForm.Add_FormClosing({
|
||||
param($sender, $e)
|
||||
|
||||
if ($script:IsMigrationRunning) {
|
||||
$e.Cancel = $true
|
||||
Show-UiMessage -Message "Bitte warten, bis der laufende Export oder Import abgeschlossen ist." -Caption "Vorgang laeuft" -Icon ([System.Windows.Forms.MessageBoxIcon]::Warning)
|
||||
return
|
||||
}
|
||||
|
||||
Save-UiSettings
|
||||
})
|
||||
|
||||
|
||||
34
column-mapping-defaults.sample.json
Normal file
34
column-mapping-defaults.sample.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"SchemaVersion": 1,
|
||||
"TemplateType": "ColumnMappings",
|
||||
"GeneratedAtUtc": "2026-06-29T00:00:00.0000000Z",
|
||||
"Rules": [
|
||||
{
|
||||
"ObjectType": "*",
|
||||
"ContainerSourceTitle": "*",
|
||||
"SourceInternalName": "SecurityClearance",
|
||||
"TargetInternalName": "GMNSecurityClearance",
|
||||
"ImportSupported": true,
|
||||
"DisplayName": "Security Clearance",
|
||||
"TypeAsString": "TaxonomyFieldType"
|
||||
},
|
||||
{
|
||||
"ObjectType": "DocumentLibrary",
|
||||
"ContainerSourceTitle": "*",
|
||||
"SourceInternalName": "Department",
|
||||
"TargetInternalName": "GMNDepartment",
|
||||
"ImportSupported": true,
|
||||
"DisplayName": "Department",
|
||||
"TypeAsString": "Text"
|
||||
},
|
||||
{
|
||||
"ObjectType": "List",
|
||||
"ContainerSourceTitle": "Projektaufgaben",
|
||||
"SourceInternalName": "DueDate",
|
||||
"TargetInternalName": "DueDate",
|
||||
"ImportSupported": true,
|
||||
"DisplayName": "Faellig am",
|
||||
"TypeAsString": "DateTime"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user