# -*- coding: iso-8859-1 -*- """ MoinMoin - SearchAndReplaceMultiplePages action This action allows you to do search and replace on multiple pages in one go. Written by Marc-Andre Lemburg Based on the RenamePage action by Jürgen Hermann @copyright: 2008, eGenx.com Software, Skills and Services GmbH @license: GNU GPL 2.0, see COPYING for details. """ import os, re from MoinMoin import wikiutil from MoinMoin.PageEditor import PageEditor ### Globals # Version __version__ = '1.0' # Should only the superuser be allowed to use this action ? SUPERUSER_ONLY = True ### class SearchAndReplaceMultiplePages: """ Search and replace on multiple pages action Note: the action name is the class name """ # Attributes request = None pagename = '' page = None orig_page = None error = u'' feedback = u'' def __init__(self, pagename, request): self.request = request self.pagename = pagename self.page = PageEditor(self.request, pagename) self.orig_page = self.page def allowed(self): """ Check if user is allowed to do this """ may = self.request.user.may return (not self.__class__.__name__ in self.request.cfg.actions_excluded and may.write(self.pagename)) def render(self): """ Render action This action returns a wiki page with optional message, or redirect to original page. """ _ = self.request.getText form = self.request.form if form.has_key('cancel'): # User canceled return self.page.send_page(self.request) # Validate user rights and page state. If we get error here, we # return an error message, without the rename form. error = None if not self.allowed(): error = _(u'You are not allowed to edit pages in this wiki!') elif SUPERUSER_ONLY and not self.request.user.isSuperUser(): error = _(u'Only superusers are allowed to use this action.') elif not self.page.exists(): error = _(u'This page is already deleted or was never created!') if error: # Send page with an error message return self.page.send_page(self.request, msg=error) if (not form.has_key('searchtext') or not form.has_key('replacetext')): self.error = _(u'Please fill in both a search text and ' 'a replacement text!') # Run search & replace on the pages elif (form.has_key('replace') and form.has_key('ticket')): self.replace() # Show the form (with error or feedback information) return self.page.send_page(self.request, msg=self.makeform()) def replace(self): """ Replace text on pages matching a regexp """ _ = self.request.getText form = self.request.form self.feedback = u'' # Require a valid ticket. Make outside attacks harder by # requiring two full HTTP transactions if not wikiutil.checkTicket(form['ticket'][0]): self.error = _(u'Please use the interactive user interface to ' 'do search & replace on multiple pages!') return # Get new name from form and normalize. comment = form.get('comment', [u''])[0] comment = wikiutil.clean_comment(comment) pagename = form.get('pagename')[0] pagename = self.request.normalizePagename(pagename) searchtext = form.get('searchtext')[0] replacetext = form.get('replacetext')[0] # Get list of all pages pages = self.request.rootpage.getPageList(user='', exists='') # Rename all matchin pages one by one for page in pages: # Check that the page name matches m = re.match(pagename, page) if m is None: continue # Search & replace on the page self.replace_page(page, searchtext, replacetext, comment) def replace_page(self, pagename, searchtext, replacetext, comment): _ = self.request.getText form = self.request.form # Open page self.pagename = pagename self.page = PageEditor(self.request, pagename) # Check permissions if not self.allowed(): self.error += _(u'You are not allowed to edit page "%s" !
' % pagename) return # Get page text oldtext = self.page.get_raw_body() # Apply repacements newtext = re.sub(searchtext, replacetext, oldtext) # Save page text with a comment, if the text changed if newtext != oldtext: try: self.page.saveText(newtext, 0, comment=comment) self.feedback += _('Updated page "%s".
' % (pagename,)) except PageEditor.SaveError, reason: self.error += _(u'Cannot save page "%s": %s !
' % (pagename, reason)) return def makeform(self): """ Display a search&replace page form The form also provides error feedback in case there was an error during the replace. """ from MoinMoin.widget.dialog import Dialog _ = self.request.getText error = '' if self.error: error = u'

%s

\n' % self.error namespace = { 'error': error, 'feedback': self.feedback, 'action': self.__class__.__name__, 'ticket': wikiutil.createTicket(), 'pagename': self.pagename, 'searchtext': self.request.form.get('searchtext', [''])[0], 'replacetext': self.request.form.get('replacetext', [''])[0], 'replace': _(u'Search & Replace'), 'cancel': _(u'Cancel'), 'pagename_label': _(u"Page name pattern"), 'searchtext_label': _(u"Search text pattern"), 'replacetext_label': _(u"Replace text pattern"), 'comment_label': _(u"Optional reason for the replacement"), 'note': _(u"Note: Page name and search pattern may use regular " "expression syntax (Python re-module syntax). " "Groups can be referenced in the replace pattern " "using \\1, \\2, etc."), } form = """ %(error)s

%(note)s

%(feedback)s

""" % namespace return Dialog(self.request, content=form) def execute(pagename, request): """ Glue code for actions """ SearchAndReplaceMultiplePages(pagename, request).render()