# -*- coding: utf-8 -*-
"""
MoinMoin - GlobalPageRename action

This action allows you to rename multiple pages in one go.
It offers a preview feature that lets you review what would be renamed
before doing the actual renaming.

Adapted to moin-1.9.8 and extended by David Linke.
Based on version 1.0 of "RenameMultiplePages.py" downloaded
from http://www.egenix.com/library/moinmoin/

Written by Marc-Andre Lemburg <mal@egenix.com>,
Based on the RenamePage action by Jürgen Hermann <jh@web.de>

@copyright: 2015-2018, David Linke, MoinMoin:DavidLinke, https://github.com/dalito
@copyright: 2008, eGenix.com Software, Skills and Services GmbH <info@egenix.com>
@license: GNU GPL 2.0, see COPYING for details.
"""

import re

from MoinMoin import wikiutil
from MoinMoin.PageEditor import PageEditor
from MoinMoin.Page import Page
from MoinMoin.action import ActionBase
from MoinMoin import log

__version__ = '2.0'

# Should only the superuser be allowed to use this action ?
SUPERUSER_ONLY = False

logging = log.getLogger(__name__)


class GlobalPageRename(ActionBase):
    """Rename multiple pages action

    Note: the action name is the class name
    """
    def __init__(self, pagename, request):
        ActionBase.__init__(self, pagename, request)
        self.page = PageEditor(self.request, pagename)
        self.orig_pagename = pagename
        self.feedback = u''
        self.newpagename = None
        self.no_refused_access = 0

    def is_allowed(self, pagename=None):
        """Check if user is allowed to access page"""
        if pagename is None:
            pagename = self.pagename
        may = self.request.user.may
        return may.write(pagename) and may.delete(pagename)

    def render(self):
        """Render action

        This action returns a wiki page with optional message, or
        redirect to one of the new pages.
        """
        _ = self.request.getText
        form = self.request.form

        if 'cancel' in form:
            # User cancelled
            return self.page.send_page()

        # Validate user rights and page state. If we get error here, we
        # return an error message, without the rename form.
        error = None
        if self.is_excluded():
            error = _('Action %(actionname)s is excluded in this wiki!') % {
                'actionname': self.actionname}
        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
            self.request.theme.add_msg(msg=error)
            return self.page.send_page()

        if not form.get('pagename') or not form.get('newpagename'):
            self.request.theme.add_msg(_(u'Please fill in both a search text '
                                         'and a replacement text!'), 'info')

        # Rename the pages.
        elif ('rename' in form or 'preview' in form) and 'ticket' in form:
            # Do the renaming
            self.rename()

            # Switch to new page if current page was renamed.
            if self.newpagename:
                self.request.theme.add_msg(_('<B>Current page renamed.</B> '
                                             ' Moved to new page.'), 'info')
                self.page = Page(self.request, self.newpagename)

        # Show the form (with error or feedback information)
        self.request.theme.add_msg(self.makeform(), 'info')
        return self.page.send_page()

    def rename(self):
        """Rename pagename and return the new page"""
        _ = self.request.getText
        form = self.request.form
        formatter = self.request.formatter
        self.feedback = u''
        self.no_refused_access = 0

        # Require a valid ticket. Make outside attacks harder by
        # requiring two full HTTP transactions
        if not wikiutil.checkTicket(self.request, form['ticket']):
            self.error = _(u'Please use the interactive user interface '
                           'to rename pages!')
            return

        # Get new name from form and normalize.
        comment = form.get('comment', [u''])
        comment = wikiutil.clean_input(comment)

        pagename = form.get('pagename')
        newpagename = form.get('newpagename')

        # test if regular expression is OK
        try:
            re.compile(pagename)
        except Exception as err:
            self.error = _(u'Error in page name pattern: %s' % err)
            return

        # Get list of all pages and find the ones to rename
        pages = self.request.rootpage.getPageList(user='', exists='')
        pages_to_rename = [p for p in pages if re.search(pagename, p)]

        self.feedback += formatter.rule()
        self.feedback += formatter.paragraph(1)
        self.feedback += _('Found %i matching pagename(s).' %
                           len(pages_to_rename))
        self.feedback += formatter.paragraph(0)

        if not pages_to_rename:
            return

        self.feedback += formatter.rule()
        self.feedback += formatter.paragraph(1)
        if 'preview' in self.request.form:
            self.feedback += _('Would do the following renaming:')
        else:
            self.feedback += _('Renaming pages...')
        self.feedback += formatter.paragraph(0)

        # Rename all matching pages one by one
        self.feedback += formatter.bullet_list(1)

        for oldpage in pages_to_rename:
            # Create & normalize new page name
            try:
                newpage = re.sub(pagename, newpagename, oldpage)
            except Exception as err:
                self.error = _(u'Error in new name pattern: %s' % err)
                return
            newpage = wikiutil.normalize_pagename(newpage, self.cfg)
            # Rename page
            if oldpage != newpage:
                self.rename_page(oldpage, newpage, comment)

        if self.no_refused_access:
            self.feedback += formatter.listitem(1)
            self.feedback += formatter.smiley('<!>')
            self.feedback += _(u' %i pages skipped because of access '
                               'restrictions.' % self.no_refused_access)
            self.feedback += formatter.listitem(0)
        self.feedback += formatter.bullet_list(0)

    def rename_page(self, pagename, newpagename, comment):
        """Rename page with pagename to newpage and add comment to editlog
        """
        _ = self.request.getText
        formatter = self.request.formatter

        # Check permissions. Don't reveal the page name, just count.
        if not self.is_allowed(pagename):
            self.no_refused_access += 1
            return

        # Name might be empty after normalization.
        if not newpagename:
            self.feedback += formatter.listitem(1)
            self.feedback += formatter.smiley('<!>')
            self.feedback += _(u' Cannot rename page "%s" to an empty '
                               'pagename!' % pagename)
            self.feedback += formatter.listitem(0)
            return

        page = PageEditor(self.request, pagename)
        newpage = PageEditor(self.request, newpagename)

        # preview
        if 'preview' in self.request.form:
            self.feedback += formatter.listitem(1)
            if newpage.exists(includeDeleted=True):
                self.feedback += formatter.smiley('<!>')
                self.feedback += ' "%s" --> "%s" ' % (pagename, newpagename)
                self.feedback += formatter.strong(1)
                self.feedback += _('Target already exists.')
                self.feedback += formatter.strong(0)
            else:
                self.feedback += '"%s" --> "%s"' % (pagename, newpagename)
            self.feedback += formatter.listitem(0)
            return

        # Check whether a page with the new name already exists
        if newpage.exists(includeDeleted=True):
            return self.page_exists_error(newpagename)

        if newpage.exists(includeDeleted=True):
            self.feedback += formatter.listitem(1)
            self.feedback += formatter.smiley('<!>')
            self.feedback += _('A page <B>%s</B> already exists.' %
                               wikiutil.escape(newpagename))
            self.feedback += formatter.listitem(0)

        # Rename page
        success, msgs = page.renamePage(newpagename, comment)
        if msgs:
            self.feedback += msgs

        if success and self.orig_pagename == pagename:
            self.newpagename = newpagename

        self.feedback += formatter.listitem(1)
        self.feedback += formatter.smiley('(./)')
        self.feedback += '%s --> %s' % (pagename, newpagename)
        self.feedback += formatter.listitem(0)

    def makeform(self):
        """ Display a rename 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(self.request),
            'pagename': self.request.form.get('pagename', self.pagename),
            'newpagename': self.request.form.get('newpagename', self.pagename),
            'comment': self.request.form.get('comment', ''),
            'rename': _(u'Rename Pages'),
            'preview': _(u'Preview'),
            'cancel': _(u'Cancel'),
            'pagename_label': _(u"Page name pattern"),
            'newname_label': _(u"New name pattern"),
            'comment_label': _(u"Optional reason for the renaming"),
            'note': _(u"Note: Page name pattern may use regular expression "
                      "syntax (Python re-module syntax). Groups can be "
                      "referenced in the new name 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" size="60" value="%(pagename)s">
        </td>
    </tr>
    <tr>
        <td class="label"><label>%(newname_label)s</label></td>
        <td class="content">
            <input type="text" name="newpagename" size="60" value="%(newpagename)s">
        </td>
    </tr>
    <tr>
        <td class="label"><label>%(comment_label)s</label></td>
        <td class="content">
            <input type="text" name="comment" size="60" maxlength="80" value="%(comment)s">
        </td>
    </tr>
    <tr>
        <td></td>
        <td class="buttons">
            <input type="submit" name="preview" value="%(preview)s">
            <input type="submit" name="rename" value="%(rename)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 page_exists_error(self, pagename):
        _ = self.request.getText
        self.error += _(u"A page with the name %s already exists. "
                        "Skipped. <br>") % (pagename,)


def execute(pagename, request):
    """Glue code for actions"""
    GlobalPageRename(pagename, request).render()
