blob: 7bac917538ca6fec457c91fb5fe1a1e7fcf9edb6 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001# Copyright 2016 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5"""Classes to implement the hotlistpeople page and related forms."""
6from __future__ import print_function
7from __future__ import division
8from __future__ import absolute_import
9
10import logging
11import time
12
13import ezt
14
15from features import hotlist_helpers
16from features import hotlist_views
17from framework import framework_helpers
18from framework import framework_views
19from framework import paginate
20from framework import permissions
21from framework import servlet
22from framework import urls
23from project import project_helpers
24
25MEMBERS_PER_PAGE = 50
26
27
28class HotlistPeopleList(servlet.Servlet):
29 _PAGE_TEMPLATE = 'project/people-list-page.ezt'
30 # Note: using the project's peoplelist page template. minor edits were
31 # to make it compatible with HotlistPeopleList
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +010032 _MAIN_TAB_MODE = servlet.Servlet.HOTLIST_TAB_PEOPLE
Copybara854996b2021-09-07 19:36:02 +000033
34 def AssertBasePermission(self, mr):
35 super(HotlistPeopleList, self).AssertBasePermission(mr)
36 if not permissions.CanViewHotlist(
37 mr.auth.effective_ids, mr.perms, mr.hotlist):
38 raise permissions.PermissionException(
39 'User is now allowed to view the hotlist people list')
40
41 def GatherPageData(self, mr):
42 """Build up a dictionary of data values to use when rendering the page."""
43 if mr.auth.user_id:
44 self.services.user.AddVisitedHotlist(
45 mr.cnxn, mr.auth.user_id, mr.hotlist_id)
46
47 all_members = (mr.hotlist.owner_ids +
48 mr.hotlist.editor_ids + mr.hotlist.follower_ids)
49
50 hotlist_url = hotlist_helpers.GetURLOfHotlist(
51 mr.cnxn, mr.hotlist, self.services.user)
52
53 with mr.profiler.Phase('gathering members on this page'):
54 users_by_id = framework_views.MakeAllUserViews(
55 mr.cnxn, self.services.user, all_members)
56 framework_views.RevealAllEmailsToMembers(
57 mr.cnxn, self.services, mr.auth, users_by_id, mr.project)
58
59 untrusted_user_group_proxies = []
60 # TODO(jojwang): implement FindUntrustedGroups()
61
62 with mr.profiler.Phase('making member views'):
63 owner_views = self._MakeMemberViews(mr, mr.hotlist.owner_ids, users_by_id)
64 editor_views = self._MakeMemberViews(mr, mr.hotlist.editor_ids,
65 users_by_id)
66 follower_views = self._MakeMemberViews(mr, mr.hotlist.follower_ids,
67 users_by_id)
68 all_member_views = owner_views + editor_views + follower_views
69
70 url_params = [(name, mr.GetParam(name)) for name in
71 framework_helpers.RECOGNIZED_PARAMS]
72 # We are passing in None for the project_name because we are not operating
73 # under any project.
74 pagination = paginate.ArtifactPagination(
75 all_member_views, mr.GetPositiveIntParam('num', MEMBERS_PER_PAGE),
76 mr.GetPositiveIntParam('start'), None,
77 '%s%s' % (hotlist_url, urls.HOTLIST_PEOPLE), url_params=url_params)
78
79 offer_membership_editing = permissions.CanAdministerHotlist(
80 mr.auth.effective_ids, mr.perms, mr.hotlist)
81
82 offer_remove_self = (
83 not offer_membership_editing and
84 mr.auth.user_id and
85 mr.auth.user_id in mr.hotlist.editor_ids)
86
87 newly_added_views = [mv for mv in all_member_views
88 if str(mv.user.user_id) in mr.GetParam('new', [])]
89
90 return {
91 'is_hotlist': ezt.boolean(True),
92 'untrusted_user_groups': untrusted_user_group_proxies,
93 'pagination': pagination,
94 'initial_add_members': '',
95 'subtab_mode': None,
96 'initially_expand_form': ezt.boolean(False),
97 'newly_added_views': newly_added_views,
98 'offer_membership_editing': ezt.boolean(offer_membership_editing),
99 'offer_remove_self': ezt.boolean(offer_remove_self),
100 'total_num_owners': len(mr.hotlist.owner_ids),
101 'check_abandonment': ezt.boolean(True),
102 'initial_new_owner_username': '',
103 'placeholder': 'new-owner-username',
104 'open_dialog': ezt.boolean(False),
105 'viewing_user_page': ezt.boolean(True),
106 'new_ui_url': '%s/%s/people' % (urls.HOTLISTS, mr.hotlist_id),
107 }
108
109 def ProcessFormData(self, mr, post_data):
110 """Process the posted form."""
111 permit_edit = permissions.CanAdministerHotlist(
112 mr.auth.effective_ids, mr.perms, mr.hotlist)
113 can_remove_self = (
114 not permit_edit and
115 mr.auth.user_id and
116 mr.auth.user_id in mr.hotlist.editor_ids)
117 if not can_remove_self and not permit_edit:
118 raise permissions.PermissionException(
119 'User is not permitted to edit hotlist membership')
120 hotlist_url = hotlist_helpers.GetURLOfHotlist(
121 mr.cnxn, mr.hotlist, self.services.user)
122 if permit_edit:
123 if 'addbtn' in post_data:
124 return self.ProcessAddMembers(mr, post_data, hotlist_url)
125 elif 'removebtn' in post_data:
126 return self.ProcessRemoveMembers(mr, post_data, hotlist_url)
127 elif 'changeowners' in post_data:
128 return self.ProcessChangeOwnership(mr, post_data)
129 if can_remove_self:
130 if 'removeself' in post_data:
131 return self.ProcessRemoveSelf(mr, hotlist_url)
132
133 def _MakeMemberViews(self, mr, member_ids, users_by_id):
134 """Return a sorted list of MemberViews for display by EZT."""
135 member_views = [hotlist_views.MemberView(
136 mr.auth.user_id, member_id, users_by_id[member_id],
137 mr.hotlist) for member_id in member_ids]
138 member_views.sort(key=lambda mv: mv.user.email)
139 return member_views
140
141 def ProcessChangeOwnership(self, mr, post_data):
142 new_owner_id_set = project_helpers.ParseUsernames(
143 mr.cnxn, self.services.user, post_data.get('changeowners'))
144 remain_as_editor = post_data.get('becomeeditor') == 'on'
145 if len(new_owner_id_set) != 1:
146 mr.errors.transfer_ownership = (
147 'Please add one valid user email.')
148 else:
149 new_owner_id = new_owner_id_set.pop()
150 if self.services.features.LookupHotlistIDs(
151 mr.cnxn, [mr.hotlist.name], [new_owner_id]):
152 mr.errors.transfer_ownership = (
153 'This user already owns a hotlist with the same name')
154
155 if mr.errors.AnyErrors():
156 self.PleaseCorrect(
157 mr, initial_new_owner_username=post_data.get('changeowners'),
158 open_dialog=ezt.boolean(True))
159 else:
160 old_and_new_owner_ids = [new_owner_id] + mr.hotlist.owner_ids
161 (_, editor_ids, follower_ids) = hotlist_helpers.MembersWithoutGivenIDs(
162 mr.hotlist, old_and_new_owner_ids)
163 if remain_as_editor and mr.hotlist.owner_ids:
164 editor_ids.append(mr.hotlist.owner_ids[0])
165
166 self.services.features.UpdateHotlistRoles(
167 mr.cnxn, mr.hotlist_id, [new_owner_id], editor_ids, follower_ids)
168
169 hotlist = self.services.features.GetHotlist(mr.cnxn, mr.hotlist_id)
170 hotlist_url = hotlist_helpers.GetURLOfHotlist(
171 mr.cnxn, hotlist, self.services.user)
172 return framework_helpers.FormatAbsoluteURL(
173 mr,'%s%s' % (hotlist_url, urls.HOTLIST_PEOPLE),
174 saved=1, ts=int(time.time()),
175 include_project=False)
176
177 def ProcessAddMembers(self, mr, post_data, hotlist_url):
178 """Process the user's request to add members.
179
180 Args:
181 mr: common information parsed from the HTTP request.
182 post_data: dictionary of form data
183 hotlist_url: hotlist_url to return to after data has been processed.
184
185 Returns:
186 String URL to redirect the user to after processing
187 """
188 # NOTE: using project_helpers function
189 new_member_ids = project_helpers.ParseUsernames(
190 mr.cnxn, self.services.user, post_data.get('addmembers'))
191 if not new_member_ids or not post_data.get('addmembers'):
192 mr.errors.incorrect_email_input = (
193 'Please give full emails seperated by commas.')
194 role = post_data['role']
195
196 (owner_ids, editor_ids, follower_ids) = hotlist_helpers.MembersWithGivenIDs(
197 mr.hotlist, new_member_ids, role)
198 # TODO(jojwang): implement MAX_HOTLIST_PEOPLE
199
200 if not owner_ids:
201 mr.errors.addmembers = (
202 'Cannot have a hotlist without an owner; please leave at least one.')
203
204 if mr.errors.AnyErrors():
205 add_members_str = post_data.get('addmembers', '')
206 self.PleaseCorrect(
207 mr, initial_add_members=add_members_str, initially_expand_form=True)
208 else:
209 self.services.features.UpdateHotlistRoles(
210 mr.cnxn, mr.hotlist_id, owner_ids, editor_ids, follower_ids)
211 return framework_helpers.FormatAbsoluteURL(
212 mr, '%s%s' % (
213 hotlist_url, urls.HOTLIST_PEOPLE),
214 saved=1, ts=int(time.time()),
215 new=','.join([str(u) for u in new_member_ids]),
216 include_project=False)
217
218 def ProcessRemoveMembers(self, mr, post_data, hotlist_url):
219 """Process the user's request to remove members."""
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100220 remove_strs = post_data.getlist('remove')
Copybara854996b2021-09-07 19:36:02 +0000221 logging.info('remove_strs = %r', remove_strs)
222 remove_ids = set(
223 self.services.user.LookupUserIDs(mr.cnxn, remove_strs).values())
224 (owner_ids, editor_ids,
225 follower_ids) = hotlist_helpers.MembersWithoutGivenIDs(
226 mr.hotlist, remove_ids)
227
228 self.services.features.UpdateHotlistRoles(
229 mr.cnxn, mr.hotlist_id, owner_ids, editor_ids, follower_ids)
230
231 return framework_helpers.FormatAbsoluteURL(
232 mr, '%s%s' % (
233 hotlist_url, urls.HOTLIST_PEOPLE),
234 saved=1, ts=int(time.time()), include_project=False)
235
236 def ProcessRemoveSelf(self, mr, hotlist_url):
237 """Process the request to remove the logged-in user."""
238 remove_ids = [mr.auth.user_id]
239
240 # This function does no permission checking; that's done by the caller.
241 (owner_ids, editor_ids,
242 follower_ids) = hotlist_helpers.MembersWithoutGivenIDs(
243 mr.hotlist, remove_ids)
244
245 self.services.features.UpdateHotlistRoles(
246 mr.cnxn, mr.hotlist_id, owner_ids, editor_ids, follower_ids)
247
248 return framework_helpers.FormatAbsoluteURL(
249 mr, '%s%s' % (
250 hotlist_url, urls.HOTLIST_PEOPLE),
251 saved=1, ts=int(time.time()), include_project=False)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200252
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100253 def GetHotlistPeoplePage(self, **kwargs):
254 return self.handler(**kwargs)
Adrià Vilanova Martínezde942802022-07-15 14:06:55 +0200255
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +0100256 def PostHotlistPeoplePage(self, **kwargs):
257 return self.handler(**kwargs)