# -*- 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 <mal@egenix.com>
    Based on the RenamePage action by Jürgen Hermann <jh@web.de>

    @copyright: 2008, eGenx.com Software, Skills and Services GmbH <info@egenix.com>
    @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" ! <br>' %
                            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". <br>' %
                                   (pagename,))

            except PageEditor.SaveError, reason:
                self.error += _(u'Cannot save page "%s": %s ! <br>' %
                                (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'<p class="error">%s</p>\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
<form method="post" action="">
<input type="hidden" name="action" value="%(action)s">
<input type="hidden" name="ticket" value="%(ticket)s">
<table>
    <tr>
        <td class="label"><label>%(pagename_label)s</label></td>
        <td class="content">
            <input type="text" name="pagename" value="%(pagename)s">
        </td>
    </tr>
    <tr>
        <td class="label"><label>%(searchtext_label)s</label></td>
        <td class="content">
            <input type="text" name="searchtext" value="%(searchtext)s">
        </td>
    </tr>
    <tr>
        <td class="label"><label>%(replacetext_label)s</label></td>
        <td class="content">
            <input type="text" name="replacetext" value="%(replacetext)s">
        </td>
    </tr>
    <tr>
        <td class="label"><label>%(comment_label)s</label></td>
        <td class="content">
            <input type="text" name="comment" maxlength="80">
        </td>
    </tr>
    <tr>
        <td></td>
        <td class="buttons">
            <input type="submit" name="replace" value="%(replace)s">
            <input type="submit" name="cancel" value="%(cancel)s">
        </td>
    </tr>
</table>
<p style="font-weight: normal">%(note)s</p>
<p>%(feedback)s</p>
</form>
""" % namespace
        
        return Dialog(self.request, content=form)        
    
def execute(pagename, request):

    """ Glue code for actions

    """
    SearchAndReplaceMultiplePages(pagename, request).render()
    
