Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/sitewide/userprofile.py b/sitewide/userprofile.py
new file mode 100644
index 0000000..bf68c5f
--- /dev/null
+++ b/sitewide/userprofile.py
@@ -0,0 +1,271 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""Classes for the user profile page ("my page")."""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import logging
+import time
+import json
+
+import ezt
+
+import settings
+from businesslogic import work_env
+from framework import framework_helpers
+from framework import framework_views
+from framework import permissions
+from framework import servlet
+from framework import timestr
+from framework import xsrf
+from project import project_views
+from sitewide import sitewide_helpers
+
+
+class UserProfile(servlet.Servlet):
+ """Shows a page of information about a user."""
+
+ _PAGE_TEMPLATE = 'sitewide/user-profile-page.ezt'
+
+ def GatherPageData(self, mr):
+ """Build up a dictionary of data values to use when rendering the page."""
+ viewed_user = mr.viewed_user_auth.user_pb
+ if self.services.usergroup.GetGroupSettings(
+ mr.cnxn, mr.viewed_user_auth.user_id):
+ url = framework_helpers.FormatAbsoluteURL(
+ mr, '/g/%s/' % viewed_user.email, include_project=False)
+ self.redirect(url, abort=True) # Show group page instead.
+
+ with work_env.WorkEnv(mr, self.services) as we:
+ project_lists = we.GetUserProjects(mr.viewed_user_auth.effective_ids)
+
+ (visible_ownership, visible_archived, visible_membership,
+ visible_contrib) = project_lists
+
+ with mr.profiler.Phase('Getting user groups'):
+ group_settings = self.services.usergroup.GetAllGroupSettings(
+ mr.cnxn, mr.viewed_user_auth.effective_ids)
+ member_ids, owner_ids = self.services.usergroup.LookupAllMembers(
+ mr.cnxn, list(group_settings.keys()))
+ friend_project_ids = [] # TODO(issue 4202): implement this.
+ visible_group_ids = []
+ for group_id in group_settings:
+ if permissions.CanViewGroupMembers(
+ mr.perms, mr.auth.effective_ids, group_settings[group_id],
+ member_ids[group_id], owner_ids[group_id], friend_project_ids):
+ visible_group_ids.append(group_id)
+
+ user_group_views = framework_views.MakeAllUserViews(
+ mr.cnxn, self.services.user, visible_group_ids)
+ user_group_views = sorted(
+ list(user_group_views.values()), key=lambda ugv: ugv.email)
+
+ with mr.profiler.Phase('Getting linked accounts'):
+ linked_parent = None
+ linked_children = []
+ linked_views = framework_views.MakeAllUserViews(
+ mr.cnxn, self.services.user,
+ [viewed_user.linked_parent_id],
+ viewed_user.linked_child_ids)
+ if viewed_user.linked_parent_id:
+ linked_parent = linked_views[viewed_user.linked_parent_id]
+ if viewed_user.linked_child_ids:
+ linked_children = [
+ linked_views[child_id] for child_id in viewed_user.linked_child_ids]
+ offer_unlink = (mr.auth.user_id == viewed_user.user_id or
+ mr.auth.user_id in linked_views)
+
+ incoming_invite_users = []
+ outgoing_invite_users = []
+ possible_parent_accounts = []
+ can_edit_invites = mr.auth.user_id == mr.viewed_user_auth.user_id
+ display_link_invites = can_edit_invites or mr.auth.user_pb.is_site_admin
+ # TODO(jrobbins): allow site admin to edit invites for other users.
+ if display_link_invites:
+ with work_env.WorkEnv(mr, self.services, phase='Getting link invites'):
+ incoming_invite_ids, outgoing_invite_ids = we.GetPendingLinkedInvites(
+ user_id=viewed_user.user_id)
+ invite_views = framework_views.MakeAllUserViews(
+ mr.cnxn, self.services.user, incoming_invite_ids, outgoing_invite_ids)
+ incoming_invite_users = [
+ invite_views[uid] for uid in incoming_invite_ids]
+ outgoing_invite_users = [
+ invite_views[uid] for uid in outgoing_invite_ids]
+ possible_parent_accounts = _ComputePossibleParentAccounts(
+ we, mr.viewed_user_auth.user_view, linked_parent, linked_children)
+
+ viewed_user_display_name = framework_views.GetViewedUserDisplayName(mr)
+
+ with work_env.WorkEnv(mr, self.services) as we:
+ starred_projects = we.ListStarredProjects(
+ viewed_user_id=mr.viewed_user_auth.user_id)
+ logged_in_starred = we.ListStarredProjects()
+ logged_in_starred_pids = {p.project_id for p in logged_in_starred}
+
+ starred_user_ids = self.services.user_star.LookupStarredItemIDs(
+ mr.cnxn, mr.viewed_user_auth.user_id)
+ starred_user_dict = framework_views.MakeAllUserViews(
+ mr.cnxn, self.services.user, starred_user_ids)
+ starred_users = list(starred_user_dict.values())
+ starred_users_json = json.dumps(
+ [uv.display_name for uv in starred_users])
+
+ is_user_starred = self._IsUserStarred(
+ mr.cnxn, mr.auth.user_id, mr.viewed_user_auth.user_id)
+
+ if viewed_user.last_visit_timestamp:
+ last_visit_str = timestr.FormatRelativeDate(
+ viewed_user.last_visit_timestamp, days_only=True)
+ last_visit_str = last_visit_str or 'Less than 2 days ago'
+ else:
+ last_visit_str = 'Never'
+
+ if viewed_user.email_bounce_timestamp:
+ last_bounce_str = timestr.FormatRelativeDate(
+ viewed_user.email_bounce_timestamp, days_only=True)
+ last_bounce_str = last_bounce_str or 'Less than 2 days ago'
+ else:
+ last_bounce_str = None
+
+ can_ban = permissions.CanBan(mr, self.services)
+ viewed_user_is_spammer = viewed_user.banned.lower() == 'spam'
+ viewed_user_may_be_spammer = not viewed_user_is_spammer
+ all_projects = self.services.project.GetAllProjects(mr.cnxn)
+ for project_id in all_projects:
+ project = all_projects[project_id]
+ viewed_user_perms = permissions.GetPermissions(viewed_user,
+ mr.viewed_user_auth.effective_ids, project)
+ if (viewed_user_perms != permissions.EMPTY_PERMISSIONSET and
+ viewed_user_perms != permissions.USER_PERMISSIONSET):
+ viewed_user_may_be_spammer = False
+
+ ban_token = None
+ ban_spammer_token = None
+ if mr.auth.user_id and can_ban:
+ form_token_path = mr.request.path + 'ban.do'
+ ban_token = xsrf.GenerateToken(mr.auth.user_id, form_token_path)
+ form_token_path = mr.request.path + 'banSpammer.do'
+ ban_spammer_token = xsrf.GenerateToken(mr.auth.user_id, form_token_path)
+
+ can_delete_user = permissions.CanExpungeUsers(mr)
+
+ page_data = {
+ 'user_tab_mode': 'st2',
+ 'viewed_user_display_name': viewed_user_display_name,
+ 'viewed_user_may_be_spammer': ezt.boolean(viewed_user_may_be_spammer),
+ 'viewed_user_is_spammer': ezt.boolean(viewed_user_is_spammer),
+ 'viewed_user_is_banned': ezt.boolean(viewed_user.banned),
+ 'owner_of_projects': [
+ project_views.ProjectView(
+ p, starred=p.project_id in logged_in_starred_pids)
+ for p in visible_ownership],
+ 'committer_of_projects': [
+ project_views.ProjectView(
+ p, starred=p.project_id in logged_in_starred_pids)
+ for p in visible_membership],
+ 'contributor_to_projects': [
+ project_views.ProjectView(
+ p, starred=p.project_id in logged_in_starred_pids)
+ for p in visible_contrib],
+ 'owner_of_archived_projects': [
+ project_views.ProjectView(p) for p in visible_archived],
+ 'starred_projects': [
+ project_views.ProjectView(
+ p, starred=p.project_id in logged_in_starred_pids)
+ for p in starred_projects],
+ 'starred_users': starred_users,
+ 'starred_users_json': starred_users_json,
+ 'is_user_starred': ezt.boolean(is_user_starred),
+ 'viewing_user_page': ezt.boolean(True),
+ 'last_visit_str': last_visit_str,
+ 'last_bounce_str': last_bounce_str,
+ 'vacation_message': viewed_user.vacation_message,
+ 'can_ban': ezt.boolean(can_ban),
+ 'ban_token': ban_token,
+ 'ban_spammer_token': ban_spammer_token,
+ 'user_groups': user_group_views,
+ 'linked_parent': linked_parent,
+ 'linked_children': linked_children,
+ 'incoming_invite_users': incoming_invite_users,
+ 'outgoing_invite_users': outgoing_invite_users,
+ 'possible_parent_accounts': possible_parent_accounts,
+ 'can_edit_invites': ezt.boolean(can_edit_invites),
+ 'offer_unlink': ezt.boolean(offer_unlink),
+ 'can_delete_user': ezt.boolean(can_delete_user),
+ }
+
+ viewed_user_prefs = None
+ if mr.perms.HasPerm(permissions.EDIT_OTHER_USERS, None, None):
+ with work_env.WorkEnv(mr, self.services) as we:
+ viewed_user_prefs = we.GetUserPrefs(mr.viewed_user_auth.user_id)
+
+ user_settings = (
+ framework_helpers.UserSettings.GatherUnifiedSettingsPageData(
+ mr.auth.user_id, mr.viewed_user_auth.user_view, viewed_user,
+ viewed_user_prefs))
+ page_data.update(user_settings)
+
+ return page_data
+
+ def _IsUserStarred(self, cnxn, logged_in_user_id, viewed_user_id):
+ """Return whether the logged in user starred the viewed user."""
+ if logged_in_user_id:
+ return self.services.user_star.IsItemStarredBy(
+ cnxn, viewed_user_id, logged_in_user_id)
+ return False
+
+ def ProcessFormData(self, mr, post_data):
+ """Process the posted form."""
+ has_admin_perm = mr.perms.HasPerm(permissions.EDIT_OTHER_USERS, None, None)
+ with work_env.WorkEnv(mr, self.services) as we:
+ framework_helpers.UserSettings.ProcessSettingsForm(
+ we, post_data, mr.viewed_user_auth.user_pb, admin=has_admin_perm)
+
+ # TODO(jrobbins): Check all calls to FormatAbsoluteURL for include_project.
+ return framework_helpers.FormatAbsoluteURL(
+ mr, mr.viewed_user_auth.user_view.profile_url, include_project=False,
+ saved=1, ts=int(time.time()))
+
+
+def _ComputePossibleParentAccounts(
+ we, user_view, linked_parent, linked_children):
+ """Return a list of email addresses of possible parent accounts."""
+ if not user_view:
+ return [] # Anon user cannot link to any account.
+ if linked_parent or linked_children:
+ return [] # If account is already linked in any way, don't offer.
+ possible_domains = settings.linkable_domains.get(user_view.domain, [])
+ possible_emails = ['%s@%s' % (user_view.username, domain)
+ for domain in possible_domains]
+ found_users, _ = we.ListReferencedUsers(possible_emails)
+ found_emails = [user.email for user in found_users]
+ return found_emails
+
+
+class UserProfilePolymer(UserProfile):
+ """New Polymer version of user profiles in Monorail."""
+
+ _PAGE_TEMPLATE = 'sitewide/user-profile-page-polymer.ezt'
+
+
+class BanUser(servlet.Servlet):
+ """Bans or un-bans a user."""
+
+ def ProcessFormData(self, mr, post_data):
+ """Process the posted form."""
+ if not permissions.CanBan(mr, self.services):
+ raise permissions.PermissionException(
+ "You do not have permission to ban users.")
+
+ framework_helpers.UserSettings.ProcessBanForm(
+ mr.cnxn, self.services.user, post_data, mr.viewed_user_auth.user_id,
+ mr.viewed_user_auth.user_pb)
+
+ # TODO(jrobbins): Check all calls to FormatAbsoluteURL for include_project.
+ return framework_helpers.FormatAbsoluteURL(
+ mr, mr.viewed_user_auth.user_view.profile_url, include_project=False,
+ saved=1, ts=int(time.time()))