Purge recycle bin attachments

Last modified by Anca Luca on 2021/03/18 11:28

cogUse this script to programmatically purge the attachments recycle bin.
TypeSnippet
Category
Developed by

Raluca Moisa, Vincent Massol, Anca Luca

Rating
0 Votes
LicenseGNU Lesser General Public License 2.1

Description

Simple version (deletes all attachments, from a single wiki)

{{groovy}}
import java.util.List
import com.xpn.xwiki.api.DeletedAttachment

// Send in request if you want to empty the trash bin for the whole wiki
def query = "select distinct ddoc.id from DeletedAttachment as ddoc "
def trashed = xwiki.search(query)
println "Total number of attachments to clean: ${trashed.size()}"
if (trashed) {
 def deleted = 0
  println "Trashed attachments"
  trashed.each() {
   def att = xwiki.getDeletedAttachment(it.toString())
   def attachmentReference = services.model.createAttachmentReference(services.model.resolveDocument(att.docName), att.filename)
    println "* ${services.model.serialize(attachmentReference)}"
   if (request.trashed == '1') {
      att.delete()      
   }      
 }
  println ""
 if (request.trashed != '1') {
    println "[[Confirm Cleaning Attachments>>||queryString='trashed=1']]"
 } else {
    println "[[Back>>||queryString='']]"
 }
} else {
  println "Everything's clean. No trashed document found."
}
{{/groovy}}

Advanced version (deletes attachments older than a given date, from multiple wikis)

{{html clean='false'}}
<script type='text/javascript'>
function registerCheckboxSelectionListener(){
  $('selector').observe('click', function(){
    $('prepareform').select('.toggleable').each(function(item) {
     if (item.checked){
        item.checked=false;
     } else {
        item.checked=true;
     }
   });
 });
}

Event.observe(document, 'xwiki:dom:loaded', function() {
  registerCheckboxSelectionListener();
});
</script>
{{/html}}

{{groovy}}
import java.util.List;
import com.xpn.xwiki.api.DeletedAttachment;
import java.util.Arrays;
import java.util.Collections;
import org.xwiki.wiki.descriptor.WikiDescriptor;
import java.text.SimpleDateFormat;
import java.util.Date;

def wikis = request.getParameterValues('wikis');
def logger = org.slf4j.LoggerFactory.getLogger(doc.fullName);
services.logging.setLevel(doc.fullName, org.xwiki.logging.LogLevel.INFO);
if (wikis == null || wikis.size() == 0) {
 /*
    No wikis selected yet, allow to choose the wiki to execute operation on.
    Also choose a date from which to delete.
  */

  def allWikis = services.wiki.getAll();
 int aThirdOfWikis = Math.floor(allWikis.size() / 3);
 int twoThirdsOfWikis = aThirdOfWikis * 2;
  println "{{info}}When many attachments found in a wiki - or on multiple wikis - the browser can display a timeout when executing the actions of this script. In that case, the progress of this script can be followed in the catalina.out logs, on the server.{{/info}}\n";

  println """{{html clean='false' wiki='false'}}
  <form id='prepareform' class='xform' method='post' action=''>
  <dl>
  <dt><label>Older than date:</label></dt>"""
;
  xwiki.ssfx.use('uicomponents/widgets/datepicker/calendarDateSelect.css', true);
  xwiki.jsfx.use('uicomponents/widgets/datepicker/calendarDateSelect.js', true);
  xwiki.jsfx.use('uicomponents/widgets/datepicker/simpleDateFormat.js', true);
  xwiki.ssfx.use('uicomponents/widgets/datepicker/dateTimePicker.css', true);
  xwiki.jsfx.use('uicomponents/widgets/datepicker/dateTimePicker.js');
  println """<dd><input type='text' class='datetime' title='dd/MM/yyyy' name='fromdate' /></dd>
  <dt><label>Wikis:</label><dt>
  <dd>
  <div class='column third'>
  <ul>
  <li><input id='selector' type='checkbox' value='' /><strong>Select/Deselect all</strong></li>"""
;
  Collections.sort(allWikis, new Comparator<WikiDescriptor> () {
   int compare(WikiDescriptor w1, WikiDescriptor w2) {
     return w1.getId().compareTo(w2.getId());
   }

   boolean equals(WikiDescriptor w1, WikiDescriptor w2) {
     return w1.getId().equals(w2.getId());
   }
 });
 for (int i = 0; i < allWikis.size(); i++) {
    def w = allWikis.get(i);
    println "<li><input name='wikis' type='checkbox' value='" + w.getId() + "' class='toggleable'>" + w.getId() + "</input></li>";
   if (i == aThirdOfWikis || i == twoThirdsOfWikis) {
      println """</ul></div>
                 <div class='column third'><ul>"""
;
   }
 }
  println """</ul>
  </dd></dl>
  <div class='clearfloats'></div>
  """
;
 // Display a skip preview button, to use only when preview timesout in the browser.
 println """
  <dl>
  <dt><label for='skippreview'>Skip preview (delete directly) - use carefully, reserve for the cases when preview times out in browser but you have watched the progress of preview in the server logs.
  <br/>Don't forget to set the same limit date as the preview case when deleting directly after a preview!</label></dt>
  <dd>
    <input type='checkbox' name='trashed' value='1' />
  </dd>
  </dl>
  <div class='wikimodel-emptyline'></div>
  """
;
  println """<div class='buttonwrapper'><input class='button' type='submit' name='preview' value='Preview (or DELETE, if preview is skipped)' /></div>
  </form>
  {{/html}}"""
;
} else {
 // prepare the query, depending on the sent date
 def queryString = "select distinct ddoc.id, ddoc.date from DeletedAttachment as ddoc";
  def query = services.query.hql(queryString);
  def date = null;
 if (request.fromdate != null && request.fromdate != '') {
    SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
    date = formatter.parse(request.fromdate);
    queryString = queryString + " where ddoc.date < :d";
    query = services.query.hql(queryString).bindValue('d', date);
 }
  println "Query: " + queryString;
  println "From date: " + date;
 for (w in wikis) {
    def originalDb = xcontext.getDatabase();
   try {
      xcontext.setDatabase(w);
     // Send in request if you want to empty the trash bin for the whole wiki
     def trashed = query.execute();
      println "==Wiki: " + w + "==";
      println "Total number of attachments to clean: ${trashed.size()}"
      logger.info("Wiki " + w + " . Total number of attachments " + trashed.size());
     int no = 0;
     if (trashed) {
        def deleted = 0
        println "Trashed attachments";
        trashed.each() {
          no++;
          def att = xwiki.getDeletedAttachment(it[0].toString())
          def attachmentReference = services.model.createAttachmentReference(services.model.resolveDocument(att.docName), att.filename)
          logger.info("Wiki " + w + ": [" + no + "] Getting attachment " + services.model.serialize(attachmentReference) + " from " + it[1]);
          println "|" + no + "|${services.model.serialize(attachmentReference)}|" + it[1];
         if (request.trashed == '1') {
            att.delete();
            logger.info("Wiki " + w + ": [" + no + "] DELETED ATTACHMENT " + services.model.serialize(attachmentReference) + " from " + it[1]);
         }      
       }
        println ""
     } else {
        println "Everything's clean. No trashed document found."
     }
   } catch (Exception e) {
      println "{{error}}An error ocurred while getting trashed attachments for this wiki.{{/error}}";
      e.printStackTrace();
   } finally {
      xcontext.setDatabase(originalDb);
   }
 }
 if (request.trashed != '1') {
   // print confirm form
   println """{{html clean='false' wiki='false'}}
    <form method='post' action=''>"""
;
   for(w in wikis) {
      println "<input type='hidden' name='wikis' value='" + w + "' />";
   }
   if (request.fromdate != null && request.fromdate != '') {
      println "<input type='hidden' name='fromdate' value='" + request.fromdate + "'></input>"
   }

    println """
    <input type='hidden' name='trashed' value='1'></input>
    <div class='buttonwrapper'>
      <input type='submit' name='doconfirm' class='button' value='Confirm cleaning attachments'></input>
    </div>
    </form>
    {{/html}}
    """
;
 } else {
    println "[[Back>>||queryString='']]"
 }
}
{{/groovy}}
Tags:
     

Get Connected