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