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 request.dicts.has_member(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 request.dicts.has_member(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 uid = user.getUserId(request, username) 74 75 # If the user does not exist, just return the existing user. 76 77 if not uid: 78 return request.user 79 80 # Otherwise, return the privileged user. 81 82 return user.User(request, uid) 83 84 def get_parent_revision_directive(request, pagename): 85 86 """ 87 Using the 'request', return a parent page revision directive for the page 88 having the given 'pagename'. 89 """ 90 91 page = Page(request, pagename) 92 return "#parent-revision %s" % page.current_rev() 93 94 def get_access_control_directive(request): 95 96 """ 97 Using the 'request', return an ACL directive for use in a page body in order 98 to prevent anyone other than reviewers from seeing it in the queue. 99 """ 100 101 return "#acl %s:read,write,delete,revert,admin All:" % ( 102 get_page_reviewers_group(request)) 103 104 def add_directives(body, directives): 105 106 "Add to the page 'body' the given 'directives'." 107 108 return "\n".join(directives + [body]) 109 110 def remove_directives(body, names): 111 112 """ 113 Return a new page body, copying the page 'body' provided but removing the 114 first of each directive having one of the given 'names', along with a 115 dictionary mapping directive names to values. 116 """ 117 118 new_body = [] 119 header = 1 120 found = {} 121 122 for line in body.split("\n"): 123 if header: 124 125 # Detect the end of the header. 126 127 if not line.startswith("#"): 128 header = 0 129 130 # Process the comment or directive. 131 132 else: 133 parts = space_pattern.split(line[1:]) 134 135 # Identify any directive. 136 137 directive = parts[0] 138 139 if directive in names and not found.has_key(directive): 140 found[directive] = "".join(parts[1:]) 141 continue 142 143 new_body.append(line) 144 145 return "\n".join(new_body), found 146 147 # Utility classes and associated functions. 148 # NOTE: These are a subset of EventAggregatorSupport. 149 150 class Form: 151 152 """ 153 A wrapper preserving MoinMoin 1.8.x (and earlier) behaviour in a 1.9.x 154 environment. 155 """ 156 157 def __init__(self, form): 158 self.form = form 159 160 def get(self, name, default=None): 161 values = self.form.getlist(name) 162 if not values: 163 return default 164 else: 165 return values 166 167 def __getitem__(self, name): 168 return self.form.getlist(name) 169 170 class ActionSupport: 171 172 """ 173 Work around disruptive MoinMoin changes in 1.9, and also provide useful 174 convenience methods. 175 """ 176 177 def get_form(self): 178 return get_form(self.request) 179 180 def get_form(request): 181 182 "Work around disruptive MoinMoin changes in 1.9." 183 184 if hasattr(request, "values"): 185 return Form(request.values) 186 else: 187 return request.form 188 189 def escattr(s): 190 return escape(s, 1) 191 192 # vim: tabstop=4 expandtab shiftwidth=4