refactor: move page meta to common meta module
This commit is contained in:
parent
88aae9e3a6
commit
496d70360b
6 changed files with 804 additions and 186 deletions
291
src/Attachment.psm1
Executable file
291
src/Attachment.psm1
Executable file
|
|
@ -0,0 +1,291 @@
|
||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
|
function New-Attachment
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
|
||||||
|
Add a new attachment
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
|
||||||
|
|
||||||
|
.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]$Name,
|
||||||
|
# attachments manifest
|
||||||
|
[Parameter(Mandatory, ValueFromPipeline)]
|
||||||
|
[PSCustomObject[]]$Manifest,
|
||||||
|
# attachments manifest index, mandatory for ancestor lookup
|
||||||
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||||
|
# pages manifest
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[PSCustomObject[]]$PagesManifest,
|
||||||
|
# pages manifest index, mandatory for container page lookup
|
||||||
|
[Parameter(Mandatory)] [Collections.Hashtable]$PagesIndex,
|
||||||
|
# flag on whether to fail hard, or just continue
|
||||||
|
[Parameter()] [Switch]$Strict
|
||||||
|
)
|
||||||
|
|
||||||
|
Begin
|
||||||
|
{
|
||||||
|
$pat = Get-PersonalAccessToken $Host
|
||||||
|
}
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
If ($Name -And $Manifest[$Index.$Name])
|
||||||
|
{
|
||||||
|
$Manifest = @(
|
||||||
|
$Manifest[$Index.$Name]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach($attachmentMeta in $Manifest)
|
||||||
|
{
|
||||||
|
If ($Name -And $attachmentMeta.Name -ne $Name) {continue}
|
||||||
|
|
||||||
|
|
||||||
|
$containerPageMeta = $PagesManifest[
|
||||||
|
$PagesIndex."$($attachmentMeta.ContainerPageTitle)"
|
||||||
|
]
|
||||||
|
|
||||||
|
If (-Not $containerPageMeta)
|
||||||
|
{
|
||||||
|
throw (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"unable to lookup metadata for container page " +
|
||||||
|
"title ``$($attachmentMeta.ContainerPageTitle)``." +
|
||||||
|
"This is fatal."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
If (-Not $containerPageMeta.Id)
|
||||||
|
{
|
||||||
|
$errMsg = (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"container page titled" +
|
||||||
|
"``$($attachmentMeta.ContainerPageTitle)`` " +
|
||||||
|
"has no id, which means that the page has " +
|
||||||
|
"(presumably) not yet been published."
|
||||||
|
)
|
||||||
|
|
||||||
|
If ($Strict) {throw $errMsg}
|
||||||
|
|
||||||
|
Write-Host "$errMsg Continuing nonetheless..."
|
||||||
|
|
||||||
|
$attachmentMeta
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ElseIf (-Not $attachmentMeta.Ref)
|
||||||
|
{
|
||||||
|
$errMsg = (
|
||||||
|
"``$($attachmentMeta.Name)``: no reference to local " +
|
||||||
|
'content for attachment .'
|
||||||
|
)
|
||||||
|
|
||||||
|
If ($Strict) {throw $errMsg}
|
||||||
|
|
||||||
|
Write-Host $errMsg
|
||||||
|
|
||||||
|
# not outputting the metadata, since it's invalid anyway
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ElseIf ($attachmentMeta.Id)
|
||||||
|
{
|
||||||
|
|
||||||
|
Write-Debug (
|
||||||
|
"New-Attachment: ``$($attacmentMeta.Name)``: skipping, " +
|
||||||
|
"already published ($($attachmentMeta.Id))"
|
||||||
|
)
|
||||||
|
|
||||||
|
$attachmentMeta
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
Else
|
||||||
|
{
|
||||||
|
Write-Host (
|
||||||
|
"New-Attachment: ``$($attachmentMeta.Name)``: creating"
|
||||||
|
)
|
||||||
|
|
||||||
|
Try
|
||||||
|
{
|
||||||
|
$rawContent = [IO.File]::ReadAllBytes($attachmentMeta.Ref)
|
||||||
|
|
||||||
|
$content = [Text.Encoding]::GetEncoding(
|
||||||
|
'ISO-8859-1'
|
||||||
|
).GetString($rawContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
Catch
|
||||||
|
{
|
||||||
|
$errMsg = "``New-Attachment: $($attachmentMeta.Name)``: $_"
|
||||||
|
|
||||||
|
If ($Strict) {throw $errMsg}
|
||||||
|
|
||||||
|
Write-Host $errMsg
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$boundary = [Guid]::NewGuid().ToString()
|
||||||
|
|
||||||
|
$LF = "`r`n";
|
||||||
|
|
||||||
|
$transportBody = (
|
||||||
|
"--$boundary",
|
||||||
|
(
|
||||||
|
"Content-Disposition: form-data; name=`"file`"; " +
|
||||||
|
"filename=`"$($attachmentMeta.Name)`""
|
||||||
|
),
|
||||||
|
"Content-Type: $($attachmentMeta.MimeType)$LF",
|
||||||
|
$content,
|
||||||
|
"--$boundary--$LF"
|
||||||
|
) -join $LF
|
||||||
|
|
||||||
|
$uri = (
|
||||||
|
"https://${Host}/rest/api/content/" +
|
||||||
|
"$($containerPageMeta.Id)/child/attachment"
|
||||||
|
)
|
||||||
|
|
||||||
|
Try
|
||||||
|
{
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri $uri `
|
||||||
|
-Method 'Post' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $pat"
|
||||||
|
'X-Atlassian-Token' = 'nocheck'
|
||||||
|
} `
|
||||||
|
-ContentType (
|
||||||
|
"multipart/form-data; boundary=`"$boundary`""
|
||||||
|
) `
|
||||||
|
-Body $transportBody `
|
||||||
|
-OutVariable rawResponse | Out-Null
|
||||||
|
}
|
||||||
|
|
||||||
|
Catch
|
||||||
|
{
|
||||||
|
$errMsg = "skipping ``$($attachmentMeta.Name)``: $($_)"
|
||||||
|
|
||||||
|
If ($Strict)
|
||||||
|
{
|
||||||
|
$_
|
||||||
|
|
||||||
|
throw $errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host $errMsg
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = ($rawResponse.Content | ConvertFrom-JSON)
|
||||||
|
|
||||||
|
$attachmentMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Id' `
|
||||||
|
-NotePropertyValue $response.id `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
$attachmentMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Version' `
|
||||||
|
-NotePropertyValue (
|
||||||
|
$response.version.number
|
||||||
|
) `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
$contentHash = (Get-StringHash $content).Hash
|
||||||
|
|
||||||
|
$attachmentMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Hash' `
|
||||||
|
-NotePropertyValue $contentHash `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
If (
|
||||||
|
($Title -And $attachmentMeta.Title -eq $Name) -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
|
||||||
|
,@($attachmentMeta)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
Else
|
||||||
|
{
|
||||||
|
$attachmentMeta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Publish-Attachment
|
||||||
|
{
|
||||||
|
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]$Name,
|
||||||
|
# attachments manifest
|
||||||
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
|
[PSCustomObject[]]$Manifest,
|
||||||
|
# attachments manifest index, mandatory for ancestor lookup
|
||||||
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||||
|
# pages manifest
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[PSCustomObject[]]$PagesManifest,
|
||||||
|
# pages manifest index, mandatory for container page lookup
|
||||||
|
[Parameter(Mandatory)] [Collections.Hashtable]$PagesIndex,
|
||||||
|
# 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 = New-Attachment `
|
||||||
|
-Host $Host `
|
||||||
|
-Space $Space `
|
||||||
|
-Manifest $Manifest `
|
||||||
|
-Index $Index `
|
||||||
|
-PagesManifest $PagesManifest `
|
||||||
|
-PagesIndex $PagesIndex `
|
||||||
|
-Strict:$Strict
|
||||||
|
}
|
||||||
|
|
||||||
|
End
|
||||||
|
{
|
||||||
|
$result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
340
src/Meta.psm1
Executable file
340
src/Meta.psm1
Executable file
|
|
@ -0,0 +1,340 @@
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
|
function Get-PageMeta
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get a Confluence page id
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
First, tries to retrieve from local page id index (cache) through
|
||||||
|
the local alias. If no cache hit, then polls the Confluence
|
||||||
|
instance host for the id by providing a space key and page title.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-PageMeta `
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-CacheIndexFile 'confluence-page-cache.json'
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
# Confluence instance hostname
|
||||||
|
[Parameter(Mandatory)] [string]$Host,
|
||||||
|
# Page title
|
||||||
|
[Parameter()] [string]$Title,
|
||||||
|
# Confluence space id
|
||||||
|
[Parameter(Mandatory)] [string]$Space,
|
||||||
|
# pages manifest
|
||||||
|
[Parameter(Mandatory, ValueFromPipeline)] [Array]$Manifest,
|
||||||
|
# page metadata index for faster lookup of single page
|
||||||
|
[Parameter()] [Collections.Hashtable]$Index,
|
||||||
|
# force to get metadata from remote
|
||||||
|
[Parameter()] [Switch]$Force,
|
||||||
|
# throw an exception on error
|
||||||
|
[Parameter()] [Switch]$Strict
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
If ($Title -And $Index -And $Manifest[$Index.$Title].Id)
|
||||||
|
{
|
||||||
|
$Manifest[$Index.$Title]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach ($pageMeta in $Manifest)
|
||||||
|
{
|
||||||
|
If ($Title -And $pageMeta.Title -ne $Title) {continue}
|
||||||
|
|
||||||
|
If ($pageMeta.Id -And -Not $Force)
|
||||||
|
{
|
||||||
|
Write-Debug (
|
||||||
|
"Get-PageMeta: ``$($pageMeta.Title)``: " +
|
||||||
|
"using locally cached metadata ($($pageMeta.Id))"
|
||||||
|
)
|
||||||
|
|
||||||
|
$pageMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
Else
|
||||||
|
{
|
||||||
|
$escapedTitle = [Uri]::EscapeDataString($pageMeta.Title)
|
||||||
|
|
||||||
|
$query = (
|
||||||
|
"title=${escapedTitle}&spaceKey=${Space}&expand=version"
|
||||||
|
)
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri "https://${Host}/rest/api/content?$query" `
|
||||||
|
-Method 'Get' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = 'Bearer ' +
|
||||||
|
$(Get-PersonalAccessToken $Host)
|
||||||
|
} `
|
||||||
|
-OutVariable response | Out-Null
|
||||||
|
|
||||||
|
$results = ($response.Content | ConvertFrom-JSON).results
|
||||||
|
|
||||||
|
If ($results.Count -gt 1)
|
||||||
|
{
|
||||||
|
$errMsg = "error: more than one result for query: $query"
|
||||||
|
|
||||||
|
If ($Strict) {throw $errMsg}
|
||||||
|
|
||||||
|
Write-Host $errMsg
|
||||||
|
|
||||||
|
$pageMeta
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ElseIf ($results.Count -eq 1)
|
||||||
|
{
|
||||||
|
Write-Debug (
|
||||||
|
"Get-PageMetadata: ``$($pageMeta.Title)``: " +
|
||||||
|
"updating metadata through remote ($($results[0].id))"
|
||||||
|
)
|
||||||
|
|
||||||
|
$pageMeta | Add-Member `
|
||||||
|
-NotePropertyName Id `
|
||||||
|
-NotePropertyValue $results[0].id `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
$pageMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Version' `
|
||||||
|
-NotePropertyValue `
|
||||||
|
$results[0].version.number `
|
||||||
|
-Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Else
|
||||||
|
{
|
||||||
|
Write-Debug (
|
||||||
|
"Get-PageMetadata: ``$($pageMeta.Title)``: " +
|
||||||
|
"no remote, using (partial) local"
|
||||||
|
)
|
||||||
|
|
||||||
|
If ($pageMeta.Version)
|
||||||
|
{
|
||||||
|
$pageMeta.PSObject.Properties.Remove('Version')
|
||||||
|
}
|
||||||
|
|
||||||
|
If ($pageMeta.Id)
|
||||||
|
{
|
||||||
|
$pageMeta.PSObject.Properties.Remove('Id')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
If (-Not $pageMeta.Hash)
|
||||||
|
{
|
||||||
|
$content = Get-Content $pageMeta.Ref | Out-String
|
||||||
|
|
||||||
|
$hash = (Get-StringHash $content).Hash
|
||||||
|
|
||||||
|
$pageMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Hash' `
|
||||||
|
-NotePropertyValue $hash `
|
||||||
|
-Force
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageMeta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-AttachmentMeta
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Get a Confluence page id
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
First, tries to retrieve from local page id index (cache) through
|
||||||
|
the local alias. If no cache hit, then polls the Confluence
|
||||||
|
instance host for the id by providing a space key and page title.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
Get-PageMeta `
|
||||||
|
-Host 'confluence.contoso.com' `
|
||||||
|
-Title 'Testitest' `
|
||||||
|
-Space 'TIARA' `
|
||||||
|
-CacheIndexFile 'confluence-page-cache.json'
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
# Confluence instance hostname
|
||||||
|
[Parameter(Mandatory)] [string]$Host,
|
||||||
|
# Confluence space id
|
||||||
|
[Parameter(Mandatory)] [string]$Space,
|
||||||
|
# Attachment name
|
||||||
|
[Parameter()] [string]$Name,
|
||||||
|
# attachments manifest
|
||||||
|
[Parameter(Mandatory, ValueFromPipeline)] [Array]$Manifest,
|
||||||
|
# page metadata index for faster lookup of single page
|
||||||
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||||
|
# pages manifest
|
||||||
|
[Parameter(Mandatory)] [Array]$PagesManifest,
|
||||||
|
# page metadata index for faster lookup of single page
|
||||||
|
[Parameter()] [Collections.Hashtable]$PagesIndex,
|
||||||
|
# force to get metadata from remote
|
||||||
|
[Parameter()] [Switch]$Force,
|
||||||
|
# throw an exception on error
|
||||||
|
[Parameter()] [Switch]$Strict
|
||||||
|
)
|
||||||
|
|
||||||
|
Begin
|
||||||
|
{
|
||||||
|
$pat = Get-PersonalAccessToken $Host
|
||||||
|
}
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
If ($Name -And $Index -And $Manifest[$Index.$Name].Id)
|
||||||
|
{
|
||||||
|
$Manifest[$Index.$Name]
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach ($attachmentMeta in $Manifest)
|
||||||
|
{
|
||||||
|
If ($Name -And $attachmentMeta.Name -ne $Name) {continue}
|
||||||
|
|
||||||
|
$containerPageMeta = $PagesManifest[
|
||||||
|
$PagesIndex."$($attachmentMeta.ContainerPageTitle)"
|
||||||
|
]
|
||||||
|
|
||||||
|
If (-Not $containerPageMeta)
|
||||||
|
{
|
||||||
|
throw (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"unable to lookup metadata for container page " +
|
||||||
|
"title ``$($attachmentMeta.ContainerPageTitle)``." +
|
||||||
|
"This is fatal."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
If (-Not $containerPageMeta.Id)
|
||||||
|
{
|
||||||
|
$errMsg = (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"container page titled" +
|
||||||
|
"``$($attachmentMeta.ContainerPageTitle)`` " +
|
||||||
|
"has no id, which means that the page has " +
|
||||||
|
"(presumably) not yet been published."
|
||||||
|
)
|
||||||
|
|
||||||
|
If ($Strict) {throw $errMsg}
|
||||||
|
|
||||||
|
Write-Host "$errMsg Continuing nonetheless..."
|
||||||
|
|
||||||
|
$attachmentMeta
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
If ($attachmentMeta.Id -And -Not $Force)
|
||||||
|
{
|
||||||
|
Write-Debug (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"using locally cached metadata ($($attachmentMeta.Id))"
|
||||||
|
)
|
||||||
|
|
||||||
|
$attachmentMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
Else
|
||||||
|
{
|
||||||
|
$escapedName = [Uri]::EscapeDataString($attachmentMeta.Name)
|
||||||
|
|
||||||
|
$query = "filename=${escapedName}&expand=version"
|
||||||
|
|
||||||
|
$uri = (
|
||||||
|
"https://${Host}/rest/api/content/" +
|
||||||
|
"$($containerPageMeta.Id)/child/attachment?$query"
|
||||||
|
)
|
||||||
|
|
||||||
|
Invoke-WebRequest `
|
||||||
|
-Uri $uri `
|
||||||
|
-Method 'Get' `
|
||||||
|
-Headers @{
|
||||||
|
'Authorization' = "Bearer $pat"
|
||||||
|
} `
|
||||||
|
-OutVariable response | Out-Null
|
||||||
|
|
||||||
|
$results = ($response.Content | ConvertFrom-JSON).results
|
||||||
|
|
||||||
|
If ($results.Count -gt 1)
|
||||||
|
{
|
||||||
|
$errMsg = (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"error: more than one result for query: $query"
|
||||||
|
)
|
||||||
|
|
||||||
|
If ($Strict) {throw $errMsg}
|
||||||
|
|
||||||
|
Write-Host $errMsg
|
||||||
|
|
||||||
|
$attachmentMeta
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ElseIf ($results.Count -eq 1)
|
||||||
|
{
|
||||||
|
Write-Debug (
|
||||||
|
"Get-AttachmentMeta: ``$($attachmentMeta.Name)``: " +
|
||||||
|
"updating metadata through remote ($($results[0].id))"
|
||||||
|
)
|
||||||
|
|
||||||
|
$attachmentMeta | Add-Member `
|
||||||
|
-NotePropertyName Id `
|
||||||
|
-NotePropertyValue $results[0].id `
|
||||||
|
-Force
|
||||||
|
|
||||||
|
$attachmentMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Version' `
|
||||||
|
-NotePropertyValue `
|
||||||
|
$results[0].version.number `
|
||||||
|
-Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Else
|
||||||
|
{
|
||||||
|
Write-Debug (
|
||||||
|
"Get-AttachmentMetadata: ``$($attachmentMeta.Name)``" +
|
||||||
|
": no remote, using (partial) local"
|
||||||
|
)
|
||||||
|
|
||||||
|
If ($attachmentMeta.Version)
|
||||||
|
{
|
||||||
|
$attachmentMeta.PSObject.Properties.Remove('Version')
|
||||||
|
}
|
||||||
|
|
||||||
|
If ($attachmentMeta.Id)
|
||||||
|
{
|
||||||
|
$attachmentMeta.PSObject.Properties.Remove('Id')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
If (-Not $attachmentMeta.Hash)
|
||||||
|
{
|
||||||
|
$content = Get-Content $attachmentMeta.Ref | Out-String
|
||||||
|
|
||||||
|
$hash = (Get-StringHash $content).Hash
|
||||||
|
|
||||||
|
$attachmentMeta | Add-Member `
|
||||||
|
-NotePropertyName 'Hash' `
|
||||||
|
-NotePropertyValue $hash `
|
||||||
|
-Force
|
||||||
|
}
|
||||||
|
|
||||||
|
$attachmentMeta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -64,7 +64,8 @@ Description = 'External Confluence publisher for xconfluencebuilder'
|
||||||
'Connection.psm1',
|
'Connection.psm1',
|
||||||
'Manifest.psm1',
|
'Manifest.psm1',
|
||||||
'Page.psm1',
|
'Page.psm1',
|
||||||
'PageMeta.psm1',
|
'Meta.psm1',
|
||||||
|
'Attachment.psm1',
|
||||||
'StringHelper.psm1'
|
'StringHelper.psm1'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,17 @@
|
||||||
#!/usr/bin/env pwsh
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
PowerShell Publisher for sphinxcontrib.confluencebuilder
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
|
|
||||||
- support for ancestral pages and containered attachments
|
|
||||||
- creates new pages if they don't exist
|
|
||||||
- updates existing pages and attachments if checksum mismatches
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
|
|
||||||
Import-Module (Join-Path 'vendor' 'tiara.rodney'
|
|
||||||
'PSConfluencePublisher'
|
|
||||||
'PSConfluencePublisher'
|
|
||||||
'PSConfluencePublisher.psd1')
|
|
||||||
|
|
||||||
Register-PersonalAccessToken `
|
|
||||||
-Host 'confluence.contoso.com' `
|
|
||||||
-Token '123456789123456789'
|
|
||||||
|
|
||||||
Test-Connection confluence.contoso.com
|
|
||||||
|
|
||||||
Publish-All `
|
|
||||||
-Url 'https://confluence.contoso.com/display/TIARA/Testitest' `
|
|
||||||
-DumpIndex build/docs/confluence.out/data.json
|
|
||||||
|
|
||||||
.NOTES
|
|
||||||
- tested with PowerShell Core (PSVersion 7.3.6)
|
|
||||||
- tested with PowerShell Desktop (PSVersion 5.1.19041.3031)
|
|
||||||
#>
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
|
||||||
function Initialize-Manifest
|
function Initialize-Manifest
|
||||||
{
|
{
|
||||||
<#
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Initialize a manifest (in-memory)
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This function initializes a manifest by loading a serialized
|
||||||
|
manifest from the filesystem, generating indexes and sorting the
|
||||||
|
pages manifest, so that the ancestral relation defines the order in
|
||||||
|
which pages will be published.
|
||||||
#>
|
#>
|
||||||
Param(
|
Param(
|
||||||
# path of manifest to load
|
# path of manifest to load
|
||||||
|
|
@ -50,27 +25,40 @@ function Initialize-Manifest
|
||||||
|
|
||||||
Process
|
Process
|
||||||
{
|
{
|
||||||
Write-Debug 'loading manifest...'
|
Write-Debug 'Initialize-Manifest: loading manifest...'
|
||||||
|
|
||||||
$manifest = Get-Manifest $literalPath
|
$manifest = Get-Manifest $literalPath
|
||||||
|
|
||||||
Write-Debug 'creating pages manifest index...'
|
Write-Debug 'Initialize-Manifest: creating pages manifest index...'
|
||||||
|
|
||||||
$pagesManifestIndex = New-PagesManifestIndex -Manifest $manifest.Pages
|
$pagesManifestIndex = New-PagesManifestIndex -Manifest $manifest.Pages
|
||||||
|
|
||||||
Write-Debug 'creating ancestral page generation cache...'
|
Write-Debug (
|
||||||
|
'Initialize-Manifest: creating ancestral page generation cache...'
|
||||||
|
)
|
||||||
|
|
||||||
$ancestralGenerationCache = New-AncestralPageGenerationCache `
|
$ancestralGenerationCache = New-AncestralPageGenerationCache `
|
||||||
-Manifest $manifest.Pages `
|
-Manifest $manifest.Pages `
|
||||||
-Index $pagesManifestIndex
|
-Index $pagesManifestIndex
|
||||||
|
|
||||||
Write-Debug 'sorting pages manifest...'
|
Write-Debug 'Initialize-Manifest: sorting pages manifest...'
|
||||||
|
|
||||||
Optimize-PagesManifest `
|
Optimize-PagesManifest `
|
||||||
-Manifest $manifest.Pages `
|
-Manifest $manifest.Pages `
|
||||||
-Lo 0 `
|
-Lo 0 `
|
||||||
-Hi ($manifest.Pages.Count - 1) `
|
-Hi ($manifest.Pages.Count - 1) `
|
||||||
-GenerationCache $ancestralGenerationCache | Out-Null
|
-GenerationCache $ancestralGenerationCache | Out-Null
|
||||||
|
|
||||||
|
Write-Debug 'Initialize-Manifest: recreating pages manifest index...'
|
||||||
|
|
||||||
|
$pagesManifestIndex = New-PagesManifestIndex -Manifest $manifest.Pages
|
||||||
|
|
||||||
|
Write-Debug (
|
||||||
|
'Initialize-Manifest: creating attachments manifest index...'
|
||||||
|
)
|
||||||
|
|
||||||
|
$attachmentsManifestIndex = New-AttachmentsManifestIndex `
|
||||||
|
-Manifest $manifest.Attachments
|
||||||
}
|
}
|
||||||
|
|
||||||
End
|
End
|
||||||
|
|
@ -79,10 +67,8 @@ function Initialize-Manifest
|
||||||
'Path' = $literalPath
|
'Path' = $literalPath
|
||||||
'Manifest' = $manifest
|
'Manifest' = $manifest
|
||||||
'Index' = @{
|
'Index' = @{
|
||||||
'Pages' = New-PagesManifestIndex `
|
'Pages' = $pagesManifestIndex
|
||||||
-Manifest $manifest.Pages
|
'Attachments' = $attachmentsManifestIndex
|
||||||
'Attachments' = New-AttachmentsManifestIndex `
|
|
||||||
-Manifest $manifest.Attachments
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,9 +77,25 @@ function Initialize-Manifest
|
||||||
|
|
||||||
function Initialize-Connection
|
function Initialize-Connection
|
||||||
{
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
initialize a connection to a Confluence instance
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This function registers a Personal Access Token (locally) and checks
|
||||||
|
connectivity to a Confluence instance. It also verifies, that the
|
||||||
|
Personal Access Tokens authenticates.
|
||||||
|
|
||||||
|
.NOTES
|
||||||
|
TODO: extend verification to also verify that write access to the
|
||||||
|
provided space is granted.
|
||||||
|
#>
|
||||||
Param(
|
Param(
|
||||||
|
# hostname (or IP address) of Confluence instance
|
||||||
[Parameter(Mandatory)] [String]$Host,
|
[Parameter(Mandatory)] [String]$Host,
|
||||||
|
# id of Confluence space
|
||||||
[Parameter(Mandatory)] [String]$Space,
|
[Parameter(Mandatory)] [String]$Space,
|
||||||
|
# personal access token
|
||||||
[Parameter(Mandatory)] [String]$PersonalAccessToken
|
[Parameter(Mandatory)] [String]$PersonalAccessToken
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -118,6 +120,23 @@ function Initialize-Connection
|
||||||
|
|
||||||
function Publish-Pages
|
function Publish-Pages
|
||||||
{
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Publish pages to Confluence instance
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This function publishes all (or one) pages as defined in the pages
|
||||||
|
manifest.
|
||||||
|
|
||||||
|
Since pipelining is supported within the low-level functions, this
|
||||||
|
function is basically just a wrapper.
|
||||||
|
|
||||||
|
.NOTE
|
||||||
|
TODO: Investigate on how we can pass-through the manifest as to
|
||||||
|
retain pipeline functionality throughout. Currently it is broken,
|
||||||
|
since the manifest isn't passed as a pieline input object from the
|
||||||
|
top (which is this function).
|
||||||
|
#>
|
||||||
Param(
|
Param(
|
||||||
# connection object created through Initialize-Connection
|
# connection object created through Initialize-Connection
|
||||||
[Parameter(Mandatory)] [Collections.Hashtable]$Connection,
|
[Parameter(Mandatory)] [Collections.Hashtable]$Connection,
|
||||||
|
|
@ -133,11 +152,54 @@ function Publish-Pages
|
||||||
|
|
||||||
Process
|
Process
|
||||||
{
|
{
|
||||||
$Manifest.Manifest.Pages | Publish-Page `
|
Publish-Page `
|
||||||
-Host $Connection.Host `
|
-Host $Connection.Host `
|
||||||
-Space $Connection.Space `
|
-Space $Connection.Space `
|
||||||
-Title $Title `
|
-Title $Title `
|
||||||
-Index $Manifest.Index.Pages `
|
-Index $Manifest.Index.Pages `
|
||||||
|
-Manifest $Manifest.Manifest.Pages `
|
||||||
|
-Strict:$Strict `
|
||||||
|
-Force:$Force | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Publish-Attachments
|
||||||
|
{
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Publish attachments to Confluence instance
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
This function publishes all (or one) attachments as defined in the
|
||||||
|
attachments manifest.
|
||||||
|
|
||||||
|
Since pipelining is supported within the low-level functions, this
|
||||||
|
function is basically just a wrapper.
|
||||||
|
#>
|
||||||
|
Param(
|
||||||
|
# connection object created through Initialize-Connection
|
||||||
|
[Parameter(Mandatory)] [Collections.Hashtable]$Connection,
|
||||||
|
# manifest object created through Initialize-Manifest
|
||||||
|
[Parameter(Mandatory)] [PSCustomObject]$Manifest,
|
||||||
|
#
|
||||||
|
[Parameter()] [Switch]$Strict,
|
||||||
|
#
|
||||||
|
[Parameter()] [Switch]$Force,
|
||||||
|
# name of attachment to be published
|
||||||
|
[Parameter()] [String]$Name
|
||||||
|
)
|
||||||
|
|
||||||
|
Process
|
||||||
|
{
|
||||||
|
Publish-Attachment `
|
||||||
|
-Host $Connection.Host `
|
||||||
|
-Space $Connection.Space `
|
||||||
|
-Name $Name `
|
||||||
|
-Manifest $Manifest.Manifest.Attachments `
|
||||||
|
-Index $Manifest.Index.Attachments `
|
||||||
|
-PagesManifest $Manifest.Manifest.Pages `
|
||||||
|
-PagesIndex $Manifest.Index.Pages `
|
||||||
-Strict:$Strict `
|
-Strict:$Strict `
|
||||||
-Force:$Force | Out-Null
|
-Force:$Force | Out-Null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ function New-Page
|
||||||
# title of page to be published
|
# title of page to be published
|
||||||
[Parameter()] [string]$Title,
|
[Parameter()] [string]$Title,
|
||||||
# pages manifest
|
# pages manifest
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory,ValueFromPipeline)]
|
||||||
[PSCustomObject[]]$Manifest,
|
[PSCustomObject[]]$Manifest,
|
||||||
# pages manifest index, mandatory for ancestor lookup
|
# pages manifest index, mandatory for ancestor lookup
|
||||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||||
|
|
@ -123,7 +123,34 @@ function New-Page
|
||||||
'representation' = 'storage'
|
'representation' = 'storage'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} | ConvertTo-JSON -Depth 5
|
}
|
||||||
|
|
||||||
|
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
|
Try
|
||||||
{
|
{
|
||||||
|
|
@ -134,7 +161,7 @@ function New-Page
|
||||||
'Authorization' = "Bearer $pat"
|
'Authorization' = "Bearer $pat"
|
||||||
} `
|
} `
|
||||||
-ContentType "application/json" `
|
-ContentType "application/json" `
|
||||||
-Body $transportBody `
|
-Body $rawTransportBody `
|
||||||
-OutVariable rawResponse | Out-Null
|
-OutVariable rawResponse | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,6 +304,8 @@ function Update-Page
|
||||||
|
|
||||||
Write-Host $errMsg
|
Write-Host $errMsg
|
||||||
|
|
||||||
|
$pageMeta
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,7 +380,34 @@ function Update-Page
|
||||||
'version' = @{
|
'version' = @{
|
||||||
'number' = $version
|
'number' = $version
|
||||||
}
|
}
|
||||||
} | ConvertTo-JSON -WarningAction 'SilentlyContinue'
|
}
|
||||||
|
|
||||||
|
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
|
Try
|
||||||
{
|
{
|
||||||
|
|
@ -363,7 +419,7 @@ function Update-Page
|
||||||
'Authorization' = "Bearer $pat"
|
'Authorization' = "Bearer $pat"
|
||||||
} `
|
} `
|
||||||
-ContentType "application/json" `
|
-ContentType "application/json" `
|
||||||
-Body $transportBody `
|
-Body $rawTransportBody `
|
||||||
-OutVariable rawResponse | Out-Null
|
-OutVariable rawResponse | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,7 +486,7 @@ function Publish-Page
|
||||||
# title of page to be published
|
# title of page to be published
|
||||||
[Parameter()] [string]$Title,
|
[Parameter()] [string]$Title,
|
||||||
# pages manifest
|
# pages manifest
|
||||||
[Parameter(Mandatory, ValueFromPipeline)]
|
[Parameter(Mandatory)]
|
||||||
[PSCustomObject[]]$Manifest,
|
[PSCustomObject[]]$Manifest,
|
||||||
# pages manifest index, mandatory for ancestor lookup
|
# pages manifest index, mandatory for ancestor lookup
|
||||||
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
[Parameter(Mandatory)] [Collections.Hashtable]$Index,
|
||||||
|
|
|
||||||
|
|
@ -1,132 +0,0 @@
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
|
|
||||||
function Get-PageMeta
|
|
||||||
{
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Get a Confluence page id
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
First, tries to retrieve from local page id index (cache) through
|
|
||||||
the local alias. If no cache hit, then polls the Confluence
|
|
||||||
instance host for the id by providing a space key and page title.
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
Get-PageMeta `
|
|
||||||
-Host 'confluence.contoso.com' `
|
|
||||||
-Title 'Testitest' `
|
|
||||||
-Space 'TIARA' `
|
|
||||||
-CacheIndexFile 'confluence-page-cache.json'
|
|
||||||
#>
|
|
||||||
Param(
|
|
||||||
# Confluence instance hostname
|
|
||||||
[Parameter(Mandatory)] [string] $Host,
|
|
||||||
# Page title
|
|
||||||
[Parameter()] [string] $Title,
|
|
||||||
# Confluence space id
|
|
||||||
[Parameter(Mandatory)] [string] $Space,
|
|
||||||
# pages manifest
|
|
||||||
[Parameter(Mandatory, ValueFromPipeline)] [Array] $Manifest,
|
|
||||||
# page metadata index for faster lookup of single page
|
|
||||||
[Parameter()] [Collections.Hashtable] $Index,
|
|
||||||
# force to get metadata from remote
|
|
||||||
[Parameter()] [Switch] $Force = $false,
|
|
||||||
# throw an exception on error
|
|
||||||
[Parameter()] [Switch] $Strict = $true
|
|
||||||
)
|
|
||||||
|
|
||||||
Process
|
|
||||||
{
|
|
||||||
If ($Title -And $Index -And $Manifest[$Index.$Title].Id)
|
|
||||||
{
|
|
||||||
$Manifest[$Index.$Title]
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ForEach ($pageMeta in $Manifest)
|
|
||||||
{
|
|
||||||
If ($Title -And $pageMeta.Title -ne $Title) {continue}
|
|
||||||
|
|
||||||
If ($pageMeta.Id -And -Not $Force)
|
|
||||||
{
|
|
||||||
Write-Debug "local (cache): $($pageMeta.Title) ($($pageMeta.Id))"
|
|
||||||
|
|
||||||
$pageMeta
|
|
||||||
}
|
|
||||||
|
|
||||||
Else
|
|
||||||
{
|
|
||||||
$escapedTitle = [Uri]::EscapeDataString($pageMeta.Title)
|
|
||||||
|
|
||||||
$query = (
|
|
||||||
"title=${escapedTitle}&spaceKey=${Space}&expand=version"
|
|
||||||
)
|
|
||||||
|
|
||||||
Invoke-WebRequest `
|
|
||||||
-Uri "https://${Host}/rest/api/content?$query" `
|
|
||||||
-Method 'Get' `
|
|
||||||
-Headers @{
|
|
||||||
'Authorization' = 'Bearer ' +
|
|
||||||
$(Get-PersonalAccessToken $Host)
|
|
||||||
} `
|
|
||||||
-OutVariable response | Out-Null
|
|
||||||
|
|
||||||
$results = ($response.Content | ConvertFrom-JSON).results
|
|
||||||
|
|
||||||
If ($results.Count -gt 1)
|
|
||||||
{
|
|
||||||
$errMsg = "error: more than one result for query: $query"
|
|
||||||
|
|
||||||
If ($Strict) {throw $errMsg}
|
|
||||||
|
|
||||||
Write-Host $errMsg
|
|
||||||
|
|
||||||
$pageMeta
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ElseIf ($results.Count -eq 1)
|
|
||||||
{
|
|
||||||
Write-Debug (
|
|
||||||
"Get-PageMetadata: ``$($pageMeta.Title)``: " +
|
|
||||||
"updating metadata through remote ($($results[0].id))"
|
|
||||||
)
|
|
||||||
|
|
||||||
$pageMeta | Add-Member `
|
|
||||||
-NotePropertyName Id `
|
|
||||||
-NotePropertyValue $results[0].id `
|
|
||||||
-Force
|
|
||||||
|
|
||||||
$pageMeta | Add-Member `
|
|
||||||
-NotePropertyName 'Version' `
|
|
||||||
-NotePropertyValue `
|
|
||||||
$results[0].version.number `
|
|
||||||
-Force
|
|
||||||
}
|
|
||||||
|
|
||||||
Else
|
|
||||||
{
|
|
||||||
Write-Debug "local: $($pageMeta.Title) (no remote)"
|
|
||||||
}
|
|
||||||
|
|
||||||
If (-Not $pageMeta.Hash)
|
|
||||||
{
|
|
||||||
$content = Get-Content $pageMeta.Ref | Out-String
|
|
||||||
|
|
||||||
$hash = (Get-StringHash $content).Hash
|
|
||||||
|
|
||||||
$pageMeta | Add-Member `
|
|
||||||
-NotePropertyName 'Hash' `
|
|
||||||
-NotePropertyValue $hash `
|
|
||||||
-Force
|
|
||||||
}
|
|
||||||
|
|
||||||
$pageMeta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue