Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/framework/framework_views.py b/framework/framework_views.py
new file mode 100644
index 0000000..17dead8
--- /dev/null
+++ b/framework/framework_views.py
@@ -0,0 +1,215 @@
+# 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
+
+"""View classes to make it easy to display framework objects in EZT."""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import logging
+import time
+
+import ezt
+
+from framework import framework_bizobj
+from framework import framework_constants
+from framework import framework_helpers
+from framework import permissions
+from framework import template_helpers
+from framework import timestr
+from proto import user_pb2
+from services import client_config_svc
+import settings
+
+
+_LABEL_DISPLAY_CHARS = 30
+_LABEL_PART_DISPLAY_CHARS = 15
+
+
+class LabelView(object):
+  """Wrapper class that makes it easier to display a label via EZT."""
+
+  def __init__(self, label, config):
+    """Make several values related to this label available as attrs.
+
+    Args:
+      label: artifact label string.  E.g., 'Priority-High' or 'Frontend'.
+      config: PB with a well_known_labels list, or None.
+    """
+    self.name = label
+    self.is_restrict = ezt.boolean(permissions.IsRestrictLabel(label))
+
+    self.docstring = ''
+    if config:
+      for wkl in config.well_known_labels:
+        if label.lower() == wkl.label.lower():
+          self.docstring = wkl.label_docstring
+
+    if '-' in label:
+      self.prefix, self.value = label.split('-', 1)
+    else:
+      self.prefix, self.value = '', label
+
+
+class StatusView(object):
+  """Wrapper class that makes it easier to display a status via EZT."""
+
+  def __init__(self, status, config):
+    """Make several values related to this status available as attrs.
+
+    Args:
+      status: artifact status string.  E.g., 'New' or 'Accepted'.
+      config: PB with a well_known_statuses list, or None.
+    """
+
+    self.name = status
+
+    self.docstring = ''
+    self.means_open = ezt.boolean(True)
+    if config:
+      for wks in config.well_known_statuses:
+        if status.lower() == wks.status.lower():
+          self.docstring = wks.status_docstring
+          self.means_open = ezt.boolean(wks.means_open)
+
+
+class UserView(object):
+  """Wrapper class to easily display basic user information in a template."""
+
+  def __init__(self, user, is_group=False):
+    self.user = user
+    self.is_group = is_group
+    email = user.email or ''
+    self.user_id = user.user_id
+    self.email = email
+    if user.obscure_email:
+      self.profile_url = '/u/%s/' % user.user_id
+    else:
+      self.profile_url = '/u/%s/' % email
+    self.obscure_email = user.obscure_email
+    self.banned = ''
+
+    (self.username, self.domain, self.obscured_username,
+     obscured_email) = framework_bizobj.ParseAndObscureAddress(email)
+    # No need to obfuscate or reveal client email.
+    # Instead display a human-readable username.
+    if self.user_id == framework_constants.DELETED_USER_ID:
+      self.display_name = framework_constants.DELETED_USER_NAME
+      self.obscure_email = ''
+      self.profile_url = ''
+    elif self.email in client_config_svc.GetServiceAccountMap():
+      self.display_name = client_config_svc.GetServiceAccountMap()[self.email]
+    elif not self.obscure_email:
+      self.display_name = email
+    else:
+      self.display_name = obscured_email
+
+    self.avail_message, self.avail_state = (
+        framework_helpers.GetUserAvailability(user, is_group))
+    self.avail_message_short = template_helpers.FitUnsafeText(
+        self.avail_message, 35)
+
+  def RevealEmail(self):
+    if not self.email:
+      return
+    if self.email not in client_config_svc.GetServiceAccountMap():
+      self.obscure_email = False
+      self.display_name = self.email
+      self.profile_url = '/u/%s/' % self.email
+
+
+def MakeAllUserViews(
+    cnxn, user_service, *list_of_user_id_lists, **kw):
+  """Make a dict {user_id: user_view, ...} for all user IDs given."""
+  distinct_user_ids = set()
+  distinct_user_ids.update(*list_of_user_id_lists)
+  if None in distinct_user_ids:
+    distinct_user_ids.remove(None)
+  group_ids = kw.get('group_ids', [])
+  user_dict = user_service.GetUsersByIDs(cnxn, distinct_user_ids)
+  return {user_id: UserView(user_pb, is_group=user_id in group_ids)
+          for user_id, user_pb in user_dict.items()}
+
+
+def MakeUserView(cnxn, user_service, user_id):
+  """Make a UserView for the given user ID."""
+  user = user_service.GetUser(cnxn, user_id)
+  return UserView(user)
+
+
+def StuffUserView(user_id, email, obscure_email):
+  """Construct a UserView with the given parameters for testing."""
+  user = user_pb2.MakeUser(user_id, email=email, obscure_email=obscure_email)
+  return UserView(user)
+
+
+# TODO(https://crbug.com/monorail/8192): Remove optional project.
+def RevealAllEmailsToMembers(cnxn, services, auth, users_by_id, project=None):
+  # type: (MonorailConnection, Services, AuthData, Collection[user_pb2.User],
+  #     Optional[project_pb2.Project] -> None)
+  """Reveal emails based on the authenticated user.
+
+  The actual behavior can be determined by looking into
+  framework_bizobj.ShouldRevealEmail. Look at https://crbug.com/monorail/8030
+  for context.
+  This method should be deleted when endpoints and ezt pages are deprecated.
+
+  Args:
+    cnxn: MonorailConnection to the database.
+    services: Services object for connections to backend services.
+    auth: AuthData object that identifies the logged in user.
+    users_by_id: dictionary of UserView's that might be displayed.
+    project: Optional Project PB for the current project.
+
+  Returns:
+    Nothing, but the UserViews in users_by_id may be modified to
+    publish email address.
+  """
+  if project:
+    for user_view in users_by_id.values():
+      if framework_bizobj.DeprecatedShouldRevealEmail(auth, project,
+                                                      user_view.email):
+        user_view.RevealEmail()
+  else:
+    viewable_users = framework_bizobj.FilterViewableEmails(
+        cnxn, services, auth, users_by_id.values())
+    for user_view in viewable_users:
+      user_view.RevealEmail()
+
+
+def RevealAllEmails(users_by_id):
+  """Allow anyone to see unobscured email addresses of project members.
+
+  The modified view objects should only be used to generate views for other
+  project members.
+
+  Args:
+    users_by_id: dictionary of UserViews that will be displayed.
+
+  Returns:
+    Nothing, but the UserViews in users_by_id may be modified to
+    publish email address.
+  """
+  for user_view in users_by_id.values():
+    user_view.RevealEmail()
+
+
+def GetViewedUserDisplayName(mr):
+  """Get display name of the viewed user given the logged-in user."""
+  # Do not obscure email if current user is a site admin. Do not obscure
+  # email if current user is viewing their own profile. For all other
+  # cases do whatever obscure_email setting for the user is.
+  viewing_self = mr.auth.user_id == mr.viewed_user_auth.user_id
+  email_obscured = (not(mr.auth.user_pb.is_site_admin or viewing_self)
+                    and mr.viewed_user_auth.user_view.obscure_email)
+  if email_obscured:
+    (_username, _domain, _obscured_username,
+     obscured_email) = framework_bizobj.ParseAndObscureAddress(
+         mr.viewed_user_auth.email)
+    viewed_user_display_name = obscured_email
+  else:
+    viewed_user_display_name = mr.viewed_user_auth.email
+
+  return viewed_user_display_name