1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - ApproveChanges library 4 5 This library relies on the existence of a user (by default 6 "ApprovalQueueUser") who has sufficient privileges to write pages with ACLs 7 to an approval queue (ACL permissions "write,admin"). 8 9 If users other than the superuser are to be able to edit pages freely, they 10 must be present in a group (by default "ApprovedGroup"), and if they are to 11 be allowed to review changes, they must be present in a different group (by 12 default "PageReviewersGroup"). 13 14 @copyright: 2011 by Paul Boddie <paul@boddie.org.uk> 15 @license: GNU GPL (v2 or later), see COPYING.txt for details. 16 """ 17 18 from MoinMoin import user 19 from MoinMoin.Page import Page 20 from MoinMoin.wikiutil import escape 21 import re 22 23 __version__ = "0.1" 24 25 space_pattern = re.compile("(\s+)") 26 27 def get_queued_changes_page(request): 28 return getattr(request.cfg, "queued_changes_page", "ApprovalQueue") 29 30 def get_approved_editors_group(request): 31 return getattr(request.cfg, "approved_editors_group", "ApprovedGroup") 32 33 def get_page_reviewers_group(request): 34 return getattr(request.cfg, "reviewers_group", "PageReviewersGroup") 35 36 def get_queued_changes_user(request): 37 return getattr(request.cfg, "queued_changes_user", "ApprovalQueueUser") 38 39 def get_secret_key(request): 40 return request.cfg.secrets["wikiutil/tickets"] 41 42 def is_reviewer(request): 43 return request.user.valid and ( 44 has_member(request, get_approved_editors_group(request), request.user.name) or \ 45 request.user.isSuperUser()) 46 47 def is_approved(request): 48 return request.user.valid and ( 49 has_member(request, get_approved_editors_group(request), request.user.name) or \ 50 request.user.isSuperUser()) 51 52 def is_queued_changes_user(request): 53 return request.user.valid and request.user.name == get_queued_changes_user(request) 54 55 def is_queued_changes_page(request, pagename): 56 57 "Return whether 'pagename' is a queued changes page by testing its name." 58 59 parts = pagename.split("/") 60 return len(parts) > 1 and parts[-1] == get_queued_changes_page(request) 61 62 def get_target_page_name(pagename): 63 64 "Return the target page name for the given queued changes 'pagename'." 65 66 return "/".join(pagename.split("/")[:-1]) 67 68 def get_user_for_saving(request): 69 70 "Return a user that can save pages with ACLs." 71 72 username = get_queued_changes_user(request) 73 74 # If the user does not exist, just return the existing user. 75 76 return get_user(request, username) or request.user 77 78 def get_user(request, username): 79 80 "Return the user having the given 'username'." 81 82 uid = user.getUserId(request, username) 83 84 # If the user does not exist, just return None. 85 86 if not uid: 87 return None 88 89 # Otherwise, return the privileged user. 90 91 return user.User(request, uid) 92 93 def get_parent_revision_directive(request, pagename): 94 95 """ 96 Using the 'request', return a parent page revision directive for the page 97 having the given 'pagename'. 98 """ 99 100 page = Page(request, pagename) 101 return "#parent-revision %s" % page.current_rev() 102 103 def get_access_control_directive(request): 104 105 """ 106 Using the 'request', return an ACL directive for use in a page body in order 107 to prevent anyone other than reviewers from seeing it in the queue. 108 """ 109 110 return "#acl %s:read,write,delete,revert,admin All:" % ( 111 get_page_reviewers_group(request)) 112 113 def get_user_directive(request): 114 115 """ 116 Using the 'request', return a user directive for use in a page body in order 117 to record who saved the changes originally. 118 """ 119 120 if request.user.valid: 121 return "#unapproved-user %s" % request.user.name 122 else: 123 return "" 124 125 def add_directives(body, directives): 126 127 "Add to the page 'body' the given 'directives'." 128 129 return "\n".join(directives + [body]) 130 131 def remove_directives(body, names): 132 133 """ 134 Return a new page body, copying the page 'body' provided but removing the 135 first of each directive having one of the given 'names', along with a 136 dictionary mapping directive names to values. 137 """ 138 139 new_body = [] 140 header = 1 141 found = {} 142 143 for line in body.split("\n"): 144 if header: 145 146 # Detect the end of the header. 147 148 if not line.startswith("#"): 149 header = 0 150 151 # Process the comment or directive. 152 153 else: 154 parts = space_pattern.split(line[1:]) 155 156 # Identify any directive. 157 158 directive = parts[0] 159 160 # Obtain the value of the first instance of any directive, 161 # stripping any initial space. 162 163 if directive in names and not found.has_key(directive): 164 found[directive] = "".join(parts[2:]) 165 continue 166 167 new_body.append(line) 168 169 return "\n".join(new_body), found 170 171 # Utility classes and associated functions. 172 # NOTE: These are a subset of EventAggregatorSupport. 173 174 class Form: 175 176 """ 177 A wrapper preserving MoinMoin 1.8.x (and earlier) behaviour in a 1.9.x 178 environment. 179 """ 180 181 def __init__(self, form): 182 self.form = form 183 184 def get(self, name, default=None): 185 values = self.form.getlist(name) 186 if not values: 187 return default 188 else: 189 return values 190 191 def __getitem__(self, name): 192 return self.form.getlist(name) 193 194 class ActionSupport: 195 196 """ 197 Work around disruptive MoinMoin changes in 1.9, and also provide useful 198 convenience methods. 199 """ 200 201 def get_form(self): 202 return get_form(self.request) 203 204 def get_form(request): 205 206 "Work around disruptive MoinMoin changes in 1.9." 207 208 if hasattr(request, "values"): 209 return Form(request.values) 210 else: 211 return request.form 212 213 def escattr(s): 214 return escape(s, 1) 215 216 # More Moin 1.9 compatibility functions. 217 218 def has_member(request, groupname, username): 219 if hasattr(request.dicts, "has_member"): 220 return request.dicts.has_member(groupname, username) 221 else: 222 return username in request.dicts.get(groupname, []) 223 224 # vim: tabstop=4 expandtab shiftwidth=4