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