Batch move documents


I am looking for a solution to move all documents from one space to another. I am surprised this is so difficult to achieve in XWiki, I’d expect this to be basic functionality.

However, after some Googling, I found this conversation. It is suggested to use a combination of the query module and the refactoring module.

So after some more Googling and reading documentation, I came up with this script:


#if("$!request.sourceSpaceName" == '')
    <form action="" id="newdoc" method="post">
        New location: <input type="text" name="sourceSpaceName" value="old document location" class="withTip" size="50"/>
        <span class="buttonwrapper"><input type="submit" value="Lookup documents" class="button"/></span>
  == Documents in space $request.sourceSpaceName

|= name |=title|= full name |= parent
#set( $sourceSpaceName = $!request.sourceSpaceName)
#set( $sourceSpaceWebHome = $sourceSpaceName + ".WebHome")
#set( $docs = $services.query.xwql("select doc from Document as doc where doc.parent = :spaceName").bindValue('spaceName', $sourceSpaceWebHome).setLimit(200).execute() )
#foreach( $doc in $docs )
| $ | $doc.title |  $doc.fullName | $doc.parent

#if("$!request.targetSpaceName" != '')
  #set ($query = $services.query.xwql('select space from XWikiSpace as space where = :spaceName'))
  #set ($space = $query.bindValue('spaceName', $request.targetSpaceName).execute())

  #if ($space.size() > 0)
    #if  ($space[0].name == $request.targetSpaceName)
      #foreach ($doc in $docs)
        #set ($source = $services.model.resolveSpace($doc.fullName))
        * moving $doc ($source)
        #set ($dest1 = $services.model.resolveDocument($request.targetSpaceName))
        #set ($dest2 = $doc.fullName.replace($sourceSpaceName, ${request.targetSpaceName}).replace(".WebHome", ""))
        #set ($destination = $services.model.resolveSpace($dest2))
        ** to $destination
        #try ()
          #set ($job = $services.refactoring.move($source, $destination))
          #set ($result = $job.join())
        #if ("$!exception" != '')
          #displayException('some summary to display', $exception)
    (% class="box errormessage" %)
    Space does not exist


  <form action="" id="newdoc" method="post">
      <input type="hidden" name="sourceSpaceName" value="$sourceSpaceName"/>
      New location: <input type="text" name="targetSpaceName" value="new document location" class="withTip" size="50"/>
      <span class="buttonwrapper"><input type="submit" value="Move these documents" class="button"/></span>


It does the job, however, it does have a few flaws:

  • It has limited validation, thus is not fool proof;
  • No rights are checked whatsoever, not really secure;
  • There’s no feedback to users on progress and/or errors;
  • It is not recursive. So if there are multiple levels of documents under the source space, only the top level of documents are moved, I you end up with the deeper levels still in the original space, which is a pretty weird situation.

To overcome the last thing, I tried to replace #set ($dest1 = $services.model.resolveDocument($request.targetSpaceName)) by #set ($dest1 = $services.model.resolveSpace($request.targetSpaceName)). My idea was that when moving it as a space instead of a page, the children would be moved also. But if I do this change, nothing gets moved at all and I don’t get any error either.

The first three bullets from above are things I could add, but it seems a lot of work. Especially the feedback part, the documentation on Jobs is not very comprehensive and it seems complicated to me.

My questions are:

  1. Is this the way to go? Or are there simpler ways to achieve what I want?
  2. If this is the way to go, can someone give me some examples on how I could add:
    a. validation
    b. checking of rights
    c. feedback on progress
    d. feedback on errors
    e. recursion

I hope someone can help me.

Best regards,
Bart Kummel

It’s very simple :slight_smile: Using the UI directly

And if you want to script it, it’s also very easy, see


Hi @vmassol,

Thanks for your very quick answer! Unfortunately, it is not really helpful to me:

  • I can’t use the UI, since I want to move all children, but not the space itself. And the space I want to move the children to already contains other documents.
  • I’m already aware of the refactoring module, as you can see in my script, where I use the move operation from that module. However, I find the documentation a bit lacking:
    • Especially this part:

      For the sake of brevity in the following examples the HTTP request is blocked until the job is done by calling join(). In real life you should monitor the progress of the operation using the job status.

      I can’t find any useful documentation on how to “monitor the progress of the operation using the job status” from within a Velocity script in a wiki page.

    • And I’m also struggling to find out what’s going on when an error occurs, it seems the join() just finishes successfully, eating up all errors and exceptions.

    • I am also still searching for details documentation on the behavior of the move() call itself. E.g. what does it mean when you move a space instead of a page?

I hope you can give me some more pointers… :wink:

Best regards,
Bart Kummel