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