# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - ApproveChanges

    Permit the approval of an edited page queued by the page editor when an
    unprivileged user attempted to save a page. Since queued changes are placed
    in a subpage, this action just moves the queued page content into the
    existing page when approving the changes.

    @copyright: 2011 Paul Boddie <paul@boddie.org.uk>
    @license: GNU GPL (v2 or later), see COPYING.txt for details.
"""

Dependencies = ['pages']

from MoinMoin.action import ActionBase
from MoinMoin.Page import Page
from MoinMoin.PageEditor import PageEditor, conflict_markers
from MoinMoin.util import diff3
from ApproveChangesSupport import *

# Action class and supporting functions.

class ApproveChanges(ActionBase):

    "An action which approves a queued page version."

    def __init__(self, pagename, request):
        ActionBase.__init__(self, pagename, request)
        _ = self._
        self.form_trigger = "approve"
        self.form_trigger_label = _("Approve changes")

    def get_revision(self):
        request = self.request
        form = get_form(request)

        # Get the revision or None.

        rev = form.get("rev")
        if rev is None:
            return self.page.current_rev()
        else:
            return int(rev[0])

    def get_body(self, rev):
        request = self.request
        page = Page(request, self.page.page_name, rev=rev)
        return page.get_raw_body()

    def get_form_html(self, buttons_html):
        _ = self._
        request = self.request
        fmt = request.formatter

        if not is_queued_changes_page(request, self.pagename):
            return fmt.paragraph(1) + fmt.text(_("This page does not show queued changes.")) + fmt.paragraph(0)

        rev = self.get_revision()

        # Get information about the queued changes.

        body = self.get_body(rev)
        _body, directives = remove_directives(body, ["acl", "parent-revision", "unapproved-user"])

        # Get the target page's parent revision for the queued changes.

        target_page_name = get_target_page_name(self.pagename)
        target_page = PageEditor(request, target_page_name)

        current_rev = target_page.current_rev()
        parent_rev = int(directives.get("parent-revision", current_rev))

        # Get the user who submitted the changes.

        username = directives.get("unapproved-user")

        d = {
            "approval_label"        : escape(_("Make %s an approved user") % username),
            "buttons_html"          : buttons_html,
            "prompt"                : escape(_("Approve the displayed page version?")),
            "rev"                   : escattr(rev),
            "notice"                : escape(_("The affected page has been edited since the queued changes were made.")),
            }

        # Prepare the output HTML.

        html = '''
<table>
    <tr>
        <td>%(prompt)s</td>
    </tr>''' % d

        if parent_rev != current_rev:
            html += '''
    <tr>
        <td><em>%(notice)s</em></td>
    </tr>''' % d

        if username and not user_is_approved(request, username):
            html += '''
    <tr>
        <td><input name="approve" type="checkbox" value="true" /> %(approval_label)s</td>
    </tr>''' % d

        html += '''
    <tr>
        <td class="buttons">
            %(buttons_html)s
        </td>
    </tr>
</table>''' % d

        if rev:
            html += '''
<input name="rev" type="hidden" value="%(rev)s" />''' % d

        return html

    def do_action(self):

        "Approve the page and move it into place."

        _ = self._
        request = self.request
        form = get_form(request)

        # Make sure that only suitably privileged users can perform this action.

        queued_changes_page = get_queued_changes_page(request)

        if not is_reviewer(request):
            return 0, _("Only page reviewers can perform this action.")

        # Edit the target page, using this page's content.
        # The current page must be a queued page version.

        if not is_queued_changes_page(request, self.pagename):
            return 0, _("This page is not queued for approval.")

        # First, the displayed revision of the queued changes page must be
        # retrieved.

        rev = self.get_revision()
        body = self.get_body(rev)

        # Remove any introduced directives.

        body, directives = remove_directives(body, ["acl", "parent-revision", "unapproved-user"])

        # Get the target page's parent revision for the queued changes.

        target_page_name = get_target_page_name(self.pagename)
        target_page = PageEditor(request, target_page_name)

        current_rev = target_page.current_rev()
        parent_rev = int(directives.get("parent-revision", current_rev))

        # Get the user who submitted the changes.

        username = directives.get("unapproved-user")

        # Approve the user if requested, regardless of what happens below.

        if username and form.get("approve", ["false"])[0] == "true":
            add_to_group_page(request, username, get_approved_editors_group(request))

        # Where the parent revision differs from the current revision of the
        # page, attempt to merge the changes.

        conflict = False

        if parent_rev != current_rev:

            # The body of the parent revision of the target page, along with the
            # body of the current revision must be acquired.

            parent_body = Page(request, target_page_name, rev=parent_rev).get_raw_body()
            current_body = target_page.get_raw_body()

            # The parent, current and queued texts must then be merged.

            body = diff3.text_merge(parent_body, current_body, body, True, *conflict_markers)

            # Look for conflict markers and redirect to edit mode on the
            # resulting page if they are present.

            for marker in conflict_markers:
                if body.find(marker) != -1:
                    conflict = True
                    break

        # Delete the queued changes page.
        # NOTE: The page could be deleted completely or certain revisions
        # NOTE: purged.
        # NOTE: (to-do/proper-queued-page-deletion.txt)

        current_page = PageEditor(request, self.pagename)
        current_page.deletePage(_("Changes to page approved."))

        # Prepare a comment.

        comment = _("Changes to page approved from queue revision %d.") % rev

        # Save the target page, but only if there is no conflict.

        if not conflict:

            # Switch user if a specific user was recorded.

            if username:
                new_user = get_user(request, username)
            else:
                new_user = None

            if new_user:
                user = request.user
                request.user = new_user

            # Save the page.

            try:
                try:
                    target_page.saveText(body, 0, comment=comment)
                except PageEditor.Unchanged:
                    pass

            # Restore the user.

            finally:
                if new_user:
                    request.user = user

            # Redirect to the target page.

            request.http_redirect(target_page.url(request))

        # Otherwise, send the page editor.
        # NOTE: Replacing the revision in the request to prevent Moin from
        # NOTE: attempting to use the queued changes page's revision.
        # NOTE: Replacing the action and page in the request to avoid issues
        # NOTE: with editing tickets.

        else:
            request.rev = current_rev
            request.action = "edit"
            request.page = target_page
            target_page.sendEditor(preview=body, comment=comment, staytop=True)

        return 1, None

    def render_success(self, msg, msgtype):

        """
        Render neither 'msg' nor 'msgtype' since redirection should occur
        instead.
        NOTE: msgtype is optional because MoinMoin 1.5.x does not support it.
        """

        pass

# Action function.

def execute(pagename, request):
    ApproveChanges(pagename, request).render()

# vim: tabstop=4 expandtab shiftwidth=4
