Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(321)

Side by Side Diff: appengine/monorail/project/peopledetail.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/monorail/project/__init__.py ('k') | appengine/monorail/project/peoplelist.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium Authors. All rights reserved.
2 # Use of this source code is govered by a BSD-style
3 # license that can be found in the LICENSE file or at
4 # https://developers.google.com/open-source/licenses/bsd
5
6 """A class to display details about each project member."""
7
8 import logging
9 import time
10
11 from third_party import ezt
12
13 from framework import framework_bizobj
14 from framework import framework_helpers
15 from framework import framework_views
16 from framework import jsonfeed
17 from framework import monorailrequest
18 from framework import permissions
19 from framework import servlet
20 from framework import template_helpers
21 from framework import urls
22 from project import project_helpers
23 from project import project_views
24 from services import user_svc
25
26 CHECKBOX_PERMS = [
27 permissions.VIEW,
28 permissions.COMMIT,
29 permissions.CREATE_ISSUE,
30 permissions.ADD_ISSUE_COMMENT,
31 permissions.EDIT_ISSUE,
32 permissions.EDIT_ISSUE_OWNER,
33 permissions.EDIT_ISSUE_SUMMARY,
34 permissions.EDIT_ISSUE_STATUS,
35 permissions.EDIT_ISSUE_CC,
36 permissions.DELETE_ISSUE,
37 permissions.DELETE_OWN,
38 permissions.DELETE_ANY,
39 permissions.EDIT_ANY_MEMBER_NOTES,
40 permissions.MODERATE_SPAM,
41 ]
42
43
44 class PeopleDetail(servlet.Servlet):
45 """People detail page documents one partipant's involvement in a project."""
46
47 _PAGE_TEMPLATE = 'project/people-detail-page.ezt'
48 _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_PEOPLE
49
50 def AssertBasePermission(self, mr):
51 """Check that the user is allowed to access this servlet."""
52 super(PeopleDetail, self).AssertBasePermission(mr)
53 member_id = self.ValidateMemberID(mr.cnxn, mr.specified_user_id, mr.project)
54 # For now, contributors who cannot view other contributors are further
55 # restricted from viewing any part of the member list or detail pages.
56 if (not permissions.CanViewContributorList(mr) and
57 member_id != mr.auth.user_id):
58 raise permissions.PermissionException(
59 'User is not allowed to view other people\'s details')
60
61 def GatherPageData(self, mr):
62 """Build up a dictionary of data values to use when rendering the page."""
63
64 member_id = self.ValidateMemberID(mr.cnxn, mr.specified_user_id, mr.project)
65 users_by_id = framework_views.MakeAllUserViews(
66 mr.cnxn, self.services.user, [member_id])
67 framework_views.RevealAllEmailsToMembers(mr, users_by_id)
68
69 project_commitments = self.services.project.GetProjectCommitments(
70 mr.cnxn, mr.project_id)
71 member_view = project_views.MemberView(
72 mr.auth.user_id, member_id, users_by_id[member_id], mr.project,
73 project_commitments)
74
75 member_user = self.services.user.GetUser(mr.cnxn, member_id)
76 # This ignores indirect memberships, which is ok because we are viewing
77 # the page for a member directly involved in the project
78 role_perms = permissions.GetPermissions(
79 member_user, {member_id}, mr.project)
80
81 # TODO(jrobbins): clarify in the UI which permissions are built-in to
82 # the user's direct role, vs. which are granted via a group membership,
83 # vs. which ones are extra_perms that have been added specifically for
84 # this user.
85 member_perms = template_helpers.EZTItem()
86 for perm in CHECKBOX_PERMS:
87 setattr(member_perms, perm,
88 ezt.boolean(role_perms.HasPerm(perm, member_id, mr.project)))
89
90 displayed_extra_perms = [perm for perm in member_view.extra_perms
91 if perm not in CHECKBOX_PERMS]
92
93 viewing_self = mr.auth.user_id == member_id
94 warn_abandonment = (viewing_self and
95 permissions.ShouldCheckForAbandonment(mr))
96
97 return {
98 'subtab_mode': None,
99 'member': member_view,
100 'role_perms': role_perms,
101 'member_perms': member_perms,
102 'displayed_extra_perms': displayed_extra_perms,
103 'offer_edit_perms': ezt.boolean(self.CanEditPerms(mr)),
104 'offer_edit_member_notes': ezt.boolean(
105 self.CanEditMemberNotes(mr, member_id)),
106 'offer_remove_role': ezt.boolean(self.CanRemoveRole(mr, member_id)),
107 'expand_perms': ezt.boolean(mr.auth.user_pb.keep_people_perms_open),
108 'warn_abandonment': ezt.boolean(warn_abandonment),
109 'total_num_owners': len(mr.project.owner_ids),
110 }
111
112 def ValidateMemberID(self, cnxn, member_id, project):
113 """Lookup a project member by user_id.
114
115 Args:
116 cnxn: connection to SQL database.
117 member_id: int user_id, same format as user profile page.
118 project: the current Project PB.
119
120 Returns:
121 The user ID of the project member. Raises an exception if the username
122 cannot be looked up, or if that user is not in the project.
123 """
124 if not member_id:
125 self.abort(404, 'project member not specified')
126
127 member_username = None
128 try:
129 member_username = self.services.user.LookupUserEmail(cnxn, member_id)
130 except user_svc.NoSuchUserException:
131 logging.info('user_id %s not found', member_id)
132
133 if not member_username:
134 logging.info('There is no such user id %r', member_id)
135 self.abort(404, 'project member not found')
136
137 if not framework_bizobj.UserIsInProject(project, {member_id}):
138 logging.info('User %r is not a member of %r',
139 member_username, project.project_name)
140 self.abort(404, 'project member not found')
141
142 return member_id
143
144 def ProcessFormData(self, mr, post_data):
145 """Process the posted form."""
146 # 1. Parse and validate user input.
147 user_id, role, extra_perms, notes = self.ParsePersonData(mr, post_data)
148 member_id = self.ValidateMemberID(mr.cnxn, user_id, mr.project)
149
150 # 2. Call services layer to save changes.
151 if 'remove' in post_data:
152 self.ProcessRemove(mr, member_id)
153 else:
154 self.ProcessSave(mr, role, extra_perms, notes, member_id)
155
156 # 3. Determine the next page in the UI flow.
157 if 'remove' in post_data:
158 return framework_helpers.FormatAbsoluteURL(
159 mr, urls.PEOPLE_LIST, saved=1, ts=int(time.time()))
160 else:
161 return framework_helpers.FormatAbsoluteURL(
162 mr, urls.PEOPLE_DETAIL, u=user_id, saved=1, ts=int(time.time()))
163
164 def ProcessRemove(self, mr, member_id):
165 """Process the posted form when the user pressed 'Remove'."""
166 if not self.CanRemoveRole(mr, member_id):
167 raise permissions.PermissionException(
168 'User is not allowed to remove this member from the project')
169
170 self.RemoveRole(mr.cnxn, mr.project, member_id)
171
172 def ProcessSave(self, mr, role, extra_perms, notes, member_id):
173 """Process the posted form when the user pressed 'Save'."""
174 if not self.CanEditPerms(mr) and not self.CanEditMemberNotes(mr, member_id):
175 raise permissions.PermissionException(
176 'User is not allowed to edit people in this project')
177
178 if self.CanEditPerms(mr):
179 self.services.project.UpdateExtraPerms(
180 mr.cnxn, mr.project_id, member_id, extra_perms)
181 self.UpdateRole(mr.cnxn, mr.project, role, member_id)
182
183 if self.CanEditMemberNotes(mr, member_id):
184 self.services.project.UpdateCommitments(
185 mr.cnxn, mr.project_id, member_id, notes)
186
187 def CanEditMemberNotes(self, mr, member_id):
188 """Return true if the logged in user can edit the current user's notes."""
189 return (self.CheckPerm(mr, permissions.EDIT_ANY_MEMBER_NOTES) or
190 member_id == mr.auth.user_id)
191
192 def CanEditPerms(self, mr):
193 """Return true if the logged in user can edit the current user's perms."""
194 return self.CheckPerm(mr, permissions.EDIT_PROJECT)
195
196 def CanRemoveRole(self, mr, member_id):
197 """Return true if the logged in user can remove the current user's role."""
198 return (self.CheckPerm(mr, permissions.EDIT_PROJECT) or
199 member_id == mr.auth.user_id)
200
201 def ParsePersonData(self, mr, post_data):
202 """Parse the POST data for a project member.
203
204 Args:
205 mr: common information parsed from the user's request.
206 post_data: dictionary of lists of values for each HTML
207 form field.
208
209 Returns:
210 A tuple with user_id, role, extra_perms, and notes.
211 """
212 if not mr.specified_user_id:
213 raise monorailrequest.InputException('Field user_id is missing')
214
215 role = post_data.get('role', '').lower()
216 extra_perms = []
217 for ep in post_data.getall('extra_perms'):
218 perm = framework_bizobj.CanonicalizeLabel(ep)
219 # Perms with leading underscores are reserved.
220 perm = perm.strip('_')
221 if perm:
222 extra_perms.append(perm)
223
224 notes = post_data.get('notes', '').strip()
225 return mr.specified_user_id, role, extra_perms, notes
226
227 def RemoveRole(self, cnxn, project, member_id):
228 """Remove the given member from the project."""
229 owner_ids, committer_ids, contributor_ids = project_helpers.MembersWithout(
230 project, {member_id})
231 self.services.project.UpdateProjectRoles(
232 cnxn, project.project_id, owner_ids, committer_ids, contributor_ids)
233
234 def UpdateRole(self, cnxn, project, role, member_id):
235 """If the user's role was changed, update that in the Project."""
236 if not role:
237 return # Role was not in the form data
238
239 if role == framework_helpers.GetRoleName({member_id}, project).lower():
240 return # No change needed
241
242 owner_ids, committer_ids, contributor_ids = project_helpers.MembersWith(
243 project, {member_id}, role)
244
245 self.services.project.UpdateProjectRoles(
246 cnxn, project.project_id, owner_ids, committer_ids, contributor_ids)
247
248
249 class PagePrefs(jsonfeed.JsonFeed):
250 """Remember a user pref for hide/show state of people permissions."""
251
252 def HandleRequest(self, mr):
253 """Store the logged in user's preference for the people detail page."""
254 expanded = bool(mr.GetIntParam('perms_expanded'))
255 logging.info('setting expanded: %r', expanded)
256
257 if mr.auth.user_id:
258 self.services.user.UpdateUserSettings(
259 mr.cnxn, mr.auth.user_id, mr.auth.user_pb,
260 keep_people_perms_open=expanded)
261
262 return {'expanded': expanded}
OLDNEW
« no previous file with comments | « appengine/monorail/project/__init__.py ('k') | appengine/monorail/project/peoplelist.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698