Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Unified Diff: appengine/monorail/tracker/issueoptions.py

Issue 1868553004: Open Source Monorail (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « appengine/monorail/tracker/issuelistcsv.py ('k') | appengine/monorail/tracker/issueoriginal.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: appengine/monorail/tracker/issueoptions.py
diff --git a/appengine/monorail/tracker/issueoptions.py b/appengine/monorail/tracker/issueoptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..83019000cb6fac37b15e418554228371fef3a201
--- /dev/null
+++ b/appengine/monorail/tracker/issueoptions.py
@@ -0,0 +1,263 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is govered by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""JSON feed for issue autocomplete options."""
+
+import logging
+from third_party import ezt
+
+from framework import framework_helpers
+from framework import framework_views
+from framework import jsonfeed
+from framework import monorailrequest
+from framework import permissions
+from project import project_helpers
+from tracker import tracker_helpers
+from tracker import tracker_views
+
+
+# Here are some restriction labels to help people do the most common things
+# that they might want to do with restrictions.
+_FREQUENT_ISSUE_RESTRICTIONS = [
+ (permissions.VIEW, permissions.EDIT_ISSUE,
+ 'Only users who can edit the issue may access it'),
+ (permissions.ADD_ISSUE_COMMENT, permissions.EDIT_ISSUE,
+ 'Only users who can edit the issue may add comments'),
+ ]
+
+
+# These issue restrictions should be offered as examples whenever the project
+# does not have any custom permissions in use already.
+_EXAMPLE_ISSUE_RESTRICTIONS = [
+ (permissions.VIEW, 'CoreTeam',
+ 'Custom permission CoreTeam is needed to access'),
+ ]
+
+
+class IssueOptionsJSON(jsonfeed.JsonFeed):
+ """JSON data describing all issue statuses, labels, and members."""
+
+ def HandleRequest(self, mr):
+ """Provide the UI with info used in auto-completion.
+
+ Args:
+ mr: common information parsed from the HTTP request.
+
+ Returns:
+ Results dictionary in JSON format
+ """
+ # Issue options data can be cached separately in each user's browser. When
+ # the project changes, a new cached_content_timestamp is set and it will
+ # cause new requests to use a new URL.
+ self.SetCacheHeaders(self.response)
+
+ member_data = project_helpers.BuildProjectMembers(
+ mr.cnxn, mr.project, self.services.user)
+ owner_views = member_data['owners']
+ committer_views = member_data['committers']
+ contributor_views = member_data['contributors']
+
+ config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id)
+
+ open_statuses = []
+ closed_statuses = []
+ for wks in config.well_known_statuses:
+ if not wks.deprecated:
+ item = dict(name=wks.status, doc=wks.status_docstring)
+ if wks.means_open:
+ open_statuses.append(item)
+ else:
+ closed_statuses.append(item)
+
+ # TODO(jrobbins): restrictions on component definitions?
+ components = [{'name': cd.path, 'doc': cd.docstring}
+ for cd in config.component_defs if not cd.deprecated]
+
+ labels = []
+ field_names = [
+ fd.field_name for fd in config.field_defs if not fd.is_deleted]
+ non_masked_labels = tracker_helpers.LabelsNotMaskedByFields(
+ config, field_names)
+ for wkl in non_masked_labels:
+ if not wkl.commented:
+ item = dict(name=wkl.name, doc=wkl.docstring)
+ labels.append(item)
+
+ # TODO(jrobbins): omit fields that they don't have permission to view.
+ field_def_views = [
+ tracker_views.FieldDefView(fd, config)
+ for fd in config.field_defs
+ if not fd.is_deleted]
+ fields = [
+ dict(field_name=fdv.field_name, field_type=fdv.field_type,
+ field_id=fdv.field_id, needs_perm=fdv.needs_perm,
+ is_required=fdv.is_required, is_multivalued=fdv.is_multivalued,
+ choices=[dict(name=c.name, doc=c.docstring) for c in fdv.choices],
+ docstring=fdv.docstring)
+ for fdv in field_def_views]
+
+ frequent_restrictions = _FREQUENT_ISSUE_RESTRICTIONS[:]
+ custom_permissions = permissions.GetCustomPermissions(mr.project)
+ if not custom_permissions:
+ frequent_restrictions.extend(
+ _EXAMPLE_ISSUE_RESTRICTIONS)
+
+ labels.extend(_BuildRestrictionChoices(
+ mr.project, frequent_restrictions,
+ permissions.STANDARD_ISSUE_PERMISSIONS))
+
+ group_ids = self.services.usergroup.DetermineWhichUserIDsAreGroups(
+ mr.cnxn, [mem.user_id for mem in member_data['all_members']])
+ logging.info('group_ids is %r', group_ids)
+
+ # TODO(jrobbins): Normally, users will be allowed view the members
+ # of any user group if the project From: email address is listed
+ # as a group member, as well as any group that they are personally
+ # members of.
+ member_ids, owner_ids = self.services.usergroup.LookupVisibleMembers(
+ mr.cnxn, group_ids, mr.perms, mr.auth.effective_ids, self.services)
+ indirect_ids = set()
+ for gid in group_ids:
+ indirect_ids.update(member_ids.get(gid, []))
+ indirect_ids.update(owner_ids.get(gid, []))
+ indirect_user_ids = list(indirect_ids)
+ indirect_member_views = framework_views.MakeAllUserViews(
+ mr.cnxn, self.services.user, indirect_user_ids).values()
+
+ visible_member_views = _FilterMemberData(
+ mr, owner_views, committer_views, contributor_views,
+ indirect_member_views)
+ # Filter out servbice accounts
+ visible_member_views = [m for m in visible_member_views
+ if not framework_helpers.IsServiceAccount(m.email)]
+ visible_member_email_list = list({
+ uv.email for uv in visible_member_views})
+ user_indexes = {email: idx
+ for idx, email in enumerate(visible_member_email_list)}
+ visible_members_dict = {}
+ for uv in visible_member_views:
+ visible_members_dict[uv.email] = uv.user_id
+ group_ids = self.services.usergroup.DetermineWhichUserIDsAreGroups(
+ mr.cnxn, visible_members_dict.values())
+
+ for field_dict in fields:
+ needed_perm = field_dict['needs_perm']
+ if needed_perm:
+ qualified_user_indexes = []
+ for uv in visible_member_views:
+ # TODO(jrobbins): Similar code occurs in field_helpers.py.
+ user = self.services.user.GetUser(mr.cnxn, uv.user_id)
+ auth = monorailrequest.AuthData.FromUserID(
+ mr.cnxn, uv.user_id, self.services)
+ user_perms = permissions.GetPermissions(
+ user, auth.effective_ids, mr.project)
+ has_perm = user_perms.CanUsePerm(
+ needed_perm, auth.effective_ids, mr.project, [])
+ if has_perm:
+ qualified_user_indexes.append(user_indexes[uv.email])
+
+ field_dict['user_indexes'] = sorted(set(qualified_user_indexes))
+
+ excl_prefixes = [prefix.lower() for prefix in
+ config.exclusive_label_prefixes]
+ members_def_list = [dict(name=email, doc='')
+ for email in visible_member_email_list]
+ members_def_list = sorted(
+ members_def_list, key=lambda md: md['name'])
+ for md in members_def_list:
+ md_id = visible_members_dict[md['name']]
+ if md_id in group_ids:
+ md['is_group'] = True
+
+ return {
+ 'open': open_statuses,
+ 'closed': closed_statuses,
+ 'statuses_offer_merge': config.statuses_offer_merge,
+ 'components': components,
+ 'labels': labels,
+ 'fields': fields,
+ 'excl_prefixes': excl_prefixes,
+ 'strict': ezt.boolean(config.restrict_to_known),
+ 'members': members_def_list,
+ 'custom_permissions': custom_permissions,
+ }
+
+
+def _FilterMemberData(
+ mr, owner_views, committer_views, contributor_views,
+ indirect_member_views):
+ """Return a filtered list of members that the user can view.
+
+ In most projects, everyone can view the entire member list. But,
+ some projects are configured to only allow project owners to see
+ all members. In those projects, committers and contributors do not
+ see any contributors. Regardless of how the project is configured
+ or the role that the user plays in the current project, we include
+ any indirect members through user groups that the user has access
+ to view.
+
+ Args:
+ mr: Commonly used info parsed from the HTTP request.
+ owner_views: list of UserViews for project owners.
+ committer_views: list of UserViews for project committers.
+ contributor_views: list of UserViews for project contributors.
+ indirect_member_views: list of UserViews for users who have
+ an indirect role in the project via a user group, and that the
+ logged in user is allowed to see.
+
+ Returns:
+ A list of owners, committer and visible indirect members if the user is not
+ signed in. If the project is set to display contributors to non-owners or
+ the signed in user has necessary permissions then additionally a list of
+ contributors.
+ """
+ visible_members = []
+
+ # Everyone can view owners and committers
+ visible_members.extend(owner_views)
+ visible_members.extend(committer_views)
+
+ # The list of indirect members is already limited to ones that the user
+ # is allowed to see according to user group settings.
+ visible_members.extend(indirect_member_views)
+
+ # If the user is allowed to view the list of contributors, add those too.
+ if permissions.CanViewContributorList(mr):
+ visible_members.extend(contributor_views)
+
+ return visible_members
+
+
+def _BuildRestrictionChoices(project, freq_restrictions, actions):
+ """Return a list of autocompletion choices for restriction labels.
+
+ Args:
+ project: Project PB for the current project.
+ freq_restrictions: list of (action, perm, doc) tuples for restrictions
+ that are frequently used.
+ actions: list of strings for actions that are relevant to the current
+ artifact.
+
+ Returns:
+ A list of dictionaries [{'name': 'perm name', 'doc': 'docstring'}, ...]
+ suitable for use in a JSON feed to our JS autocompletion functions.
+ """
+ custom_permissions = permissions.GetCustomPermissions(project)
+ choices = []
+
+ for action, perm, doc in freq_restrictions:
+ choices.append({
+ 'name': 'Restrict-%s-%s' % (action, perm),
+ 'doc': doc,
+ })
+
+ for action in actions:
+ for perm in custom_permissions:
+ choices.append({
+ 'name': 'Restrict-%s-%s' % (action, perm),
+ 'doc': 'Permission %s needed to use %s' % (perm, action),
+ })
+
+ return choices
« no previous file with comments | « appengine/monorail/tracker/issuelistcsv.py ('k') | appengine/monorail/tracker/issueoriginal.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698