Public/Remove-DuplicateConfluencePage.ps1
|
function Remove-DuplicateConfluencePage { <# .SYNOPSIS Removes duplicate Confluence pages that share a title and parent, keeping one. .DESCRIPTION Remove-DuplicateConfluencePage asks Confluence for every page in the space with exactly the given title (all result pages are read), keeps those under the given parent and, when there is more than one, deletes all but one. By default the newest page (latest creation date, createdAt) is kept; use -KeepNewest:$false to keep the oldest. The version number is not used, because an old page that was edited often has a higher version than a newer copy. Every deletion asks for confirmation unless you pass -Confirm:$false; -WhatIf shows what would be deleted. The kept page is returned. Called as Remove-DuplicateConfluencePages (the name used before 0.1.0) it still works through an alias. .PARAMETER SpaceKey The key or numeric ID of the space to search. .PARAMETER ParentId The ID of the parent page of the duplicates. .PARAMETER PageTitle The exact title of the duplicated page. .PARAMETER KeepNewest Keep the most recently created page (default). Use -KeepNewest:$false to keep the oldest page instead. .EXAMPLE Remove-DuplicateConfluencePage -SpaceKey DOCS -ParentId 1000 -PageTitle 'Weekly report' -WhatIf Shows which duplicate "Weekly report" pages under page 1000 would be deleted. .EXAMPLE Remove-DuplicateConfluencePage -SpaceKey DOCS -ParentId 1000 -PageTitle 'Weekly report' -KeepNewest:$false -Confirm:$false Deletes all but the oldest duplicate without prompting. .OUTPUTS System.Management.Automation.PSCustomObject. The page that was kept. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] [Alias('Remove-DuplicateConfluencePages')] [OutputType([pscustomobject])] param ( [Parameter(Mandatory, HelpMessage = 'The space key or ID.')] [string]$SpaceKey, [Parameter(Mandatory, HelpMessage = 'The ID of the parent page.')] [string]$ParentId, [Parameter(Mandatory, HelpMessage = 'The exact page title.')] [string]$PageTitle, [Parameter(HelpMessage = 'Keep the newest page (default) or, with -KeepNewest:$false, the oldest.')] [switch]$KeepNewest ) if (-not $PSBoundParameters.ContainsKey('KeepNewest')) { $KeepNewest = $true } Write-Verbose "Looking for duplicate pages Title='$PageTitle' ParentId='$ParentId' SpaceKey='$SpaceKey' KeepNewest=$KeepNewest" $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 try { $query = @{ spaceKey = $SpaceKey; title = $PageTitle; limit = 250 } $response = Invoke-ConfluenceRequest -Method GET -Resource pages -ApiVersion 2 -Query $query -All -ErrorAction Stop $duplicates = @($response.Results | Where-Object { $null -ne $_ -and $_.title -eq $PageTitle -and "$($_.parentId)" -eq $ParentId }) if ($duplicates.Count -le 1) { Write-Verbose 'Zero or one page found with that title/parent. Nothing to remove.' return ($duplicates | Select-Object -First 1) } Write-Verbose "Found $($duplicates.Count) pages matching criteria." # Newest = latest creation date; page IDs (which increase) break ties $sortProperties = @( @{ Expression = { ConvertTo-ConfluenceUtcDate -Value $_.createdAt } }, @{ Expression = { $number = [long]0; if ([long]::TryParse("$($_.id)", [ref]$number)) { $number } else { [long]0 } } } ) $sortedPages = @($duplicates | Sort-Object -Property $sortProperties -Descending:$KeepNewest) $pageToKeep = $sortedPages[0] $pagesToDelete = @($sortedPages | Select-Object -Skip 1) Write-Verbose ('Keeping page ID={0} CreatedAt={1}' -f $pageToKeep.id, $pageToKeep.createdAt) foreach ($page in $pagesToDelete) { $deleteId = $page.id if (-not $PSCmdlet.ShouldProcess("Confluence page $deleteId ('$PageTitle')", 'Remove duplicate')) { continue } try { $null = Invoke-ConfluenceRequest -Method DELETE -Resource pages -ApiVersion 2 -Id $deleteId -MaxQueryPages 1 -ErrorAction Stop Write-Verbose "Deleted page ID=$deleteId" } catch { Write-Error ('Failed to delete page ID {0}. Error: {1}' -f $deleteId, $_) } } return $pageToKeep } catch { $telemetryFailed = $true Invoke-TelemetryCollection @TelemetryArgs -Stage End -Failed $true -Exception $_ Write-Error "Error removing duplicate pages. $_" } finally { if (-not $telemetryFailed) { Invoke-TelemetryCollection @TelemetryArgs -Stage End } } } |