Bulk replace content in pages

Last modified by Clément Aubin on 2024/01/11 09:49

cogAllows to perfrom bulk search and replace of contents in wiki pages
Developed by

Clément Aubin

0 Votes
LicenseGNU Lesser General Public License 2.1

Table of contents


This script allows to perform bulk search and replace of content in wiki pages.

This snippet requires the Job Macro to run.

In a new page, copy-paste the following snippet :

#set ($spacePickerParams = {
  'name': 'targetSpace',
  'value': "$!{request.targetSpace}"
This script allows to perform bulk search and replace of XWiki document contents.

Programming rights are required to use this script.

{{html clean="false"}}
<form class="xform" action="#" method="post">
     <label for="targetSpace">Space</label>
     <span class="xHint">The script will look for documents containing the given search text within the following space.</span>
     <label for="search">Text to be searched</label>
     <span class="xHint">The script will look for the following text in documents. The text in document contents has to match exactly with the search.</span>
     <textarea name="search" id="search"></textarea>
     <label for="search">Text to replace</label>
     <textarea name="replace" id="replace"></textarea>
     <input id="savePages" name="savePages" type="checkbox" value="save"/> <label for="savePages">Save pages</label>
     <span class="xHint">By default, this script will execute in dry-mode, and will not save pages.</span>
   <span class="buttonwrapper">
     <input type="hidden" name="form_token" value="$!{services.csrf.token}"/>
     <input type="hidden" name="confirm" value="true"/>
     <input class="button" type="submit" value="Replace contents"/>

{{job id="bulkReplace" start="{{velocity}}$!{request.confirm}{{/velocity}}"}}
  import org.apache.commons.lang3.StringUtils;

  logger = services.logging.getLogger('FixBrokenImages');
  services.logging.setLevel('FixBrokenImages', org.xwiki.logging.LogLevel.INFO);

  if (hasProgramming && services.csrf.isTokenValid(request.form_token)) {
    // Check if we have enough to work on
    if (request.targetSpace && StringUtils.isNotBlank(request.targetSpace)
    && request.search && StringUtils.isNotBlank(request.search)
    && request.replace && StringUtils.isNotBlank(request.replace)) {
      def spacePrefix = "${StringUtils.removeEnd(request.targetSpace, 'WebHome')}%";

      def sanitizedSearchString = request.search.replace('\r\n','\n');
      def sanitizedReplaceString = request.replace.replace('\r\n','\n');
      def documents = services.query.hql('select doc.fullName from XWikiDocument doc where doc.fullName like :spacePrefix and doc.content like :searchContent').bindValue('spacePrefix', spacePrefix.toString()).bindValue('searchContent').anyChars().literal(sanitizedSearchString).anyChars().query().execute();
      logger.debug('Found [{}] documents to verify', documents.size());
      documents.each { documentFullName ->
        try {
          def document = xwiki.getDocument(documentFullName);
          logger.info('Verifying document [{}]', document.getDocumentReference());
          def oldContent = document.getContent();
          def newContent = StringUtils.replace(oldContent, sanitizedSearchString, sanitizedReplaceString);
          hasContentChanged = !(oldContent.equals(newContent));

          if (hasContentChanged && 'save'.equals(request.savePages)) {
            logger.info('Content has changed ; saving document [{}]', document.getDocumentReference());
          } else if (hasContentChanged) {
            logger.info('Content for document [{}] has changed but will not be saved', document.getDocumentReference());
        } catch (Exception e) {
          logger.error('Uncaught exception [{}]', e);
    } else {
      logger.error('Insufficient parameters. Aborting.');
  } else {
    logger.error('Insufficient permissions or invalid CSRF token. Aborting.')

Get Connected