ApproveChanges

Annotated actions/ApproveChanges.py

24:bb4e763711e9
2011-10-16 Paul Boddie Added tag rel-0-1 for changeset 598a4562d7f3
paul@0 1
# -*- coding: iso-8859-1 -*-
paul@0 2
"""
paul@0 3
    MoinMoin - ApproveChanges
paul@0 4
paul@0 5
    Permit the approval of an edited page queued by the page editor when an
paul@3 6
    unprivileged user attempted to save a page. Since queued changes are placed
paul@3 7
    in a subpage, this action just moves the queued page content into the
paul@0 8
    existing page when approving the changes.
paul@0 9
paul@0 10
    @copyright: 2011 Paul Boddie <paul@boddie.org.uk>
paul@0 11
    @license: GNU GPL (v2 or later), see COPYING.txt for details.
paul@0 12
"""
paul@0 13
paul@0 14
Dependencies = ['pages']
paul@0 15
paul@0 16
from MoinMoin.action import ActionBase
paul@7 17
from MoinMoin.Page import Page
paul@11 18
from MoinMoin.PageEditor import PageEditor, conflict_markers
paul@11 19
from MoinMoin.util import diff3
paul@1 20
from ApproveChangesSupport import *
paul@0 21
paul@0 22
# Action class and supporting functions.
paul@0 23
paul@0 24
class ApproveChanges(ActionBase):
paul@0 25
paul@0 26
    "An action which approves a queued page version."
paul@0 27
paul@0 28
    def __init__(self, pagename, request):
paul@0 29
        ActionBase.__init__(self, pagename, request)
paul@0 30
        _ = self._
paul@0 31
        self.form_trigger = "approve"
paul@0 32
        self.form_trigger_label = _("Approve changes")
paul@0 33
paul@15 34
    def get_revision(self):
paul@15 35
        request = self.request
paul@15 36
        form = get_form(request)
paul@15 37
paul@15 38
        # Get the revision or None.
paul@15 39
paul@15 40
        rev = form.get("rev")
paul@15 41
        if rev is None:
paul@15 42
            return self.page.current_rev()
paul@15 43
        else:
paul@15 44
            return int(rev[0])
paul@15 45
paul@15 46
    def get_body(self, rev):
paul@15 47
        request = self.request
paul@15 48
        page = Page(request, self.page.page_name, rev=rev)
paul@15 49
        return page.get_raw_body()
paul@15 50
paul@0 51
    def get_form_html(self, buttons_html):
paul@0 52
        _ = self._
paul@0 53
        request = self.request
paul@0 54
        fmt = request.formatter
paul@18 55
paul@18 56
        if not is_queued_changes_page(request, self.pagename):
paul@18 57
            return fmt.paragraph(1) + fmt.text(_("This page does not show queued changes.")) + fmt.paragraph(0)
paul@18 58
paul@15 59
        rev = self.get_revision()
paul@15 60
paul@15 61
        # Get information about the queued changes.
paul@15 62
paul@15 63
        body = self.get_body(rev)
paul@15 64
        _body, directives = remove_directives(body, ["acl", "parent-revision", "unapproved-user"])
paul@15 65
paul@15 66
        # Get the target page's parent revision for the queued changes.
paul@15 67
paul@15 68
        target_page_name = get_target_page_name(self.pagename)
paul@15 69
        target_page = PageEditor(request, target_page_name)
paul@15 70
paul@15 71
        current_rev = target_page.current_rev()
paul@15 72
        parent_rev = int(directives.get("parent-revision", current_rev))
paul@1 73
paul@21 74
        # Get the user who submitted the changes.
paul@21 75
paul@21 76
        username = directives.get("unapproved-user")
paul@21 77
paul@1 78
        d = {
paul@21 79
            "approval_label"        : escape(_("Make %s an approved user") % username),
paul@1 80
            "buttons_html"          : buttons_html,
paul@1 81
            "prompt"                : escape(_("Approve the displayed page version?")),
paul@15 82
            "rev"                   : escattr(rev),
paul@15 83
            "notice"                : escape(_("The affected page has been edited since the queued changes were made.")),
paul@1 84
            }
paul@1 85
paul@1 86
        # Prepare the output HTML.
paul@1 87
paul@1 88
        html = '''
paul@1 89
<table>
paul@1 90
    <tr>
paul@4 91
        <td>%(prompt)s</td>
paul@15 92
    </tr>''' % d
paul@15 93
paul@15 94
        if parent_rev != current_rev:
paul@15 95
            html += '''
paul@15 96
    <tr>
paul@15 97
        <td><em>%(notice)s</em></td>
paul@15 98
    </tr>''' % d
paul@15 99
paul@21 100
        if username and not user_is_approved(request, username):
paul@21 101
            html += '''
paul@21 102
    <tr>
paul@21 103
        <td><input name="approve" type="checkbox" value="true" /> %(approval_label)s</td>
paul@21 104
    </tr>''' % d
paul@21 105
paul@15 106
        html += '''
paul@1 107
    <tr>
paul@1 108
        <td class="buttons">
paul@1 109
            %(buttons_html)s
paul@1 110
        </td>
paul@1 111
    </tr>
paul@1 112
</table>''' % d
paul@1 113
paul@7 114
        if rev:
paul@7 115
            html += '''
paul@15 116
<input name="rev" type="hidden" value="%(rev)s" />''' % d
paul@7 117
paul@1 118
        return html
paul@0 119
paul@0 120
    def do_action(self):
paul@0 121
paul@0 122
        "Approve the page and move it into place."
paul@0 123
paul@0 124
        _ = self._
paul@0 125
        request = self.request
paul@21 126
        form = get_form(request)
paul@0 127
paul@0 128
        # Make sure that only suitably privileged users can perform this action.
paul@0 129
paul@3 130
        queued_changes_page = get_queued_changes_page(request)
paul@0 131
paul@4 132
        if not is_reviewer(request):
paul@0 133
            return 0, _("Only page reviewers can perform this action.")
paul@0 134
paul@0 135
        # Edit the target page, using this page's content.
paul@1 136
        # The current page must be a queued page version.
paul@0 137
paul@3 138
        if not is_queued_changes_page(request, self.pagename):
paul@1 139
            return 0, _("This page is not queued for approval.")
paul@1 140
paul@11 141
        # First, the displayed revision of the queued changes page must be
paul@11 142
        # retrieved.
paul@4 143
paul@15 144
        rev = self.get_revision()
paul@15 145
        body = self.get_body(rev)
paul@10 146
paul@11 147
        # Remove any introduced directives.
paul@11 148
paul@14 149
        body, directives = remove_directives(body, ["acl", "parent-revision", "unapproved-user"])
paul@11 150
paul@11 151
        # Get the target page's parent revision for the queued changes.
paul@11 152
paul@11 153
        target_page_name = get_target_page_name(self.pagename)
paul@11 154
        target_page = PageEditor(request, target_page_name)
paul@11 155
paul@11 156
        current_rev = target_page.current_rev()
paul@11 157
        parent_rev = int(directives.get("parent-revision", current_rev))
paul@11 158
paul@21 159
        # Get the user who submitted the changes.
paul@21 160
paul@21 161
        username = directives.get("unapproved-user")
paul@21 162
paul@21 163
        # Approve the user if requested, regardless of what happens below.
paul@21 164
paul@21 165
        if username and form.get("approve", ["false"])[0] == "true":
paul@21 166
            add_to_group_page(request, username, get_approved_editors_group(request))
paul@21 167
paul@11 168
        # Where the parent revision differs from the current revision of the
paul@11 169
        # page, attempt to merge the changes.
paul@11 170
paul@12 171
        conflict = False
paul@11 172
paul@11 173
        if parent_rev != current_rev:
paul@10 174
paul@11 175
            # The body of the parent revision of the target page, along with the
paul@11 176
            # body of the current revision must be acquired.
paul@11 177
paul@11 178
            parent_body = Page(request, target_page_name, rev=parent_rev).get_raw_body()
paul@11 179
            current_body = target_page.get_raw_body()
paul@11 180
paul@11 181
            # The parent, current and queued texts must then be merged.
paul@11 182
paul@11 183
            body = diff3.text_merge(parent_body, current_body, body, True, *conflict_markers)
paul@11 184
paul@11 185
            # Look for conflict markers and redirect to edit mode on the
paul@11 186
            # resulting page if they are present.
paul@11 187
paul@11 188
            for marker in conflict_markers:
paul@11 189
                if body.find(marker) != -1:
paul@12 190
                    conflict = True
paul@11 191
                    break
paul@11 192
paul@11 193
        # Delete the queued changes page.
paul@3 194
        # NOTE: The page could be deleted completely or certain revisions
paul@3 195
        # NOTE: purged.
paul@3 196
        # NOTE: (to-do/proper-queued-page-deletion.txt)
paul@0 197
paul@0 198
        current_page = PageEditor(request, self.pagename)
paul@0 199
        current_page.deletePage(_("Changes to page approved."))
paul@0 200
paul@12 201
        # Prepare a comment.
paul@12 202
paul@12 203
        comment = _("Changes to page approved from queue revision %d.") % rev
paul@12 204
paul@12 205
        # Save the target page, but only if there is no conflict.
paul@12 206
paul@12 207
        if not conflict:
paul@14 208
paul@14 209
            # Switch user if a specific user was recorded.
paul@14 210
paul@14 211
            if username:
paul@14 212
                new_user = get_user(request, username)
paul@14 213
            else:
paul@14 214
                new_user = None
paul@14 215
paul@14 216
            if new_user:
paul@14 217
                user = request.user
paul@14 218
                request.user = new_user
paul@14 219
paul@14 220
            # Save the page.
paul@14 221
paul@12 222
            try:
paul@14 223
                try:
paul@14 224
                    target_page.saveText(body, 0, comment=comment)
paul@14 225
                except PageEditor.Unchanged:
paul@14 226
                    pass
paul@14 227
paul@14 228
            # Restore the user.
paul@14 229
paul@14 230
            finally:
paul@14 231
                if new_user:
paul@14 232
                    request.user = user
paul@12 233
paul@12 234
            # Redirect to the target page.
paul@0 235
paul@12 236
            request.http_redirect(target_page.url(request))
paul@12 237
paul@12 238
        # Otherwise, send the page editor.
paul@12 239
        # NOTE: Replacing the revision in the request to prevent Moin from
paul@12 240
        # NOTE: attempting to use the queued changes page's revision.
paul@12 241
        # NOTE: Replacing the action and page in the request to avoid issues
paul@12 242
        # NOTE: with editing tickets.
paul@12 243
paul@12 244
        else:
paul@12 245
            request.rev = current_rev
paul@12 246
            request.action = "edit"
paul@12 247
            request.page = target_page
paul@12 248
            target_page.sendEditor(preview=body, comment=comment, staytop=True)
paul@12 249
paul@0 250
        return 1, None
paul@0 251
paul@0 252
    def render_success(self, msg, msgtype):
paul@0 253
paul@0 254
        """
paul@0 255
        Render neither 'msg' nor 'msgtype' since redirection should occur
paul@0 256
        instead.
paul@0 257
        NOTE: msgtype is optional because MoinMoin 1.5.x does not support it.
paul@0 258
        """
paul@0 259
paul@0 260
        pass
paul@0 261
paul@0 262
# Action function.
paul@0 263
paul@0 264
def execute(pagename, request):
paul@0 265
    ApproveChanges(pagename, request).render()
paul@0 266
paul@0 267
# vim: tabstop=4 expandtab shiftwidth=4