161 lines
5.3 KiB
PowerShell
161 lines
5.3 KiB
PowerShell
<#
|
|
.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/<id>.meta.json — HistoryEntry (without text):
|
|
id, topicId, date (YYYY-MM-DD), sortOrder, linkedContextId,
|
|
doneAt, wiedervorlageDate, wiedervorlageResolvedAt,
|
|
updatedAt, deletedAt, version
|
|
history/<id>.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
|
|
}
|