Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in
It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Search for unused attachments in server instance

Edited

Hi,

I've finally made un update of my initial script which I described here .

Now it checkes all the pages in our instance, and write the output in a Confluence page.

Quite sure many people here have already more sophisticated ways to do the job, but if this can help rookie-admins like me, then I'm happy.

I know I should stop using PageManager and SpaceManager get... calls, but honestly I haven't yet really understood how to make it work in the other way :-) so I'm staying on the good old managers I know ... :-)

Ciao Ciao, Andrea

 

Immagine 2020-11-26 005534.jpg

import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.confluence.pages.Page
import com.atlassian.confluence.pages.PageManager
import com.atlassian.confluence.spaces.Space
import com.atlassian.confluence.spaces.SpaceManager
import com.atlassian.confluence.pages.Attachment
import com.atlassian.confluence.pages.AttachmentManager
import com.atlassian.confluence.core.DefaultSaveContext
import groovy.xml.MarkupBuilder
import java.math.RoundingMode
import org.jsoup.Jsoup

def spaceManager = ComponentLocator.getComponent(SpaceManager)
def pageManager = ComponentLocator.getComponent(PageManager)
def attachmentManager = ComponentLocator.getComponent(AttachmentManager)


//All spaces to check
def spacesChecked=spaceManager.allSpaces.findAll{it instanceof Space}

//log.warn(spacesChecked)

//List and Map for attachments use cases
def attachmentUseCases=[]
//List and Map for attachment cases
def attachmentCases=[]
def attachmentCasesMap=[:]
//List and Map for unused attachment cases
def unusedAttachmentCases=[]
def unusedAttachmentCasesMap=[:]

////////////////////////////////////////
//Loop through spaces
////////////////////////////////////////

spacesChecked.each(){
spaceChecked->

//Pages to be checked
def pagesChecked=pageManager.getPages(spaceChecked, true).findAll{it instanceof Page}

////////////////////////////////////////
//Loop through space pages
////////////////////////////////////////

pagesChecked.each(){
pageChecked->

//////////////////////////////////////////
//Looking for attachment use cases in page
//////////////////////////////////////////

//Get content page for parsing
def body = pageChecked.bodyContent.body

//Parse page content
//Refs for use of Jsoup
//https://jsoup.org/cookbook/extracting-data/selector-syntax
//https://jsoup.org/cookbook/extracting-data/attributes-text-html
def parsedBody = Jsoup.parse(body)
//Looking for attachment tag
def attachments = parsedBody.select("ri|attachment")

//Going through all the attachment tags found
attachments.each(){
attachment ->

//Attachment file name
def attFileName=attachment.attr('ri:filename')

//Page of origin of the attachment
def attPageOrigin=attachment.select("ri|page").attr('ri:content-title')
if (attPageOrigin){
}
else{
attPageOrigin=pageChecked.title
}
//Space of origin of the attachment
def attSpaceOrigin=attachment.select("ri|page").attr('ri:space-key')
if (attSpaceOrigin){
}
else{
attSpaceOrigin=spaceChecked.key
}

//Attachment use case unique id
def attUseCaseId="${attSpaceOrigin}__${attPageOrigin}__${attFileName}"

//Add attachment use case id to list
if(!attachmentUseCases.contains(attUseCaseId)){
attachmentUseCases.add(attUseCaseId)
}
}

/////////////////////////////////
//Looking for attachments to page
/////////////////////////////////

def loadedAttachments=attachmentManager.getLatestVersionsOfAttachments(pageChecked)
loadedAttachments.each(){
lAttachment->

def lAttachmentFileName=lAttachment.fileName
def lAttachmentSize=(double)lAttachment.getFileSize()/(1024*1024)
BigDecimal lAttachmentSizeBD = new BigDecimal(lAttachmentSize).setScale(4, RoundingMode.HALF_UP)
def lAttachmentId="${spaceChecked.key}__${pageChecked.title}__${lAttachmentFileName}"

attachmentCases.add(lAttachmentId) //List for quick reference

def attachmentCasesMapAtt=[
space:spaceChecked.key,
page:pageChecked.title,
pageId:pageChecked.id,
pagePath:pageChecked.getUrlPath(),
name:lAttachmentFileName,
size:lAttachmentSizeBD.toString(),
path:lAttachment.getDownloadPath()
]
attachmentCasesMap.put(lAttachmentId, attachmentCasesMapAtt)
}
}
}

////////////////////////////////////////
//Check resulting lists from loops
////////////////////////////////////////

//Sort lists
attachmentCases.sort()
attachmentUseCases.sort()

//Compare lists to find unused attachment cases
attachmentCases.each(){
eAttachment->
if(!attachmentUseCases.contains(eAttachment)){
unusedAttachmentCases.add(eAttachment)
}
}

/////////////////////////////////
//Export to a table
/////////////////////////////////

def writer = new StringWriter()
def xml = new MarkupBuilder(writer)


xml.p("Unused attachment cases:")

xml.table(class: "aui") {
thead {
tr{
th('Space')
th('Page')
th('Page Id')
th('Attachment')
th('Size (MB)')
th('Download Path')
th('Delete Link')
}
}
tbody{
unusedAttachmentCases.each(){
unusedAttachmentCase->
tr{
td(attachmentCasesMap.get(unusedAttachmentCase).get('space'))
td{
a(href:[CONFLUENCE BASE DIR]+
attachmentCasesMap.get(unusedAttachmentCase).get('pagePath')){
p(attachmentCasesMap.get(unusedAttachmentCase).get('page'))
}
}
td(attachmentCasesMap.get(unusedAttachmentCase).get('pageId'))
td(attachmentCasesMap.get(unusedAttachmentCase).get('name'))
td(attachmentCasesMap.get(unusedAttachmentCase).get('size'))
td{
a(href:[CONFLUENCE BASE DIR]+attachmentCasesMap.get(unusedAttachmentCase).get('path')){
p('Download Link')
}
}
td{
a([CONFLUENCE BASE DIR]+'/lockpoint/confirmattachmentremoval.action?pageId='
+attachmentCasesMap.get(unusedAttachmentCase).get('pageId')+'&fileName='
+attachmentCasesMap.get(unusedAttachmentCase).get('name')){
p('Delete Link')
}
}
}
}
}

}

/////////////////////////////////
//Write Page in Confluence
/////////////////////////////////

//Define Space and Page for output
def outputPage=pageManager.getPage("MySpace", "MyPage")


def oldVersion=outputPage.clone()
def contentEntityObjectOut = outputPage.getEntity()
contentEntityObjectOut.setBodyAsString(writer.toString())
pageManager.saveContentEntity(outputPage, oldVersion, DefaultSaveContext.BULK_OPERATION)

//Finally write in console
return writer

 

2 comments

Hi @Andrea Boerio 

this looks really interesting - especially at the end of the year a good "house-clean" -> it's time for!

Definitely gonna give it a try! Thanks for sharing 😊

Corrected this section of my previous code

Immagine 2020-12-01 191642.jpg

Added .title and .key

Ciao, Andrea

Comment

Log in or Sign up to comment
TAGS
Community showcase
Published in Confluence

Welcome to the Confluence Holiday Workshop 🎁

Welcome to Confluence’s Holiday Workshop, where knowledge and collaboration collide! This year come celebrate the holidays with brand-new Confluence features to kickoff your new year, best practice...

436 views 11 31
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you