<# .SYNOPSIS Confluence Space Export fuer pathOS / Bestellsystem .DESCRIPTION Exportiert die Seitenstruktur und Inhalte aus dem Confluence Space BES. Kompatibel mit PowerShell 5.1 - keine Sonderzeichen. #> $ConfluenceUrl = "https://arija-confluence.jaas.service.deutschebahn.com" $SpaceKey = "BES" $RootPageId = "196749641" $ApiBase = "$ConfluenceUrl/rest/api" $PerPage = 50 $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $OutputDir = Join-Path (Split-Path -Parent $ScriptDir) "data\confluence-export" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Write-Host "============================================================" Write-Host " pathOS / Bestellsystem - Confluence Export" Write-Host "============================================================" Write-Host " Confluence: $ConfluenceUrl" Write-Host " Space: $SpaceKey" Write-Host "" Write-Host "Authentifizierung:" Write-Host " Option 1: Personal Access Token (PAT) - empfohlen" Write-Host " Option 2: Benutzername + Passwort" Write-Host "" $AuthMethod = Read-Host "PAT oder Passwort? (pat/pw)" if ($AuthMethod -eq "pw") { $Username = Read-Host "Benutzername" $Password = Read-Host "Passwort" $Pair = "${Username}:${Password}" $Bytes = [System.Text.Encoding]::ASCII.GetBytes($Pair) $Base64 = [System.Convert]::ToBase64String($Bytes) $Headers = @{ "Authorization" = "Basic $Base64" } } else { $PAT = Read-Host "Personal Access Token" $Headers = @{ "Authorization" = "Bearer $PAT" } } function Safe-String($val) { if ($null -eq $val) { return "" } return [string]$val } function Invoke-ConfluenceApi { param([string]$Endpoint, [hashtable]$Params = @{}) $QS = ($Params.GetEnumerator() | ForEach-Object { "$($_.Key)=$([Uri]::EscapeDataString($_.Value))" }) -join "&" $Uri = "$ApiBase$Endpoint" if ($QS) { $Uri = "$Uri`?$QS" } try { $Resp = Invoke-WebRequest -Uri $Uri -Headers $Headers -UseBasicParsing -ErrorAction Stop return ($Resp.Content | ConvertFrom-Json) } catch { Write-Host " FEHLER bei $Uri : $($_.Exception.Message)" -ForegroundColor Red return $null } } function Get-ChildPages { param([string]$PageId) $AllChildren = @() $Start = 0 do { $Result = Invoke-ConfluenceApi -Endpoint "/content/$PageId/child/page" -Params @{ start = "$Start" limit = "$PerPage" expand = "version" } if ($null -eq $Result) { break } $Pages = $Result.results if ($null -eq $Pages -or $Pages.Count -eq 0) { break } $AllChildren += $Pages $Start += $PerPage $Size = 0 if ($Result.size) { $Size = $Result.size } } while ($Size -ge $PerPage) return $AllChildren } function Get-PageContent { param([string]$PageId) return Invoke-ConfluenceApi -Endpoint "/content/$PageId" -Params @{ expand = "body.storage,version,ancestors,metadata.labels,space" } } function Convert-HtmlToText($html) { if ($null -eq $html) { return "" } $text = $html -replace '', "`n" $text = $text -replace ']*>', "`n" $text = $text -replace '

', "" $text = $text -replace ']*>', "`n- " $text = $text -replace ']*>', "`n## " $text = $text -replace '', "`n" $text = $text -replace ']*>', "`n| " $text = $text -replace ']*>', " | " $text = $text -replace ']*>', " | " $text = $text -replace '<[^>]+>', '' Add-Type -AssemblyName System.Web -ErrorAction SilentlyContinue try { $text = [System.Web.HttpUtility]::HtmlDecode($text) } catch {} return $text.Trim() } function Export-PageTree { param( [string]$PageId, [string]$ParentPath, [int]$Depth = 0, [ref]$Counter ) $Indent = " " * $Depth $Counter.Value++ $Num = $Counter.Value $Page = Get-PageContent -PageId $PageId if ($null -eq $Page) { return $null } $Title = Safe-String $Page.title Write-Host "$Indent[$Num] $Title" $Labels = @() if ($Page.metadata -and $Page.metadata.labels -and $Page.metadata.labels.results) { $Labels = @($Page.metadata.labels.results | ForEach-Object { $_.name }) } $VersionNum = 0 if ($Page.version) { $VersionNum = $Page.version.number } $PageInfo = [PSCustomObject]@{ id = $Page.id title = $Title version = $VersionNum labels = $Labels depth = $Depth path = "$ParentPath/$Title" children = @() } # Inhalt speichern $HtmlContent = "" $TextContent = "" if ($Page.body -and $Page.body.storage) { $HtmlContent = Safe-String $Page.body.storage.value $TextContent = Convert-HtmlToText $HtmlContent } $SafeTitle = $Title -replace '[\\/:*?"<>|]', '_' if ($SafeTitle.Length -gt 80) { $SafeTitle = $SafeTitle.Substring(0, 80) } $PageDir = Join-Path $OutputDir "pages" # Markdown $MdLines = @() $MdLines += "# $Title" $MdLines += "" $MdLines += "> Confluence Page ID: $($Page.id)" $MdLines += "> Version: $VersionNum" $MdLines += "> Pfad: $($PageInfo.path)" $MdLines += "> Labels: $(($Labels) -join ', ')" $MdLines += "" $MdLines += "---" $MdLines += "" $MdLines += $TextContent $MdPath = Join-Path $PageDir "$($Page.id)_$SafeTitle.md" $MdLines -join "`n" | Out-File -FilePath $MdPath -Encoding UTF8 # HTML $HtmlPath = Join-Path $PageDir "$($Page.id)_$SafeTitle.html" $HtmlContent | Out-File -FilePath $HtmlPath -Encoding UTF8 # Kindseiten $Children = @(Get-ChildPages -PageId $PageId) $ChildInfos = @() foreach ($Child in $Children) { $ChildInfo = Export-PageTree -PageId $Child.id -ParentPath $PageInfo.path -Depth ($Depth + 1) -Counter $Counter if ($null -ne $ChildInfo) { $ChildInfos += $ChildInfo } } $PageInfo.children = $ChildInfos return $PageInfo } function Write-TreeLines { param($Node, [int]$IndentLevel = 0, [System.Collections.ArrayList]$Lines) $Spaces = " " * $IndentLevel $null = $Lines.Add("${Spaces}+-- $($Node.title) (ID: $($Node.id), v$($Node.version))") foreach ($Child in $Node.children) { Write-TreeLines -Node $Child -IndentLevel ($IndentLevel + 1) -Lines $Lines } } # === MAIN === New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null New-Item -ItemType Directory -Path "$OutputDir\pages" -Force | Out-Null Add-Type -AssemblyName System.Web -ErrorAction SilentlyContinue try { Write-Host "" Write-Host ">> Teste Verbindung..." -ForegroundColor Yellow $TestResult = Invoke-ConfluenceApi -Endpoint "/space/$SpaceKey" if ($null -eq $TestResult) { Write-Host " Verbindung fehlgeschlagen. Token/Credentials pruefen." -ForegroundColor Red exit 1 } Write-Host " Space gefunden: $($TestResult.name) ($SpaceKey)" -ForegroundColor Green Write-Host "" Write-Host ">> Exportiere Seitenbaum ab Seite $RootPageId..." -ForegroundColor Yellow $Counter = [ref]0 $Tree = Export-PageTree -PageId $RootPageId -ParentPath "" -Depth 0 -Counter $Counter Write-Host "" Write-Host ">> $($Counter.Value) Seiten exportiert" -ForegroundColor Green # Baumstruktur $TreeLines = [System.Collections.ArrayList]::new() Write-TreeLines -Node $Tree -Lines $TreeLines $TreePath = Join-Path $OutputDir "page-tree.md" $TreeOut = @() $TreeOut += "# Confluence Seitenbaum - pathOS ($SpaceKey)" $TreeOut += "" $TreeOut += "> Exportiert: $(Get-Date -Format 'yyyy-MM-dd HH:mm')" $TreeOut += "> Seiten: $($Counter.Value)" $TreeOut += "" $TreeOut += $TreeLines $TreeOut -join "`n" | Out-File -FilePath $TreePath -Encoding UTF8 Write-Host " Baumstruktur: $TreePath" # JSON-Index $JsonPath = Join-Path $OutputDir "page-index.json" $Tree | ConvertTo-Json -Depth 20 | Out-File -FilePath $JsonPath -Encoding UTF8 Write-Host " JSON-Index: $JsonPath" Write-Host "" Write-Host "============================================================" -ForegroundColor Green Write-Host " FERTIG! $($Counter.Value) Seiten exportiert." -ForegroundColor Green Write-Host " Ergebnisse: $OutputDir" -ForegroundColor Green Write-Host "============================================================" -ForegroundColor Green } catch { Write-Host "" Write-Host "FEHLER: $($_.Exception.Message)" -ForegroundColor Red if ($_.Exception.Message -match "401|403") { Write-Host " Authentifizierung fehlgeschlagen." -ForegroundColor Yellow } if ($_.Exception.Message -match "resolve|Remotename") { Write-Host " Verbindung fehlgeschlagen. VPN aktiv?" -ForegroundColor Yellow } exit 1 }