521 lines
15 KiB
PowerShell
Executable file
521 lines
15 KiB
PowerShell
Executable file
#!/usr/bin/env pwsh
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
|
|
function New-Page
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Add a confluence page
|
|
|
|
.DESCRIPTION
|
|
|
|
This function is unaware of the publishing status of ancestors and
|
|
assumes that ancestral hierarchy is maintained through the
|
|
manifest's item order.
|
|
|
|
If a page's metadata does not include a reference, it will be
|
|
treated as a publishing failure and therefore not output the
|
|
original metadata.
|
|
|
|
.OUTPUTS
|
|
|
|
When no $Title is provided and the $Manifest array only contains 1
|
|
page metadata, the ``Count`` attribute is faulty. Why? Don't know.
|
|
|
|
.EXAMPLE
|
|
Add-ConfluencePage `
|
|
-Host 'confluence.contoso.com' `
|
|
-Space 'TIARA' `
|
|
-Title 'Testitest' `
|
|
-Content @{}
|
|
#>
|
|
Param(
|
|
# confluence instance hostname
|
|
[Parameter(Mandatory)] [string]$Host,
|
|
# name of the Confluence space to publish to
|
|
[Parameter(Mandatory)] [string]$Space,
|
|
# title of page to be published
|
|
[Parameter()] [string]$Title,
|
|
# pages manifest
|
|
[Parameter(Mandatory,ValueFromPipeline)]
|
|
[PSCustomObject[]]$Manifest,
|
|
# pages manifest index, mandatory for ancestor lookup
|
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
|
# flag on whether to fail hard, or just continue
|
|
[Parameter()] [Switch]$Strict
|
|
)
|
|
|
|
Begin
|
|
{
|
|
$pat = Get-PersonalAccessToken $Host
|
|
}
|
|
|
|
Process
|
|
{
|
|
If ($Title -And $Manifest[$Index.$Title])
|
|
{
|
|
$Manifest = @(
|
|
$Manifest[$Index.$Title]
|
|
)
|
|
}
|
|
|
|
ForEach($pageMeta in $Manifest)
|
|
{
|
|
If ($Title -And $pageMeta.Title -ne $Title) {continue}
|
|
|
|
ElseIf (-Not $pageMeta.Ref)
|
|
{
|
|
$errMsg = ("``$($pageMeta.Title)``: no reference to local " +
|
|
'content for page .')
|
|
|
|
If ($Strict) {throw $errMsg}
|
|
|
|
Write-Host $errMsg
|
|
|
|
continue
|
|
}
|
|
|
|
ElseIf ($pageMeta.Id)
|
|
{
|
|
|
|
Write-Debug (
|
|
"New-Page: ``$($pageMeta.Title)``: skipping, already " +
|
|
"published ($($pageMeta.Id))"
|
|
)
|
|
|
|
$pageMeta
|
|
|
|
continue
|
|
}
|
|
|
|
Else
|
|
{
|
|
Write-Host "New-Page: ``$($pageMeta.Title)``: creating"
|
|
|
|
Try
|
|
{
|
|
$content = Get-Content -Path $pageMeta.Ref | Out-String
|
|
}
|
|
|
|
Catch
|
|
{
|
|
$errMsg = "``New-Page: $($PageMeta.Title)``: $_"
|
|
|
|
If ($Strict) {throw $errMsg}
|
|
|
|
Write-Host $errMsg
|
|
|
|
continue
|
|
}
|
|
|
|
$contentHash = (Get-StringHash $content).Hash
|
|
|
|
$transportBody = @{
|
|
'type' = 'page'
|
|
'title' = $pageMeta.Title
|
|
'space' = @{
|
|
'key' = $Space
|
|
}
|
|
'body' = @{
|
|
'storage' = @{
|
|
'value' = $content
|
|
'representation' = 'storage'
|
|
}
|
|
}
|
|
}
|
|
|
|
If ($pageMeta.AncestorTitle)
|
|
{
|
|
$ancestorPageMeta = $Manifest[
|
|
$Index."$($pageMeta.AncestorTitle)"
|
|
]
|
|
|
|
If (-Not $ancestorPageMeta)
|
|
{
|
|
Throw (
|
|
"ancestor (``$($ancestorPageMeta.Title)``) of " +
|
|
"``$($pageMeta.Title)`` does not have an id. " +
|
|
"This indicates, that the ancestor has not been " +
|
|
"published and therefore the pages manifest may " +
|
|
"not be in the correct order."
|
|
)
|
|
}
|
|
|
|
$transportBody.ancestors = @(
|
|
@{'id' = $ancestorPageMeta.Id}
|
|
)
|
|
}
|
|
|
|
$rawTransportBody = (
|
|
$transportBody | ConvertTo-JSON `
|
|
-WarningAction 'SilentlyContinue'
|
|
)
|
|
|
|
Try
|
|
{
|
|
Invoke-WebRequest `
|
|
-Uri "https://${Host}/rest/api/content" `
|
|
-Method 'Post' `
|
|
-Headers @{
|
|
'Authorization' = "Bearer $pat"
|
|
} `
|
|
-ContentType "application/json" `
|
|
-Body $rawTransportBody `
|
|
-OutVariable rawResponse | Out-Null
|
|
}
|
|
|
|
Catch
|
|
{
|
|
$errMsg = "skipping ``$($pageMeta.Title)``: $($_)"
|
|
|
|
If ($Strict)
|
|
{
|
|
$_
|
|
|
|
throw $errMsg
|
|
}
|
|
|
|
Write-Host $errMsg
|
|
|
|
continue
|
|
}
|
|
|
|
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
|
|
|
$pageMeta | Add-Member `
|
|
-NotePropertyName 'Id' `
|
|
-NotePropertyValue $response.id `
|
|
-Force
|
|
|
|
$pageMeta | Add-Member `
|
|
-NotePropertyName 'Version' `
|
|
-NotePropertyValue "1" `
|
|
-Force
|
|
|
|
$pageMeta | Add-Member `
|
|
-NotePropertyName 'Hash' `
|
|
-NotePropertyValue $contentHash `
|
|
-Force
|
|
|
|
If (
|
|
($Title -And $pageMeta.Title -eq $Title) -Or
|
|
$Manifest.Count -eq 1
|
|
)
|
|
{
|
|
# TODO: further research mechanism of expanding single item
|
|
# array pipelines. For now we have to apply the unary
|
|
# operator, otherwise we get a wrong count on the output
|
|
,@($pageMeta)
|
|
|
|
break
|
|
}
|
|
|
|
Else
|
|
{
|
|
$pageMeta
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function Update-Page
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Update an existing Confluence page
|
|
|
|
.DESCRIPTION
|
|
|
|
This function is unaware of the publishing status of ancestors and
|
|
assumes that ancestral hierarchy is maintained through the
|
|
manifest's item order, therefore an index must be supplied.
|
|
|
|
If a page's metadata does not include a reference, it will be
|
|
treated as a publishing failure and therefore not output the
|
|
original metadata.
|
|
|
|
.OUTPUTS
|
|
|
|
When no $Title is provided and the $Manifest array only contains 1
|
|
page metadata, the ``Count`` attribute is faulty. Why? Don't know.
|
|
|
|
.EXAMPLE
|
|
Add-ConfluencePage `
|
|
-Host 'confluence.contoso.com' `
|
|
-Space 'TIARA' `
|
|
-Title 'Testitest' `
|
|
-Content @{}
|
|
#>
|
|
Param(
|
|
# confluence instance hostname
|
|
[Parameter(Mandatory)] [string]$Host,
|
|
# name of the Confluence space to publish to
|
|
[Parameter(Mandatory)] [string]$Space,
|
|
# title of page to be published
|
|
[Parameter()] [string]$Title,
|
|
# pages manifest
|
|
[Parameter(Mandatory, ValueFromPipeline)]
|
|
[PSCustomObject[]]$Manifest,
|
|
# pages manifest index, mandatory for ancestor lookup
|
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
|
# flag on whether to fail hard, or just continue
|
|
[Parameter()] [Switch]$Strict,
|
|
# flag on whether to force update of page, regardless of content
|
|
[Parameter()] [Switch]$Force
|
|
)
|
|
|
|
Begin
|
|
{
|
|
$pat = Get-PersonalAccessToken $Host
|
|
}
|
|
|
|
Process
|
|
{
|
|
If ($Title -And $Manifest[$Index.$Title])
|
|
{
|
|
$Manifest = @(
|
|
$Manifest[$Index.$Title]
|
|
)
|
|
}
|
|
|
|
ForEach($pageMeta in $Manifest)
|
|
{
|
|
If ($Title -And $pageMeta.Title -ne $Title) {continue}
|
|
|
|
ElseIf (-Not $pageMeta.Ref)
|
|
{
|
|
$errMsg = "no reference to local content for page ``$Title``."
|
|
|
|
If ($Strict) {throw $errMsg}
|
|
|
|
Write-Host $errMsg
|
|
|
|
continue
|
|
}
|
|
|
|
ElseIf (-Not $pageMeta.Id)
|
|
{
|
|
$errMsg = "``$($pageMeta.Title)``: unknown page id"
|
|
|
|
If ($Strict) {throw $errMsg}
|
|
|
|
Write-Host $errMsg
|
|
|
|
$pageMeta
|
|
|
|
continue
|
|
}
|
|
|
|
ElseIf (-Not $pageMeta.Version)
|
|
{
|
|
Write-Host "``$($pageMeta.Title)``: unknown (current) version"
|
|
|
|
$pageMeta
|
|
|
|
continue
|
|
}
|
|
|
|
Else
|
|
{
|
|
Try
|
|
{
|
|
$content = Get-Content -Path $pageMeta.Ref | Out-String
|
|
}
|
|
|
|
Catch
|
|
{
|
|
$errMsg = "``$Title``: $_"
|
|
|
|
If ($Strict) {throw $errMsg}
|
|
|
|
Write-Host $errMsg
|
|
|
|
continue
|
|
}
|
|
|
|
$version = [Int]($pageMeta.Version) + 1
|
|
|
|
$contentHash = (Get-StringHash $content).Hash
|
|
|
|
If (
|
|
$pageMeta.Hash -And
|
|
$pageMeta.Hash -eq $contentHash -And
|
|
-Not $Force
|
|
)
|
|
{
|
|
Write-Debug (
|
|
"Update-Page: ``$($pageMeta.Title)``: skipping, no " +
|
|
"content changes"
|
|
)
|
|
|
|
$pageMeta
|
|
|
|
continue
|
|
}
|
|
|
|
Else
|
|
{
|
|
Write-Host "``$($pageMeta.Title)``: updating"
|
|
}
|
|
|
|
# status needs to be set as to restore the page, if it is
|
|
# trashed
|
|
$transportBody = @{
|
|
'id' = $PageMeta.Id
|
|
'type' = 'page'
|
|
'title' = $pageMeta.Title
|
|
'space' = @{
|
|
'key' = $Space
|
|
}
|
|
'body' = @{
|
|
'storage' = @{
|
|
'value' = $content
|
|
'representation' = 'storage'
|
|
}
|
|
}
|
|
'status' = 'current'
|
|
'version' = @{
|
|
'number' = $version
|
|
}
|
|
}
|
|
|
|
If ($pageMeta.AncestorTitle)
|
|
{
|
|
$ancestorPageMeta = $Manifest[
|
|
$Index."$($pageMeta.AncestorTitle)"
|
|
]
|
|
|
|
If (-Not $ancestorPageMeta)
|
|
{
|
|
Throw (
|
|
"ancestor (``$($ancestorPageMeta.Title)``) of " +
|
|
"``$($pageMeta.Title)`` does not have an id. " +
|
|
"This indicates, that the ancestor has not been " +
|
|
"published and therefore the pages manifest may " +
|
|
"not be in the correct order."
|
|
)
|
|
}
|
|
|
|
$transportBody.ancestors = @(
|
|
@{'id' = $ancestorPageMeta.Id}
|
|
)
|
|
}
|
|
|
|
$rawTransportBody = (
|
|
$transportBody | ConvertTo-JSON `
|
|
-WarningAction 'SilentlyContinue'
|
|
)
|
|
|
|
Try
|
|
{
|
|
Invoke-WebRequest `
|
|
-Uri ("https://${Host}/rest/api/content/" +
|
|
$PageMeta.Id) `
|
|
-Method 'Put' `
|
|
-Headers @{
|
|
'Authorization' = "Bearer $pat"
|
|
} `
|
|
-ContentType "application/json" `
|
|
-Body $rawTransportBody `
|
|
-OutVariable rawResponse | Out-Null
|
|
}
|
|
|
|
Catch
|
|
{
|
|
$errMsg = "skipping ``$($pageMeta.Title)``: $_"
|
|
|
|
If ($Strict)
|
|
{
|
|
$_
|
|
|
|
throw $errMsg
|
|
}
|
|
|
|
Write-Host $errMsg
|
|
|
|
continue
|
|
}
|
|
|
|
# response isn't needed since no field will be updated by the
|
|
# Confluence instance itself
|
|
#$response = ($rawResponse.Content | ConvertFrom-JSON)
|
|
|
|
$pageMeta | Add-Member `
|
|
-NotePropertyName 'Version' `
|
|
-NotePropertyValue $version `
|
|
-Force
|
|
|
|
$pageMeta | Add-Member `
|
|
-NotePropertyName 'Hash' `
|
|
-NotePropertyValue $contentHash `
|
|
-Force
|
|
|
|
If (
|
|
($Title -And $pageMeta.Title -eq $Title) -Or
|
|
$Manifest.Count -eq 1
|
|
)
|
|
{
|
|
# TODO: further research mechanism of expanding single item
|
|
# array pipelines. For now we have to apply the unary
|
|
# operator, otherwise we get a wrong count on the output
|
|
,@($pageMeta)
|
|
|
|
break
|
|
}
|
|
|
|
Else
|
|
{
|
|
$pageMeta
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function Publish-Page
|
|
{
|
|
Param(
|
|
# confluence instance hostname
|
|
[Parameter(Mandatory)] [string]$Host,
|
|
# name of the Confluence space to publish to
|
|
[Parameter(Mandatory)] [string]$Space,
|
|
# title of page to be published
|
|
[Parameter()] [string]$Title,
|
|
# pages manifest
|
|
[Parameter(Mandatory)]
|
|
[PSCustomObject[]]$Manifest,
|
|
# pages manifest index, mandatory for ancestor lookup
|
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
|
# flag on whether to fail hard, or just continue
|
|
[Parameter()] [Switch]$Strict,
|
|
# flag on whether to force update of page, regardless of content
|
|
[Parameter()] [Switch]$Force
|
|
)
|
|
|
|
Process
|
|
{
|
|
$result = Update-Page `
|
|
-Host $Host `
|
|
-Space $Space `
|
|
-Manifest $Manifest `
|
|
-Index $Index `
|
|
-Strict:$Strict `
|
|
-Force:$Force
|
|
|
|
$result = New-Page `
|
|
-Host $Host `
|
|
-Space $Space `
|
|
-Manifest $result `
|
|
-Index $Index `
|
|
-Strict:$Strict
|
|
}
|
|
|
|
End
|
|
{
|
|
$result
|
|
}
|
|
}
|