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