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