<# .SYNOPSIS Shared helper functions for UpNote → Ka-Note bundle imports. Dot-source this file in import scripts: . "$PSScriptRoot\import-helpers.ps1" .NOTES Bundle file formats (as of current schema): contexts.json — array of AgendaContext: id, name, type (meeting|project|person|company), sortOrder, updatedAt, deletedAt, version, archivedAt, isFavorite, meta topics.json — array of Topic: id, contextId, title, body, sortOrder, isPinned, deletedAt, updatedAt, version history/.meta.json — HistoryEntry (without text): id, topicId, date (YYYY-MM-DD), sortOrder, linkedContextId, doneAt, wiedervorlageDate, wiedervorlageResolvedAt, updatedAt, deletedAt, version history/.md — plain text / markdown content IMPORTANT: - History is TOPIC-scoped (topicId), NOT context-scoped. - History date is "YYYY-MM-DD" (no time part). - History has no "title" field. - Soft-deleted contexts cause 500 on upload if new topics reference them → always call Upsert-Context which auto-resurrects deleted contexts. #> # Globals set by Initialize-BundleSession $script:WorkDir = $null $script:Now = $null function Initialize-BundleSession { param([string]$WorkDir) $script:WorkDir = $WorkDir $script:Now = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") if (-not (Test-Path "$WorkDir\history")) { New-Item -ItemType Directory "$WorkDir\history" | Out-Null } } # Upsert a context: creates new OR resurrects if soft-deleted OR skips if active. # Always bumps version and sets name/type/sortOrder if changed. function Upsert-Context { param( [string]$CtxPath, [string]$Id, [string]$Name, [string]$Type = 'meeting', [int] $SortOrder = 10, [bool] $IsFavorite = $true ) $ctxs = Get-Content $CtxPath -Raw -Encoding UTF8 | ConvertFrom-Json $existing = $ctxs | Where-Object { $_.id -eq $Id } if ($existing) { $wasDeleted = $null -ne $existing.deletedAt $existing | Add-Member -NotePropertyName deletedAt -NotePropertyValue $null -Force $existing | Add-Member -NotePropertyName name -NotePropertyValue $Name -Force $existing | Add-Member -NotePropertyName updatedAt -NotePropertyValue $script:Now -Force $existing.version++ if ($wasDeleted) { Write-Host " [CTX] Resurrected: $Id" -ForegroundColor Green } else { Write-Host " [CTX] Already active, updated: $Id" -ForegroundColor DarkGray } } else { $new = [PSCustomObject]@{ id = $Id name = $Name type = $Type sortOrder = $SortOrder updatedAt = $script:Now deletedAt = $null version = 1 archivedAt = $null isFavorite = $IsFavorite meta = $null } $ctxs = @($ctxs) + $new Write-Host " [CTX] Created: $Id ($Name)" -ForegroundColor Green } $ctxs | ConvertTo-Json -Depth 10 | Set-Content $CtxPath -Encoding UTF8 } # Soft-delete all topics for a context (cleanup orphans or old data) function Remove-ContextTopics { param([string]$TopicsPath, [string]$ContextId) $tops = Get-Content $TopicsPath -Raw -Encoding UTF8 | ConvertFrom-Json $count = 0 foreach ($t in $tops) { if ($t.contextId -eq $ContextId -and -not $t.deletedAt) { $t | Add-Member -NotePropertyName deletedAt -NotePropertyValue $script:Now -Force $t | Add-Member -NotePropertyName updatedAt -NotePropertyValue $script:Now -Force $t.version++ $count++ } } $tops | ConvertTo-Json -Depth 10 | Set-Content $TopicsPath -Encoding UTF8 if ($count -gt 0) { Write-Host " [TOP] Soft-deleted $count orphan topics for $ContextId" -ForegroundColor DarkGray } } # Add a new topic. Returns the topic ID. function Add-Topic { param( [string]$TopicsPath, [string]$ContextId, [string]$Title, [string]$Body = '', [int] $SortOrder = 0, [bool] $IsPinned = $false ) $id = [System.Guid]::NewGuid().ToString() $tops = Get-Content $TopicsPath -Raw -Encoding UTF8 | ConvertFrom-Json $new = [PSCustomObject]@{ id = $id contextId = $ContextId title = $Title body = $Body sortOrder = $SortOrder isPinned = $IsPinned deletedAt = $null updatedAt = $script:Now version = 1 } $tops = @($tops) + $new $tops | ConvertTo-Json -Depth 10 | Set-Content $TopicsPath -Encoding UTF8 Write-Host " [TOP] + $Title" -ForegroundColor DarkGray return $id } # Add a history entry (attached to a topic). # $Date: 'YYYY-MM-DD' # $Text: markdown string function Add-HistoryEntry { param( [string]$TopicId, [string]$Date, [string]$Text, [int] $SortOrder = 0 ) $id = [System.Guid]::NewGuid().ToString() $meta = [PSCustomObject]@{ id = $id topicId = $TopicId date = $Date sortOrder = $SortOrder linkedContextId = $null doneAt = $null wiedervorlageDate = $null wiedervorlageResolvedAt = $null updatedAt = $script:Now deletedAt = $null version = 1 } $meta | ConvertTo-Json -Depth 5 | Set-Content "$($script:WorkDir)\history\$id.meta.json" -Encoding UTF8 $Text | Set-Content "$($script:WorkDir)\history\$id.md" -Encoding UTF8 Write-Host " [HIS] + $Date" -ForegroundColor DarkGray return $id }