Bulk replace content in pages

Last modified by Raphaël Jakse on 2024/11/18 12:47

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

Clément Aubin

Rating
1 Votes
LicenseGNU Lesser General Public License 2.1

Table of contents

Description

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 :

{{velocity}}
#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">
 <dl>
   <dt>
     <label for="targetSpace">Space</label>
     <span class="xHint">The script will look for documents containing the given search text within the following space.</span>
   </dt>
   <dd>
      #pagePicker($spacePickerParams)
   </dd>
   <dt>
       <input type="checkbox" name="allSpaces" id="allSpaces" />
       <label for="allSpaces">All spaces</label>
       <span class="xHint">The macro replace job will execute for every document in all spaces (excerpt the XWiki system space).</span>
   </dt>
   <dt>
     <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>
   </dt>
   <dd>
     <textarea name="search" id="search"></textarea>
   </dd>
   <dt>
     <label for="search">Text to replace</label>
   </dt>
   <dd>
     <textarea name="replace" id="replace"></textarea>
   </dd>
   <dt>
     <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>
   </dt>
 </dl>
 <p>
   <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"/>
   </span>
 </p>
</form>
{{/html}}
{{/velocity}}

{{job id="bulkReplace" start="{{velocity}}$!{request.confirm}{{/velocity}}"}}
{{groovy}}
  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
    boolean allSpaces = request.allSpaces && request.allSpaces != '0' && request.allSpaces != 'false' && request.allSpaces != 'off';
    if ((!request.targetSpace || StringUtils.isBlank(request.targetSpace)) && !allSpaces) {
        logger.error('Missing a target space. Aborting.');
        return;
    }

    if (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');
      // Get every page matching the space
      List<String> documents = (
        allSpaces
          ? services.query
              .hql("select doc.fullName from XWikiDocument doc where doc.fullName not like 'XWiki.%' and doc.content like :searchContent")
          : 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());
            document.setContent(newContent);
            document.save();
          } 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.')
  }
{{/groovy}}
{{/job}}

Get Connected