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