Public/Add-ConfluenceAttachment.ps1
|
function Add-ConfluenceAttachment { <# .SYNOPSIS Uploads files as attachments to a Confluence page. .DESCRIPTION Add-ConfluenceAttachment uploads one or more files to a page through the Confluence v1 API (PUT /wiki/rest/api/content/<id>/child/attachment, multipart/form-data with the X-Atlassian-Token: no-check header); the v2 API has no upload endpoint. When the page already has an attachment with the same file name, a new version of that attachment is created. Returns the created or updated attachments. Supports -WhatIf and -Confirm. .PARAMETER PageId The ID of the page to attach the files to. .PARAMETER Path The files to upload. Accepts pipeline input (for example from Get-ChildItem). .PARAMETER Comment A comment stored with each attachment version. .PARAMETER NotifyWatchers Notify the page watchers. By default the upload is a minor edit and watchers are not notified. .EXAMPLE Add-ConfluenceAttachment -PageId 123456 -Path ./report.pdf -Comment 'Nightly report' Attaches report.pdf to page 123456, or adds a new version of it. .EXAMPLE Get-ChildItem ./out/*.png | Add-ConfluenceAttachment -PageId 123456 Attaches every PNG file in ./out. .OUTPUTS System.Management.Automation.PSCustomObject. One object per attachment. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] [OutputType([pscustomobject])] param ( [Parameter(Mandatory, HelpMessage = 'The ID of the page.')] [string]$PageId, [Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName, HelpMessage = 'The files to upload.')] [Alias('FullName')] [string[]]$Path, [Parameter(HelpMessage = 'A comment for the attachment version.')] [string]$Comment, [Parameter(HelpMessage = 'Notify the page watchers.')] [switch]$NotifyWatchers ) begin { $TelemetryArgs = @{ ModuleName = $MyInvocation.MyCommand.Module.Name ModuleVersion = [string]$MyInvocation.MyCommand.Module.Version CommandName = $MyInvocation.MyCommand.Name ExecutionID = [guid]::NewGuid().ToString() } Invoke-TelemetryCollection @TelemetryArgs -Stage Start -ClearTimer $telemetryFailed = $false } process { try { if ($null -eq $script:ConfluenceContext -or $null -eq $script:ConfluenceCredential) { Write-Error 'No Confluence context is set. Run Set-ConfluenceContext first.' return } $uri = '{0}/wiki/rest/api/content/{1}/child/attachment' -f $script:ConfluenceContext.ConnectionBaseURL, [System.Uri]::EscapeDataString($PageId) foreach ($item in $Path) { $file = Get-Item -LiteralPath $item -ErrorAction SilentlyContinue if (-not $file -or $file.PSIsContainer) { Write-Error "File not found: $item" continue } if (-not $PSCmdlet.ShouldProcess("Confluence page $PageId", "Attach '$($file.Name)'")) { continue } $multipart = New-ConfluenceMultipartBody -FileName $file.Name -FileBytes ([System.IO.File]::ReadAllBytes($file.FullName)) -Comment $Comment -MinorEdit (-not $NotifyWatchers) try { $response = Invoke-ConfluenceHttpRequest -Uri $uri -Method PUT -BodyBytes $multipart.Bytes -ContentType $multipart.ContentType -AdditionalHeaders @{ 'X-Atlassian-Token' = 'no-check' } -ErrorAction Stop } catch { Write-Error "Failed to upload '$($file.Name)' to page $PageId. $($_.Exception.Message)" continue } if ($response.StatusCode -lt 200 -or $response.StatusCode -gt 299) { $errorText = $null try { $errorContent = $response.Content | ConvertFrom-Json -ErrorAction Stop $errorText = (@($errorContent.errors | ForEach-Object { $_.title }) + @($errorContent.message) | Where-Object { $_ }) -join '; ' } catch { Write-Verbose 'The error response body is not JSON.' } Write-Error "Failed to upload '$($file.Name)' to page $PageId. Status: $($response.StatusCode) $($response.StatusDescription) Errors: $errorText" continue } $payload = $response.Content | ConvertFrom-Json if (@($payload.PSObject.Properties.Name) -contains 'results') { foreach ($attachment in @($payload.results)) { $attachment } } else { $payload } } } catch { if (-not $telemetryFailed) { $telemetryFailed = $true Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $_ } throw } } end { if (-not $telemetryFailed) { Invoke-TelemetryCollection @TelemetryArgs -Stage End } } } |