blob: bf68c5f9f6dbda4bc4e365b63fe8c8768421ddab [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 for the user profile page ("my page")."""
7from __future__ import print_function
8from __future__ import division
9from __future__ import absolute_import
10
11import logging
12import time
13import json
14
15import ezt
16
17import settings
18from businesslogic import work_env
19from framework import framework_helpers
20from framework import framework_views
21from framework import permissions
22from framework import servlet
23from framework import timestr
24from framework import xsrf
25from project import project_views
26from sitewide import sitewide_helpers
27
28
29class UserProfile(servlet.Servlet):
30 """Shows a page of information about a user."""
31
32 _PAGE_TEMPLATE = 'sitewide/user-profile-page.ezt'
33
34 def GatherPageData(self, mr):
35 """Build up a dictionary of data values to use when rendering the page."""
36 viewed_user = mr.viewed_user_auth.user_pb
37 if self.services.usergroup.GetGroupSettings(
38 mr.cnxn, mr.viewed_user_auth.user_id):
39 url = framework_helpers.FormatAbsoluteURL(
40 mr, '/g/%s/' % viewed_user.email, include_project=False)
41 self.redirect(url, abort=True) # Show group page instead.
42
43 with work_env.WorkEnv(mr, self.services) as we:
44 project_lists = we.GetUserProjects(mr.viewed_user_auth.effective_ids)
45
46 (visible_ownership, visible_archived, visible_membership,
47 visible_contrib) = project_lists
48
49 with mr.profiler.Phase('Getting user groups'):
50 group_settings = self.services.usergroup.GetAllGroupSettings(
51 mr.cnxn, mr.viewed_user_auth.effective_ids)
52 member_ids, owner_ids = self.services.usergroup.LookupAllMembers(
53 mr.cnxn, list(group_settings.keys()))
54 friend_project_ids = [] # TODO(issue 4202): implement this.
55 visible_group_ids = []
56 for group_id in group_settings:
57 if permissions.CanViewGroupMembers(
58 mr.perms, mr.auth.effective_ids, group_settings[group_id],
59 member_ids[group_id], owner_ids[group_id], friend_project_ids):
60 visible_group_ids.append(group_id)
61
62 user_group_views = framework_views.MakeAllUserViews(
63 mr.cnxn, self.services.user, visible_group_ids)
64 user_group_views = sorted(
65 list(user_group_views.values()), key=lambda ugv: ugv.email)
66
67 with mr.profiler.Phase('Getting linked accounts'):
68 linked_parent = None
69 linked_children = []
70 linked_views = framework_views.MakeAllUserViews(
71 mr.cnxn, self.services.user,
72 [viewed_user.linked_parent_id],
73 viewed_user.linked_child_ids)
74 if viewed_user.linked_parent_id:
75 linked_parent = linked_views[viewed_user.linked_parent_id]
76 if viewed_user.linked_child_ids:
77 linked_children = [
78 linked_views[child_id] for child_id in viewed_user.linked_child_ids]
79 offer_unlink = (mr.auth.user_id == viewed_user.user_id or
80 mr.auth.user_id in linked_views)
81
82 incoming_invite_users = []
83 outgoing_invite_users = []
84 possible_parent_accounts = []
85 can_edit_invites = mr.auth.user_id == mr.viewed_user_auth.user_id
86 display_link_invites = can_edit_invites or mr.auth.user_pb.is_site_admin
87 # TODO(jrobbins): allow site admin to edit invites for other users.
88 if display_link_invites:
89 with work_env.WorkEnv(mr, self.services, phase='Getting link invites'):
90 incoming_invite_ids, outgoing_invite_ids = we.GetPendingLinkedInvites(
91 user_id=viewed_user.user_id)
92 invite_views = framework_views.MakeAllUserViews(
93 mr.cnxn, self.services.user, incoming_invite_ids, outgoing_invite_ids)
94 incoming_invite_users = [
95 invite_views[uid] for uid in incoming_invite_ids]
96 outgoing_invite_users = [
97 invite_views[uid] for uid in outgoing_invite_ids]
98 possible_parent_accounts = _ComputePossibleParentAccounts(
99 we, mr.viewed_user_auth.user_view, linked_parent, linked_children)
100
101 viewed_user_display_name = framework_views.GetViewedUserDisplayName(mr)
102
103 with work_env.WorkEnv(mr, self.services) as we:
104 starred_projects = we.ListStarredProjects(
105 viewed_user_id=mr.viewed_user_auth.user_id)
106 logged_in_starred = we.ListStarredProjects()
107 logged_in_starred_pids = {p.project_id for p in logged_in_starred}
108
109 starred_user_ids = self.services.user_star.LookupStarredItemIDs(
110 mr.cnxn, mr.viewed_user_auth.user_id)
111 starred_user_dict = framework_views.MakeAllUserViews(
112 mr.cnxn, self.services.user, starred_user_ids)
113 starred_users = list(starred_user_dict.values())
114 starred_users_json = json.dumps(
115 [uv.display_name for uv in starred_users])
116
117 is_user_starred = self._IsUserStarred(
118 mr.cnxn, mr.auth.user_id, mr.viewed_user_auth.user_id)
119
120 if viewed_user.last_visit_timestamp:
121 last_visit_str = timestr.FormatRelativeDate(
122 viewed_user.last_visit_timestamp, days_only=True)
123 last_visit_str = last_visit_str or 'Less than 2 days ago'
124 else:
125 last_visit_str = 'Never'
126
127 if viewed_user.email_bounce_timestamp:
128 last_bounce_str = timestr.FormatRelativeDate(
129 viewed_user.email_bounce_timestamp, days_only=True)
130 last_bounce_str = last_bounce_str or 'Less than 2 days ago'
131 else:
132 last_bounce_str = None
133
134 can_ban = permissions.CanBan(mr, self.services)
135 viewed_user_is_spammer = viewed_user.banned.lower() == 'spam'
136 viewed_user_may_be_spammer = not viewed_user_is_spammer
137 all_projects = self.services.project.GetAllProjects(mr.cnxn)
138 for project_id in all_projects:
139 project = all_projects[project_id]
140 viewed_user_perms = permissions.GetPermissions(viewed_user,
141 mr.viewed_user_auth.effective_ids, project)
142 if (viewed_user_perms != permissions.EMPTY_PERMISSIONSET and
143 viewed_user_perms != permissions.USER_PERMISSIONSET):
144 viewed_user_may_be_spammer = False
145
146 ban_token = None
147 ban_spammer_token = None
148 if mr.auth.user_id and can_ban:
149 form_token_path = mr.request.path + 'ban.do'
150 ban_token = xsrf.GenerateToken(mr.auth.user_id, form_token_path)
151 form_token_path = mr.request.path + 'banSpammer.do'
152 ban_spammer_token = xsrf.GenerateToken(mr.auth.user_id, form_token_path)
153
154 can_delete_user = permissions.CanExpungeUsers(mr)
155
156 page_data = {
157 'user_tab_mode': 'st2',
158 'viewed_user_display_name': viewed_user_display_name,
159 'viewed_user_may_be_spammer': ezt.boolean(viewed_user_may_be_spammer),
160 'viewed_user_is_spammer': ezt.boolean(viewed_user_is_spammer),
161 'viewed_user_is_banned': ezt.boolean(viewed_user.banned),
162 'owner_of_projects': [
163 project_views.ProjectView(
164 p, starred=p.project_id in logged_in_starred_pids)
165 for p in visible_ownership],
166 'committer_of_projects': [
167 project_views.ProjectView(
168 p, starred=p.project_id in logged_in_starred_pids)
169 for p in visible_membership],
170 'contributor_to_projects': [
171 project_views.ProjectView(
172 p, starred=p.project_id in logged_in_starred_pids)
173 for p in visible_contrib],
174 'owner_of_archived_projects': [
175 project_views.ProjectView(p) for p in visible_archived],
176 'starred_projects': [
177 project_views.ProjectView(
178 p, starred=p.project_id in logged_in_starred_pids)
179 for p in starred_projects],
180 'starred_users': starred_users,
181 'starred_users_json': starred_users_json,
182 'is_user_starred': ezt.boolean(is_user_starred),
183 'viewing_user_page': ezt.boolean(True),
184 'last_visit_str': last_visit_str,
185 'last_bounce_str': last_bounce_str,
186 'vacation_message': viewed_user.vacation_message,
187 'can_ban': ezt.boolean(can_ban),
188 'ban_token': ban_token,
189 'ban_spammer_token': ban_spammer_token,
190 'user_groups': user_group_views,
191 'linked_parent': linked_parent,
192 'linked_children': linked_children,
193 'incoming_invite_users': incoming_invite_users,
194 'outgoing_invite_users': outgoing_invite_users,
195 'possible_parent_accounts': possible_parent_accounts,
196 'can_edit_invites': ezt.boolean(can_edit_invites),
197 'offer_unlink': ezt.boolean(offer_unlink),
198 'can_delete_user': ezt.boolean(can_delete_user),
199 }
200
201 viewed_user_prefs = None
202 if mr.perms.HasPerm(permissions.EDIT_OTHER_USERS, None, None):
203 with work_env.WorkEnv(mr, self.services) as we:
204 viewed_user_prefs = we.GetUserPrefs(mr.viewed_user_auth.user_id)
205
206 user_settings = (
207 framework_helpers.UserSettings.GatherUnifiedSettingsPageData(
208 mr.auth.user_id, mr.viewed_user_auth.user_view, viewed_user,
209 viewed_user_prefs))
210 page_data.update(user_settings)
211
212 return page_data
213
214 def _IsUserStarred(self, cnxn, logged_in_user_id, viewed_user_id):
215 """Return whether the logged in user starred the viewed user."""
216 if logged_in_user_id:
217 return self.services.user_star.IsItemStarredBy(
218 cnxn, viewed_user_id, logged_in_user_id)
219 return False
220
221 def ProcessFormData(self, mr, post_data):
222 """Process the posted form."""
223 has_admin_perm = mr.perms.HasPerm(permissions.EDIT_OTHER_USERS, None, None)
224 with work_env.WorkEnv(mr, self.services) as we:
225 framework_helpers.UserSettings.ProcessSettingsForm(
226 we, post_data, mr.viewed_user_auth.user_pb, admin=has_admin_perm)
227
228 # TODO(jrobbins): Check all calls to FormatAbsoluteURL for include_project.
229 return framework_helpers.FormatAbsoluteURL(
230 mr, mr.viewed_user_auth.user_view.profile_url, include_project=False,
231 saved=1, ts=int(time.time()))
232
233
234def _ComputePossibleParentAccounts(
235 we, user_view, linked_parent, linked_children):
236 """Return a list of email addresses of possible parent accounts."""
237 if not user_view:
238 return [] # Anon user cannot link to any account.
239 if linked_parent or linked_children:
240 return [] # If account is already linked in any way, don't offer.
241 possible_domains = settings.linkable_domains.get(user_view.domain, [])
242 possible_emails = ['%s@%s' % (user_view.username, domain)
243 for domain in possible_domains]
244 found_users, _ = we.ListReferencedUsers(possible_emails)
245 found_emails = [user.email for user in found_users]
246 return found_emails
247
248
249class UserProfilePolymer(UserProfile):
250 """New Polymer version of user profiles in Monorail."""
251
252 _PAGE_TEMPLATE = 'sitewide/user-profile-page-polymer.ezt'
253
254
255class BanUser(servlet.Servlet):
256 """Bans or un-bans a user."""
257
258 def ProcessFormData(self, mr, post_data):
259 """Process the posted form."""
260 if not permissions.CanBan(mr, self.services):
261 raise permissions.PermissionException(
262 "You do not have permission to ban users.")
263
264 framework_helpers.UserSettings.ProcessBanForm(
265 mr.cnxn, self.services.user, post_data, mr.viewed_user_auth.user_id,
266 mr.viewed_user_auth.user_pb)
267
268 # TODO(jrobbins): Check all calls to FormatAbsoluteURL for include_project.
269 return framework_helpers.FormatAbsoluteURL(
270 mr, mr.viewed_user_auth.user_view.profile_url, include_project=False,
271 saved=1, ts=int(time.time()))