Example #1
0
def PaginateComments(mr, issue, issuecomment_list, config, services):
  """Filter and paginate the IssueComment PBs for the given issue.

  Unlike most pagination, this one starts at the end of the whole
  list so it shows only the most recent comments.  The user can use
  the "Older" and "Newer" links to page through older comments.

  Args:
    mr: common info parsed from the HTTP request.
    issue: Issue PB for the issue being viewed.
    issuecomment_list: list of IssueComment PBs for the viewed issue,
        the zeroth item in this list is the initial issue description.
    config: ProjectIssueConfig for the project that contains this issue.
    services: Connections to backend services.

  Returns:
    A tuple (descriptions, visible_comments, pagination), where descriptions
    is a list of IssueComment PBs for the issue description history,
    visible_comments is a list of IssueComment PBs for the comments that
    should be displayed on the current pagination page, and pagination is a
    VirtualPagination object that keeps track of the Older and Newer links.
  """
  if not issuecomment_list:
    return [], [], None

  # TODO(lukasperaza): update first comments' rows to is_description=TRUE
  # so [issuecomment_list[0]] can be removed
  descriptions = (
      [issuecomment_list[0]] +
      [comment for comment in issuecomment_list[1:] if comment.is_description])
  comments = issuecomment_list[1:]

  issue_perms = permissions.UpdateIssuePermissions(
      mr.perms, mr.project, issue, mr.auth.effective_ids, config=config)

  commenter_ids = set(c.user_id for c in comments)
  commenters = services.user.GetUsersByIDs(mr.cnxn, commenter_ids)

  allowed_comments = [
    c for c in comments
    if permissions.CanViewComment(
        c, commenters[c.user_id], mr.auth.user_id, issue_perms)]

  pagination_url = '%s?id=%d' % (urls.ISSUE_DETAIL, issue.local_id)
  url_params = [(name, mr.GetParam(name)) for name in
                framework_helpers.RECOGNIZED_PARAMS]
  pagination = paginate.VirtualPagination(
      len(allowed_comments),
      mr.GetPositiveIntParam(
          'cnum', framework_constants.DEFAULT_COMMENTS_PER_PAGE),
      mr.GetPositiveIntParam('cstart'),
      list_page_url=pagination_url, project_name=mr.project_name,
      count_up=False, start_param_name='cstart', num_param_name='cnum',
      max_num=settings.max_comments_per_page, url_params=url_params)
  if pagination.last == 1 and pagination.start == len(allowed_comments):
    pagination.visible = ezt.boolean(False)
  visible_comments = allowed_comments[
      pagination.last - 1:pagination.start]

  return descriptions, visible_comments, pagination
Example #2
0
    def ListComments(self, mc, request):
        """Return comments on the specified issue in a response proto."""
        project, issue, config = self._GetProjectIssueAndConfig(
            mc, request.issue_ref)
        with work_env.WorkEnv(mc, self.services) as we:
            comments = we.ListIssueComments(issue)
            _, comment_reporters = we.LookupIssueFlaggers(issue)

        with mc.profiler.Phase('making user views'):
            users_involved_in_comments = tracker_bizobj.UsersInvolvedInCommentList(
                comments)
            users_by_id = framework_views.MakeAllUserViews(
                mc.cnxn, self.services.user, users_involved_in_comments)
            framework_views.RevealAllEmailsToMembers(mc.auth, project,
                                                     users_by_id)

        with mc.profiler.Phase('converting to response objects'):
            issue_perms = permissions.UpdateIssuePermissions(
                mc.perms, project, issue, mc.auth.effective_ids, config=config)
            converted_comments = converters.ConvertCommentList(
                issue, comments, config, users_by_id, comment_reporters,
                mc.auth.user_id, issue_perms)
            response = issues_pb2.ListCommentsResponse(
                comments=converted_comments)

        return response
Example #3
0
def convert_approval_comment(issue, comment, mar, services, granted_perms):
    perms = permissions.UpdateIssuePermissions(mar.perms,
                                               mar.project,
                                               issue,
                                               mar.auth.effective_ids,
                                               granted_perms=granted_perms)
    commenter = services.user.GetUser(mar.cnxn, comment.user_id)
    can_delete = permissions.CanDeleteComment(comment, commenter,
                                              mar.auth.user_id, perms)

    return api_pb2_v1.ApprovalCommentWrapper(
        attachments=[convert_attachment(a) for a in comment.attachments],
        author=convert_person(comment.user_id,
                              mar.cnxn,
                              services,
                              trap_exception=True),
        canDelete=can_delete,
        content=comment.content,
        deletedBy=convert_person(comment.deleted_by,
                                 mar.cnxn,
                                 services,
                                 trap_exception=True),
        id=comment.sequence,
        published=datetime.datetime.fromtimestamp(comment.timestamp),
        approvalUpdates=convert_approval_amendments(comment.amendments, mar,
                                                    services),
        kind='monorail#approvalComment',
        is_description=comment.is_description)
Example #4
0
    def ListIssuePermissions(self, mc, request):
        """List the permissions for the current user in the given issue."""
        project, issue, config = self._GetProjectIssueAndConfig(
            mc, request.issue_ref, use_cache=False, view_deleted=True)

        perms = permissions.UpdateIssuePermissions(mc.perms,
                                                   project,
                                                   issue,
                                                   mc.auth.effective_ids,
                                                   config=config)

        return issues_pb2.ListIssuePermissionsResponse(
            permissions=sorted(perms.perm_names))
Example #5
0
def GetAttachmentIfAllowed(mr, services):
    """Retrieve the requested attachment, or raise an appropriate exception.

  Args:
    mr: commonly used info parsed from the request.
    services: connections to backend services.

  Returns:
    The requested Attachment PB, and the Issue that it belongs to.

  Raises:
    NoSuchAttachmentException: attachment was not found or was marked deleted.
    NoSuchIssueException: issue that contains attachment was not found.
    PermissionException: the user is not allowed to view the attachment.
  """
    attachment = None

    attachment, cid, issue_id = services.issue.GetAttachmentAndContext(
        mr.cnxn, mr.aid)

    issue = services.issue.GetIssue(mr.cnxn, issue_id)
    config = services.config.GetProjectConfig(mr.cnxn, issue.project_id)
    granted_perms = tracker_bizobj.GetGrantedPerms(issue,
                                                   mr.auth.effective_ids,
                                                   config)
    permit_view = permissions.CanViewIssue(mr.auth.effective_ids,
                                           mr.perms,
                                           mr.project,
                                           issue,
                                           granted_perms=granted_perms)
    if not permit_view:
        raise permissions.PermissionException(
            'Cannot view attachment\'s issue')

    comment = services.issue.GetComment(mr.cnxn, cid)
    commenter = services.user.GetUser(mr.cnxn, comment.user_id)
    issue_perms = permissions.UpdateIssuePermissions(
        mr.perms,
        mr.project,
        issue,
        mr.auth.effective_ids,
        granted_perms=granted_perms)
    can_view_comment = permissions.CanViewComment(comment, commenter,
                                                  mr.auth.user_id, issue_perms)
    if not can_view_comment:
        raise permissions.PermissionException(
            'Cannot view attachment\'s comment')

    return attachment, issue
Example #6
0
  def AssertBasePermission(self, mr):
    """Make sure that the logged in user has permission to view this page."""
    super(IssueOriginal, self).AssertBasePermission(mr)
    issue, comment = self._GetIssueAndComment(mr)

    # TODO(jrobbins): take granted perms into account here.
    if issue and not permissions.CanViewIssue(
        mr.auth.effective_ids, mr.perms, mr.project, issue,
        allow_viewing_deleted=True):
      raise permissions.PermissionException(
          'User is not allowed to view this issue')

    can_view_inbound_message = self.CheckPerm(
        mr, permissions.VIEW_INBOUND_MESSAGES, art=issue)
    issue_perms = permissions.UpdateIssuePermissions(
        mr.perms, mr.project, issue, mr.auth.effective_ids)
    commenter = self.services.user.GetUser(mr.cnxn, comment.user_id)
    can_view_comment = permissions.CanViewComment(
        comment, commenter, mr.auth.user_id, issue_perms)
    if not can_view_inbound_message or not can_view_comment:
      raise permissions.PermissionException(
          'Only project members may view original email text')
Example #7
0
    def ListActivities(self, mc, request):
        """Return issue activities by a specified user in a response proto."""
        converted_user = converters.IngestUserRef(mc.cnxn, request.user_ref,
                                                  self.services.user)
        user = self.services.user.GetUser(mc.cnxn, converted_user)
        comments = self.services.issue.GetIssueActivity(
            mc.cnxn, user_ids={request.user_ref.user_id})
        issues = self.services.issue.GetIssues(mc.cnxn,
                                               {c.issue_id
                                                for c in comments})
        project_dict = tracker_helpers.GetAllIssueProjects(
            mc.cnxn, issues, self.services.project)
        config_dict = self.services.config.GetProjectConfigs(
            mc.cnxn, list(project_dict.keys()))
        allowed_issues = tracker_helpers.FilterOutNonViewableIssues(
            mc.auth.effective_ids, user, project_dict, config_dict, issues)
        issue_dict = {issue.issue_id: issue for issue in allowed_issues}
        comments = [c for c in comments if c.issue_id in issue_dict]

        users_by_id = framework_views.MakeAllUserViews(
            mc.cnxn, self.services.user, [request.user_ref.user_id],
            tracker_bizobj.UsersInvolvedInCommentList(comments))
        for project in project_dict.values():
            framework_views.RevealAllEmailsToMembers(mc.auth, project,
                                                     users_by_id)

        issues_by_project = {}
        for issue in allowed_issues:
            issues_by_project.setdefault(issue.project_id, []).append(issue)

        # A dictionary {issue_id: perms} of the PermissionSet for the current user
        # on each of the issues.
        issue_perms_dict = {}
        # A dictionary {comment_id: [reporter_id]} of users who have reported the
        # comment as spam.
        comment_reporters = {}
        for project_id, project_issues in issues_by_project.items():
            mc.LookupLoggedInUserPerms(project_dict[project_id])
            issue_perms_dict.update({
                issue.issue_id: permissions.UpdateIssuePermissions(
                    mc.perms,
                    project_dict[issue.project_id],
                    issue,
                    mc.auth.effective_ids,
                    config=config_dict[issue.project_id])
                for issue in project_issues
            })

            with work_env.WorkEnv(mc, self.services) as we:
                project_issue_reporters = we.LookupIssuesFlaggers(
                    project_issues)
                for _, issue_comment_reporters in project_issue_reporters.values(
                ):
                    comment_reporters.update(issue_comment_reporters)

        with mc.profiler.Phase('converting to response objects'):
            converted_comments = []
            for c in comments:
                issue = issue_dict.get(c.issue_id)
                issue_perms = issue_perms_dict.get(c.issue_id)
                result = converters.ConvertComment(
                    issue, c, config_dict.get(issue.project_id), users_by_id,
                    comment_reporters.get(c.id, []),
                    {c.id: 1} if c.is_description else {}, mc.auth.user_id,
                    issue_perms)
                converted_comments.append(result)
            converted_issues = [
                issue_objects_pb2.IssueSummary(project_name=issue.project_name,
                                               local_id=issue.local_id,
                                               summary=issue.summary)
                for issue in allowed_issues
            ]
            response = issues_pb2.ListActivitiesResponse(
                comments=converted_comments, issue_summaries=converted_issues)

        return response
Example #8
0
    def __init__(self,
                 project_name,
                 comment_pb,
                 users_by_id,
                 autolink,
                 all_referenced_artifacts,
                 mr,
                 issue,
                 effective_ids=None):
        """Get IssueComment PB and make its fields available as attrs.

    Args:
      project_name: Name of the project this issue belongs to.
      comment_pb: Comment protocol buffer.
      users_by_id: dict mapping user_ids to UserViews, including
          the user that entered the comment, and any changed participants.
      autolink: utility object for automatically linking to other
        issues, git revisions, etc.
      all_referenced_artifacts: opaque object with details of referenced
        artifacts that is needed by autolink.
      mr: common information parsed from the HTTP request.
      issue: Issue PB for the issue that this comment is part of.
      effective_ids: optional set of int user IDs for the comment author.
    """
        super(IssueCommentView, self).__init__(comment_pb)

        self.id = comment_pb.id
        self.creator = users_by_id[comment_pb.user_id]

        # TODO(jrobbins): this should be based on the issue project, not the
        # request project for non-project views and cross-project.
        if mr.project:
            self.creator_role = framework_helpers.GetRoleName(
                effective_ids or {self.creator.user_id}, mr.project)
        else:
            self.creator_role = None

        time_tuple = time.localtime(comment_pb.timestamp)
        self.date_string = timestr.FormatAbsoluteDate(
            comment_pb.timestamp, old_format=timestr.MONTH_DAY_YEAR_FMT)
        self.date_relative = timestr.FormatRelativeDate(comment_pb.timestamp)
        self.date_tooltip = time.asctime(time_tuple)
        self.date_yyyymmdd = timestr.FormatAbsoluteDate(
            comment_pb.timestamp,
            recent_format=timestr.MONTH_DAY_YEAR_FMT,
            old_format=timestr.MONTH_DAY_YEAR_FMT)
        self.text_runs = _ParseTextRuns(comment_pb.content)
        if autolink and not comment_pb.deleted_by:
            self.text_runs = autolink.MarkupAutolinks(
                mr, self.text_runs, all_referenced_artifacts)

        self.attachments = [
            AttachmentView(attachment, project_name)
            for attachment in comment_pb.attachments
        ]
        self.amendments = sorted(
            [
                AmendmentView(amendment, users_by_id, mr.project_name)
                for amendment in comment_pb.amendments
            ],
            key=lambda amendment: amendment.field_name.lower())
        # Treat comments from banned users as being deleted.
        self.is_deleted = (comment_pb.deleted_by
                           or (self.creator and self.creator.banned))
        self.can_delete = False

        # TODO(jrobbins): pass through config to get granted permissions.
        perms = permissions.UpdateIssuePermissions(mr.perms, mr.project, issue,
                                                   mr.auth.effective_ids)
        if mr.auth.user_id and mr.project:
            self.can_delete = permissions.CanDeleteComment(
                comment_pb, self.creator, mr.auth.user_id, perms)

        self.visible = permissions.CanViewComment(comment_pb, self.creator,
                                                  mr.auth.user_id, perms)