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@0 | 34 | def get_form_html(self, buttons_html): |
paul@0 | 35 | _ = self._ |
paul@0 | 36 | request = self.request |
paul@0 | 37 | fmt = request.formatter |
paul@7 | 38 | form = get_form(request) |
paul@7 | 39 | rev = form.get("rev") |
paul@1 | 40 | |
paul@1 | 41 | d = { |
paul@1 | 42 | "buttons_html" : buttons_html, |
paul@1 | 43 | "prompt" : escape(_("Approve the displayed page version?")), |
paul@1 | 44 | } |
paul@1 | 45 | |
paul@1 | 46 | # Prepare the output HTML. |
paul@1 | 47 | |
paul@1 | 48 | html = ''' |
paul@1 | 49 | <table> |
paul@1 | 50 | <tr> |
paul@4 | 51 | <td>%(prompt)s</td> |
paul@1 | 52 | </tr> |
paul@1 | 53 | <tr> |
paul@1 | 54 | <td class="buttons"> |
paul@1 | 55 | %(buttons_html)s |
paul@1 | 56 | </td> |
paul@1 | 57 | </tr> |
paul@1 | 58 | </table>''' % d |
paul@1 | 59 | |
paul@7 | 60 | if rev: |
paul@7 | 61 | html += ''' |
paul@7 | 62 | <input name="rev" type="hidden" value="%(rev)s" />''' % {"rev" : escattr(rev[0])} |
paul@7 | 63 | |
paul@1 | 64 | return html |
paul@0 | 65 | |
paul@0 | 66 | def do_action(self): |
paul@0 | 67 | |
paul@0 | 68 | "Approve the page and move it into place." |
paul@0 | 69 | |
paul@0 | 70 | _ = self._ |
paul@0 | 71 | request = self.request |
paul@0 | 72 | |
paul@0 | 73 | # Make sure that only suitably privileged users can perform this action. |
paul@0 | 74 | |
paul@3 | 75 | queued_changes_page = get_queued_changes_page(request) |
paul@0 | 76 | |
paul@4 | 77 | if not is_reviewer(request): |
paul@0 | 78 | return 0, _("Only page reviewers can perform this action.") |
paul@0 | 79 | |
paul@0 | 80 | # Edit the target page, using this page's content. |
paul@1 | 81 | # The current page must be a queued page version. |
paul@0 | 82 | |
paul@3 | 83 | if not is_queued_changes_page(request, self.pagename): |
paul@1 | 84 | return 0, _("This page is not queued for approval.") |
paul@1 | 85 | |
paul@11 | 86 | # First, the displayed revision of the queued changes page must be |
paul@11 | 87 | # retrieved. |
paul@4 | 88 | |
paul@7 | 89 | form = get_form(request) |
paul@7 | 90 | |
paul@7 | 91 | # Get the revision or None. |
paul@7 | 92 | |
paul@7 | 93 | rev = form.get("rev") |
paul@7 | 94 | if rev is None: |
paul@7 | 95 | rev = self.page.current_rev() |
paul@7 | 96 | else: |
paul@7 | 97 | rev = int(rev[0]) |
paul@7 | 98 | |
paul@7 | 99 | page = Page(request, self.page.page_name, rev=rev) |
paul@7 | 100 | body = page.get_raw_body() |
paul@10 | 101 | |
paul@11 | 102 | # Remove any introduced directives. |
paul@11 | 103 | |
paul@11 | 104 | body, directives = remove_directives(body, ["acl", "parent-revision"]) |
paul@11 | 105 | |
paul@11 | 106 | # Get the target page's parent revision for the queued changes. |
paul@11 | 107 | |
paul@11 | 108 | target_page_name = get_target_page_name(self.pagename) |
paul@11 | 109 | target_page = PageEditor(request, target_page_name) |
paul@11 | 110 | |
paul@11 | 111 | current_rev = target_page.current_rev() |
paul@11 | 112 | parent_rev = int(directives.get("parent-revision", current_rev)) |
paul@11 | 113 | |
paul@11 | 114 | # Where the parent revision differs from the current revision of the |
paul@11 | 115 | # page, attempt to merge the changes. |
paul@11 | 116 | |
paul@11 | 117 | query = {} |
paul@11 | 118 | |
paul@11 | 119 | if parent_rev != current_rev: |
paul@10 | 120 | |
paul@11 | 121 | # The body of the parent revision of the target page, along with the |
paul@11 | 122 | # body of the current revision must be acquired. |
paul@11 | 123 | |
paul@11 | 124 | parent_body = Page(request, target_page_name, rev=parent_rev).get_raw_body() |
paul@11 | 125 | current_body = target_page.get_raw_body() |
paul@11 | 126 | |
paul@11 | 127 | # The parent, current and queued texts must then be merged. |
paul@11 | 128 | |
paul@11 | 129 | body = diff3.text_merge(parent_body, current_body, body, True, *conflict_markers) |
paul@11 | 130 | |
paul@11 | 131 | # Look for conflict markers and redirect to edit mode on the |
paul@11 | 132 | # resulting page if they are present. |
paul@11 | 133 | |
paul@11 | 134 | for marker in conflict_markers: |
paul@11 | 135 | if body.find(marker) != -1: |
paul@11 | 136 | query = {'action' : 'edit'} |
paul@11 | 137 | break |
paul@11 | 138 | |
paul@11 | 139 | # Save the target page. |
paul@0 | 140 | |
paul@0 | 141 | try: |
paul@7 | 142 | target_page.saveText(body, 0, comment=_("Changes to page approved from queue revision %d.") % rev) |
paul@0 | 143 | except PageEditor.Unchanged: |
paul@0 | 144 | pass |
paul@0 | 145 | |
paul@11 | 146 | # Delete the queued changes page. |
paul@3 | 147 | # NOTE: The page could be deleted completely or certain revisions |
paul@3 | 148 | # NOTE: purged. |
paul@3 | 149 | # NOTE: (to-do/proper-queued-page-deletion.txt) |
paul@0 | 150 | |
paul@0 | 151 | current_page = PageEditor(request, self.pagename) |
paul@0 | 152 | current_page.deletePage(_("Changes to page approved.")) |
paul@0 | 153 | |
paul@11 | 154 | # Redirect to the target page, potentially in editing mode. |
paul@0 | 155 | |
paul@11 | 156 | request.http_redirect(target_page.url(request, query)) |
paul@0 | 157 | return 1, None |
paul@0 | 158 | |
paul@0 | 159 | def render_success(self, msg, msgtype): |
paul@0 | 160 | |
paul@0 | 161 | """ |
paul@0 | 162 | Render neither 'msg' nor 'msgtype' since redirection should occur |
paul@0 | 163 | instead. |
paul@0 | 164 | NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. |
paul@0 | 165 | """ |
paul@0 | 166 | |
paul@0 | 167 | pass |
paul@0 | 168 | |
paul@0 | 169 | # Action function. |
paul@0 | 170 | |
paul@0 | 171 | def execute(pagename, request): |
paul@0 | 172 | ApproveChanges(pagename, request).render() |
paul@0 | 173 | |
paul@0 | 174 | # vim: tabstop=4 expandtab shiftwidth=4 |