XWiki Attachment Cleanup Script

Last modified by Vertganti on 2026/06/02 17:55

cogThis script is designed to identify and remove unused attachments from specified XWiki spaces. It provides an interface to display the list of unused attachments with relevant details, as well as options to delete individual attachments or all attachments in the list.
TypeSnippet
CategoryOther
Developed by

Paul Panțiru

Rating
0 Votes
LicenseGNU Lesser General Public License 2.1

Description

This script is designed to identify and remove unused attachments from specified XWiki spaces. It provides an interface to display the list of unused attachments with relevant details, as well as options to delete individual attachments or all attachments in the list.

Features

  • Display the list of unused attachments
  • Show attachment details such as the document name, attachment name, and creator
  • Provide a link to view the attachment
  • Provide a button to delete individual attachments
  • Provide a button to delete all listed attachments
  • Bypass the recycle bin (optional)

Usage

  1. Place the provided script in a new XWiki page within your desired space.
  2. Access the page with the script, and you will see a form to configure the script's behavior.
  3. Enter the space name where you want to look for unused attachments.
  4. Check the "Confirmation Alert" checkbox if you want to display a confirmation dialog before deleting an attachment.
  5. Check the "Move to Recycle Bin" checkbox if you want to move the deleted attachments to the recycle bin. Uncheck this option if you want to permanently delete the attachments without moving them to the recycle bin.
  6. Click the "Submit" button to display the list of unused attachments in the specified space.
  7. From the list, you can click the "Delete" button next to an individual attachment to remove it, or click the "Delete all" button at the bottom of the list to remove all unused attachments in the list.

Notes

  • The script will not work in the "XWiki" space to prevent accidental removal of crucial attachments that could cause system issues.
  • The deletion process may take some time, especially if there are many attachments to remove. Be patient and wait for the process to complete before navigating away from the page.
  • Always double-check the list of attachments before proceeding with deletion, as the script might identify some attachments as unused even though they are still required. This may happen, for example, if the attachment is linked dynamically via JavaScript or other non-standard methods.
  • To avoid unintended data loss, it is recommended to perform a backup of your XWiki instance before running this script, especially if you plan to delete a large number of attachments.
  • Tested on: 13.10.7 and 14.10.5

Troubleshooting

  • If the script doesn't display the list of unused attachments or doesn't delete attachments as expected, ensure that you have the necessary permissions to execute the script and perform the required operations.
  • If you encounter performance issues when running the script, consider executing it during off-peak hours to minimize the impact on your XWiki instance.
     
{{velocity}}
#displayForm

#if ($request.space)
  #set($space = $request.space)
  #set($confirmationAlert = $request.confirmationAlert == 'true')
  #set($moveToRecycleBin = $request.moveToRecycleBin == 'true')
#end

##set($space = 'Tour')
##set($confirmationAlert = true)
##set($moveToRecycleBin = true) ## set this to false if low on space or you just don't want it moved to the recycle bin

#if($space && $space != "XWiki")


  ####
  #set($nspace = "${space}.%")
  #set($query = "select distinct doc.fullName, attach.filename
  from XWikiAttachment as attach
  inner join XWikiDocument as doc on attach.docId = doc.id
  where attach.docId in (
      select distinct doc.id
      from XWikiDocument as doc
      where doc.name <> 'WebPreferences'
      and (doc.space like :space or doc.space like :nspace)
  )
  and not exists (
      select 1
      from XWikiDocument as doc
      where doc.content like CONCAT('%', attach.filename, '%')
  )
  and not exists (
      select 1
      from XWikiDocument as doc, BaseObject as obj, StringProperty as prop
      where doc.fullName = obj.name and obj.id=prop.id.id
      and prop.value like CONCAT('%', attach.filename, '%')
  )
  and not exists (
      select 1
      from XWikiDocument as doc, BaseObject as obj, LargeStringProperty as prop
      where doc.fullName = obj.name and obj.id=prop.id.id
      and prop.value like CONCAT('%', attach.filename, '%')
  )
  ")

  #set($results = $services.query.hql($query).bindValue('space',$space).bindValue('nspace',$nspace).execute())

  #displayList($results $confirmationAlert)

  #macro(displayForm)
    {{html}}
      <form id="xwikiForm" action="" method="post" #if($request.confirmationAlert)hidden#end>
        <label for="space">Space:</label>
        <input type="text" name="space" id="space" value="$!{request.space}" required>
        <br><br>

        <label for="confirmationAlert">Confirmation Alert:</label>
        <input type="checkbox" name="confirmationAlert" id="confirmationAlert" value="true" checked>
        <br><br>

        <label for="moveToRecycleBin">Move to Recycle Bin:</label>
        <input type="checkbox" name="moveToRecycleBin" id="moveToRecycleBin" value="true" checked>
        <br><br>

        <input type="submit" class="btn btn-primary" value="Submit">
      </form>
      #if($request.confirmationAlert)<a href="$doc.getURL()" class="btn btn-default">Back</a>#end
    {{/html}}
  #end

  #macro(displayList $results $confirmationAlert)
    #if($request.delete != "all")
      |=Doc Name|=Doc reference|=Doc Creator|=Attachment|=Added by|=Delete attachment
      #foreach($result in $results)
        #set($docName = $result[0])
        #set($attachName = $result[1])
        #set($document = $xwiki.getDocument($docName))
        #if(!$document.isNew())
          #set($delURL = $document.getAttachmentURL($attachName, "delattachment", "form_token=$!{services.csrf.token}"))
          #set($deleteConfirm = $services.localization.render('core.viewers.attachments.delete.confirm', [$attachName]))
          #if($request.attachment != "$!{attachName}")
            |[[$document]]|$document.fullName|#userlink($document.creator)|[[$attachName>>attach:$document@$attachName]]|#userlink($document.getAttachment($attachName).getAuthor())|
            {{html}}<a href="?space=$!{request.space}&confirmationAlert=$!{request.confirmationAlert}&moveToRecycleBin=$!{request.moveToRecycleBin}&delete=true&attachment=$!{attachName}&document=$!{document}" #if($confirmationAlert)onclick="return confirm('$deleteConfirm');"#end class="btn btn-danger">Delete</a>{{/html}}|
          #end
        #end
      #end
    #end
  #end

  {{html}}<a href="?space=$!{request.space}&confirmationAlert=$!{request.confirmationAlert}&moveToRecycleBin=$!{request.moveToRecycleBin}&delete=all" onclick="return confirm('$deleteConfirm');" class="btn btn-danger">Delete all</a>{{/html}}

  #if($!request.delete == "true")
    #set($document = $xwiki.getDocument($request.document).getDocument())
    #set($attachName = $request.attachment)
    #deleteAttachment(${document}, $attachName, $moveToRecycleBin, false)
  #end

  #if($!request.delete == "all")
    #foreach($res in $results)
      #deleteAttachment($res[0], $res[1], $moveToRecycleBin, true)
    #end
  #end

  #macro(deleteAttachment $docName $attName $rb $all)
    #set ($myDoc = $xwiki.getDocument($docName))
    #set ($attachmentName = $attName)
    #if ($myDoc.getAttachment($attachmentName))
      #set ($moveToRecycleBin = $rb)
      #set ($ok = $myDoc.document.removeAttachment($myDoc.getAttachment($attachmentName).attachment, $moveToRecycleBin))
      #set ($ok = $myDoc.save())
    #end
    #if (!$myDoc.getAttachment($attachmentName))
      Deleted: **${attachmentName}** from **${myDoc}**
    #end
  #end

  #macro(userlink $usr)
      #if($usr != 'XWiki.superadmin')
        #set($usrDisplay = "[[$usr]]")
      #else
        #set($usrDisplay = "superadmin")
      #end
      $usrDisplay##
  #end
#elseif($space)

  {{warning}}
  This script is not allwed to be run on the XWiki space as it could cause problems
  {{/warning}}

#end
{{/velocity}}

Get Connected