Ejemplo n.º 1
0
  def GatherPageData(self, mr):
    """Build up a dictionary of data values to use when rendering the page."""
    if mr.auth.user_id:
      self.services.user.AddVisitedHotlist(
          mr.cnxn, mr.auth.user_id, mr.hotlist_id)

    all_members = (mr.hotlist.owner_ids +
                   mr.hotlist.editor_ids + mr.hotlist.follower_ids)

    hotlist_url = hotlist_helpers.GetURLOfHotlist(
        mr.cnxn, mr.hotlist, self.services.user)

    with self.profiler.Phase('gathering members on this page'):
      users_by_id = framework_views.MakeAllUserViews(
          mr.cnxn, self.services.user, all_members)
      framework_views.RevealAllEmailsToMembers(mr, users_by_id)

    untrusted_user_group_proxies = []
    # TODO(jojwang): implement FindUntrustedGroups()

    with self.profiler.Phase('making member views'):
      owner_views = self._MakeMemberViews(mr, mr.hotlist.owner_ids, users_by_id)
      editor_views = self._MakeMemberViews(mr, mr.hotlist.editor_ids,
                                           users_by_id)
      follower_views = self._MakeMemberViews(mr, mr.hotlist.follower_ids,
                                             users_by_id)
      all_member_views = owner_views + editor_views + follower_views

    pagination = paginate.ArtifactPagination(
        mr, all_member_views, MEMBERS_PER_PAGE,'%s%s' % (
              hotlist_url, urls.HOTLIST_PEOPLE))

    offer_membership_editing = permissions.CanAdministerHotlist(
        mr.auth.effective_ids, mr.hotlist)

    newly_added_views = [mv for mv in all_member_views
                         if str(mv.user.user_id) in mr.GetParam('new', [])]

    return {
        'is_hotlist': ezt.boolean(True),
        'untrusted_user_groups': untrusted_user_group_proxies,
        'pagination': pagination,
        'initial_add_members': '',
        'subtab_mode': None,
        'initially_expand_form': ezt.boolean(False),
        'newly_added_views': newly_added_views,
        'offer_membership_editing': ezt.boolean(offer_membership_editing),
        'total_num_owners': len(mr.hotlist.owner_ids),
        'check_abandonment': ezt.boolean(True),
        'initial_new_owner_username': '',
        'placeholder': 'new-owner-username',
        'open_dialog': ezt.boolean(False),
        'viewing_user_page': ezt.boolean(True),
        }
Ejemplo n.º 2
0
  def GatherPageData(self, mr):
    """Build up a dictionary of data values to use when rendering the page."""
    group_id = mr.viewed_user_auth.user_id
    group_settings = self.services.usergroup.GetGroupSettings(
        mr.cnxn, group_id)
    if not group_settings:
      raise usergroup_svc.NoSuchGroupException()

    member_ids_dict, owner_ids_dict = (
        self.services.usergroup.LookupVisibleMembers(
            mr.cnxn, [group_id], mr.perms, mr.auth.effective_ids,
            self.services))
    member_ids = member_ids_dict[group_id]
    owner_ids = owner_ids_dict[group_id]
    member_pbs_dict = self.services.user.GetUsersByIDs(
        mr.cnxn, member_ids)
    owner_pbs_dict = self.services.user.GetUsersByIDs(
        mr.cnxn, owner_ids)
    member_dict = {}
    for user_id, user_pb in member_pbs_dict.iteritems():
      member_view = group_helpers.GroupMemberView(user_pb, group_id, 'member')
      member_dict[user_id] = member_view
    owner_dict = {}
    for user_id, user_pb in owner_pbs_dict.iteritems():
      member_view = group_helpers.GroupMemberView(user_pb, group_id, 'owner')
      owner_dict[user_id] = member_view

    member_user_views = []
    member_user_views.extend(
        sorted(owner_dict.values(), key=lambda u: u.email))
    member_user_views.extend(
        sorted(member_dict.values(), key=lambda u: u.email))

    group_view = sitewide_views.GroupView(
        mr.viewed_user_auth.email, len(member_ids), group_settings,
        mr.viewed_user_auth.user_id)
    pagination = paginate.ArtifactPagination(
        mr, member_user_views, MEMBERS_PER_PAGE, group_view.detail_url)

    is_imported_group = bool(group_settings.ext_group_type)

    offer_membership_editing = permissions.CanEditGroup(
        mr.perms, mr.auth.effective_ids, owner_ids) and not is_imported_group

    return {
        'admin_tab_mode': self.ADMIN_TAB_META,
        'offer_membership_editing': ezt.boolean(offer_membership_editing),
        'initial_add_members': '',
        'initially_expand_form': ezt.boolean(False),
        'groupid': group_id,
        'groupname': mr.viewed_username,
        'settings': group_settings,
        'pagination': pagination,
        }
Ejemplo n.º 3
0
    def Paginate(self):
        """Fetch matching issues and paginate the search results.

    These two actions are intertwined because we try to only
    retrieve the Issues on the current pagination page.
    """
        if self.grid_mode:
            # We don't paginate the grid view.  But, pagination object shows counts.
            self.pagination = paginate.ArtifactPagination(
                self.mr,
                self.allowed_results,
                self.default_results_per_page,
                total_count=self.total_count,
                list_page_url=urls.ISSUE_LIST)
            # We limited the results, but still show the original total count.
            self.visible_results = self.allowed_results

        else:
            # We already got the issues, just display a slice of the visible ones.
            limit_reached = False
            for shard_limit_reached in self.search_limit_reached.values():
                limit_reached |= shard_limit_reached
            self.pagination = paginate.ArtifactPagination(
                self.mr,
                self.allowed_results,
                self.default_results_per_page,
                total_count=self.total_count,
                list_page_url=urls.ISSUE_LIST,
                limit_reached=limit_reached,
                skipped=self.num_skipped_at_start)
            self.visible_results = self.pagination.visible_results

        # If we were not forced to look up visible users already, do it now.
        if self.grid_mode:
            self._LookupNeededUsers(self.allowed_results)
        else:
            self._LookupNeededUsers(self.visible_results)
Ejemplo n.º 4
0
    def ListHotlistIssues(self, mc, request):
        """Get the issues on the specified hotlist."""
        # TODO(ehmaldonado): This probably doesn't work, since we need to check
        # the permissions for each issue in their own project, and we're not doing
        # that.
        hotlist_id = converters.IngestHotlistRef(mc.cnxn, self.services.user,
                                                 self.services.features,
                                                 request.hotlist_ref)

        with work_env.WorkEnv(mc, self.services) as we:
            hotlist_items = we.GetHotlist(hotlist_id).items
            issue_ids = [item.issue_id for item in hotlist_items]
            issues = we.GetIssuesDict(issue_ids)

            projects = we.GetProjectsByName(
                [issue.project_name for issue in issues.values()])
            configs = we.GetProjectConfigs(
                [project.project_id for project in projects.values()])
            configs = {
                project.project_name: configs[project.project_id]
                for project in projects.values()
            }
            related_refs = we.GetRelatedIssueRefs(iter(issues.values()))

        with mc.profiler.Phase('making user views'):
            users_involved = set(item.adder_id for item in hotlist_items)
            users_involved.update(
                tracker_bizobj.UsersInvolvedInIssues(iter(issues.values())))
            users_by_id = framework_views.MakeAllUserViews(
                mc.cnxn, self.services.user, users_involved)
            framework_views.RevealAllEmailsToMembers(mc.auth, None,
                                                     users_by_id)

        hotlist_items = [
            hotlist_item for hotlist_item in hotlist_items
            if hotlist_item.issue_id in issues
        ]

        start, max_items = converters.IngestPagination(request.pagination)
        pagination = paginate.ArtifactPagination(hotlist_items, max_items,
                                                 start, None, None)

        result = features_pb2.ListHotlistIssuesResponse(items=[
            converters.ConvertHotlistItem(hotlist_item, issues, users_by_id,
                                          related_refs, configs)
            for hotlist_item in pagination.visible_results
        ])
        return result
Ejemplo n.º 5
0
    def GatherPageData(self, mr):
        if not self.CheckPerm(mr, permissions.MODERATE_SPAM):
            raise permissions.PermissionException()

        page_perms = self.MakePagePerms(mr, None, permissions.MODERATE_SPAM,
                                        permissions.EDIT_ISSUE,
                                        permissions.CREATE_ISSUE,
                                        permissions.SET_STAR)

        # TODO(seanmccullough): Figure out how to get the IssueFlagQueue either
        # integrated into this page data, or on its own subtab of spam moderation.
        # Also figure out the same for Comments.
        issue_items, total_count = self.services.spam.GetIssueClassifierQueue(
            mr.cnxn, self.services.issue, mr.project.project_id, mr.start,
            mr.num)

        issue_queue = spam_helpers.DecorateIssueClassifierQueue(
            mr.cnxn, self.services.issue, self.services.spam,
            self.services.user, issue_items)

        p = paginate.ArtifactPagination(mr, [], mr.num,
                                        urls.SPAM_MODERATION_QUEUE,
                                        total_count)

        return {
            'issue_queue':
            issue_queue,
            'projectname':
            mr.project.project_name,
            'pagination':
            p,
            'page_perms':
            page_perms,
            'moderate_spam_token':
            xsrf.GenerateToken(
                mr.auth.user_id,
                '/p/%s%s.do' % (mr.project_name, urls.SPAM_MODERATION_QUEUE)),
        }
Ejemplo n.º 6
0
  def GetProjectsAndPaginate(self, cnxn, list_page_url):
    """Paginate the filtered list of project names and retrieve Project PBs.

    Args:
      cnxn: connection to SQL database.
      list_page_url: string page URL for prev and next links.
    """
    with self.mr.profiler.Phase('getting all projects'):
      project_dict = self.services.project.GetProjects(
          cnxn, self.allowed_project_ids)
      project_list = sorted(
          project_dict.values(),
          key=lambda p: p.project_name)
      logging.info('project_list is %r', project_list)

    url_params = [(name, self.mr.GetParam(name)) for name in
                  framework_helpers.RECOGNIZED_PARAMS]
    self.pagination = paginate.ArtifactPagination(
        project_list,
        self.mr.GetPositiveIntParam('num', self.default_results_per_page),
        self.mr.GetPositiveIntParam('start'), self.mr.project_name,
        list_page_url, url_params=url_params)
    self.visible_results = self.pagination.visible_results
Ejemplo n.º 7
0
    def GatherPageData(self, mr):
        if not self.CheckPerm(mr, permissions.MODERATE_SPAM):
            raise permissions.PermissionException()

        page_perms = self.MakePagePerms(mr, None, permissions.MODERATE_SPAM,
                                        permissions.EDIT_ISSUE,
                                        permissions.CREATE_ISSUE,
                                        permissions.SET_STAR)

        # TODO(seanmccullough): Figure out how to get the IssueFlagQueue either
        # integrated into this page data, or on its own subtab of spam moderation.
        # Also figure out the same for Comments.
        issue_items, total_count = self.services.spam.GetIssueClassifierQueue(
            mr.cnxn, self.services.issue, mr.project.project_id, mr.start,
            mr.num)

        issue_queue = spam_helpers.DecorateIssueClassifierQueue(
            mr.cnxn, self.services.issue, self.services.spam,
            self.services.user, issue_items)

        url_params = [(name, mr.GetParam(name))
                      for name in framework_helpers.RECOGNIZED_PARAMS]
        p = paginate.ArtifactPagination([],
                                        mr.num,
                                        mr.GetPositiveIntParam('start'),
                                        mr.project_name,
                                        urls.SPAM_MODERATION_QUEUE,
                                        total_count=total_count,
                                        url_params=url_params)

        return {
            'issue_queue': issue_queue,
            'projectname': mr.project.project_name,
            'pagination': p,
            'page_perms': page_perms,
        }
Ejemplo n.º 8
0
    def GetGridViewData(self, mr):
        """EZT template values to render a Table View of issues.

    Args:
      mr: commonly used info parsed from the request.

    Returns:
      Dictionary of page data for rendering of the Table View.
    """
        mr.ComputeColSpec(mr.hotlist)
        starred_iid_set = set(
            self.services.issue_star.LookupStarredItemIDs(
                mr.cnxn, mr.auth.user_id))
        issues_list = self.services.issue.GetIssues(
            mr.cnxn,
            [hotlist_issue.issue_id for hotlist_issue in mr.hotlist.items])
        allowed_issues = hotlist_helpers.FilterIssues(mr, issues_list,
                                                      self.services)
        issue_and_hotlist_users = tracker_bizobj.UsersInvolvedInIssues(
            allowed_issues
            or []).union(features_bizobj.UsersInvolvedInHotlists([mr.hotlist]))
        users_by_id = framework_views.MakeAllUserViews(
            mr.cnxn, self.services.user, issue_and_hotlist_users)
        hotlist_issues_project_ids = hotlist_helpers.GetAllProjectsOfIssues(
            [issue for issue in issues_list])
        config_list = hotlist_helpers.GetAllConfigsOfProjects(
            mr.cnxn, hotlist_issues_project_ids, self.services)
        harmonized_config = tracker_bizobj.HarmonizeConfigs(config_list)
        limit = settings.max_issues_in_grid
        grid_limited = len(allowed_issues) > limit
        lower_cols = mr.col_spec.lower().split()
        grid_x = (mr.x or harmonized_config.default_x_attr or '--').lower()
        grid_y = (mr.y or harmonized_config.default_y_attr or '--').lower()
        lower_cols.append(grid_x)
        lower_cols.append(grid_y)
        related_iids = set()
        for issue in allowed_issues:
            if 'blockedon' in lower_cols:
                related_iids.update(issue.blocked_on_iids)
            if 'blocking' in lower_cols:
                related_iids.update(issue.blocking_iids)
            if 'mergedinto' in lower_cols:
                related_iids.add(issue.merged_into)
        related_issues_list = self.services.issue.GetIssues(
            mr.cnxn, list(related_iids))
        related_issues = {
            issue.issue_id: issue
            for issue in related_issues_list
        }

        hotlist_context_dict = {
            hotlist_issue.issue_id: {
                'adder_id': hotlist_issue.adder_id,
                'date_added':
                timestr.FormatRelativeDate(hotlist_issue.date_added),
                'note': hotlist_issue.note
            }
            for hotlist_issue in mr.hotlist.items
        }

        grid_view_data = grid_view_helpers.GetGridViewData(
            mr,
            allowed_issues,
            harmonized_config,
            users_by_id,
            starred_iid_set,
            grid_limited,
            related_issues,
            hotlist_context_dict=hotlist_context_dict)

        url_params = [(name, mr.GetParam(name))
                      for name in framework_helpers.RECOGNIZED_PARAMS]
        # We are passing in None for the project_name in ArtifactPagination
        # because we are not operating under any project.
        grid_view_data.update({
            'pagination':
            paginate.ArtifactPagination(
                allowed_issues,
                mr.GetPositiveIntParam(
                    'num', features_constants.DEFAULT_RESULTS_PER_PAGE),
                mr.GetPositiveIntParam('start'),
                None,
                urls.HOTLIST_ISSUES,
                total_count=len(allowed_issues),
                url_params=url_params)
        })

        return grid_view_data
Ejemplo n.º 9
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""
        group_id = mr.viewed_user_auth.user_id
        group_settings = self.services.usergroup.GetGroupSettings(
            mr.cnxn, group_id)
        if not group_settings:
            raise exceptions.NoSuchGroupException()

        member_ids_dict, owner_ids_dict = (
            self.services.usergroup.LookupVisibleMembers(
                mr.cnxn, [group_id], mr.perms, mr.auth.effective_ids,
                self.services))
        member_ids = member_ids_dict[group_id]
        owner_ids = owner_ids_dict[group_id]
        member_pbs_dict = self.services.user.GetUsersByIDs(mr.cnxn, member_ids)
        owner_pbs_dict = self.services.user.GetUsersByIDs(mr.cnxn, owner_ids)
        member_dict = {}
        for user_id, user_pb in member_pbs_dict.items():
            member_view = group_helpers.GroupMemberView(
                user_pb, group_id, 'member')
            member_dict[user_id] = member_view
        owner_dict = {}
        for user_id, user_pb in owner_pbs_dict.items():
            member_view = group_helpers.GroupMemberView(
                user_pb, group_id, 'owner')
            owner_dict[user_id] = member_view

        member_user_views = []
        member_user_views.extend(
            sorted(list(owner_dict.values()), key=lambda u: u.email))
        member_user_views.extend(
            sorted(list(member_dict.values()), key=lambda u: u.email))

        group_view = sitewide_views.GroupView(mr.viewed_user_auth.email,
                                              len(member_ids), group_settings,
                                              mr.viewed_user_auth.user_id)
        url_params = [(name, mr.GetParam(name))
                      for name in framework_helpers.RECOGNIZED_PARAMS]
        pagination = paginate.ArtifactPagination(
            member_user_views,
            mr.GetPositiveIntParam('num', MEMBERS_PER_PAGE),
            mr.GetPositiveIntParam('start'),
            mr.project_name,
            group_view.detail_url,
            url_params=url_params)

        is_imported_group = bool(group_settings.ext_group_type)

        offer_membership_editing = permissions.CanEditGroup(
            mr.perms, mr.auth.effective_ids,
            owner_ids) and not is_imported_group

        group_type = 'Monorail user group'
        if group_settings.ext_group_type:
            group_type = str(group_settings.ext_group_type).capitalize()

        return {
            'admin_tab_mode': self.ADMIN_TAB_META,
            'offer_membership_editing': ezt.boolean(offer_membership_editing),
            'initial_add_members': '',
            'initially_expand_form': ezt.boolean(False),
            'groupid': group_id,
            'groupname': mr.viewed_username,
            'settings': group_settings,
            'group_type': group_type,
            'pagination': pagination,
        }
Ejemplo n.º 10
0
def CreateHotlistTableData(mr, hotlist_issues, profiler, services):
    """Creates the table data for the hotlistissues table."""
    with profiler.Phase('getting stars'):
        starred_iid_set = set(
            services.issue_star.LookupStarredItemIDs(mr.cnxn, mr.auth.user_id))

    with profiler.Phase('Computing col_spec'):
        mr.ComputeColSpec(mr.hotlist)

    issues_list = services.issue.GetIssues(
        mr.cnxn, [hotlist_issue.issue_id for hotlist_issue in hotlist_issues])
    with profiler.Phase('Getting config'):
        hotlist_issues_project_ids = GetAllProjectsOfIssues(
            [issue for issue in issues_list])
        is_cross_project = len(hotlist_issues_project_ids) > 1
        config_list = GetAllConfigsOfProjects(mr.cnxn,
                                              hotlist_issues_project_ids,
                                              services)
        harmonized_config = tracker_bizobj.HarmonizeConfigs(config_list)

    (sorted_issues, hotlist_issues_context,
     issues_users_by_id) = GetSortedHotlistIssues(mr, hotlist_issues,
                                                  issues_list,
                                                  harmonized_config, profiler,
                                                  services)

    with profiler.Phase("getting related issues"):
        related_iids = set()
        results_needing_related = sorted_issues
        lower_cols = mr.col_spec.lower().split()
        for issue in results_needing_related:
            if 'blockedon' in lower_cols:
                related_iids.update(issue.blocked_on_iids)
            if 'blocking' in lower_cols:
                related_iids.update(issue.blocking_iids)
            if 'mergedinto' in lower_cols:
                related_iids.add(issue.merged_into)
        related_issues_list = services.issue.GetIssues(mr.cnxn,
                                                       list(related_iids))
        related_issues = {
            issue.issue_id: issue
            for issue in related_issues_list
        }

    with profiler.Phase('building table'):
        context_for_all_issues = {
            issue.issue_id: hotlist_issues_context[issue.issue_id]
            for issue in sorted_issues
        }

        column_values = table_view_helpers.ExtractUniqueValues(
            mr.col_spec.lower().split(),
            sorted_issues,
            issues_users_by_id,
            harmonized_config,
            related_issues,
            hotlist_context_dict=context_for_all_issues)
        unshown_columns = table_view_helpers.ComputeUnshownColumns(
            sorted_issues, mr.col_spec.split(), harmonized_config,
            features_constants.OTHER_BUILT_IN_COLS)
        pagination = paginate.ArtifactPagination(
            mr, sorted_issues, mr.num,
            GetURLOfHotlist(mr.cnxn, mr.hotlist, services.user),
            len(sorted_issues))

        sort_spec = '%s %s %s' % (mr.group_by_spec, mr.sort_spec,
                                  harmonized_config.default_sort_spec)

        table_data = _MakeTableData(pagination.visible_results,
                                    starred_iid_set,
                                    mr.col_spec.lower().split(),
                                    mr.group_by_spec.lower().split(),
                                    issues_users_by_id,
                                    tablecell.CELL_FACTORIES, related_issues,
                                    harmonized_config, context_for_all_issues,
                                    mr.hotlist_id, sort_spec)

    table_related_dict = {
        'column_values': column_values,
        'unshown_columns': unshown_columns,
        'pagination': pagination,
        'is_cross_project': is_cross_project
    }
    return table_data, table_related_dict
Ejemplo n.º 11
0
    def GatherPageData(self, mr):
        """Build up a dictionary of data values to use when rendering the page."""
        all_members = (mr.project.owner_ids + mr.project.committer_ids +
                       mr.project.contributor_ids)

        with self.profiler.Phase('gathering members on this page'):
            users_by_id = framework_views.MakeAllUserViews(
                mr.cnxn, self.services.user, all_members)
            framework_views.RevealAllEmailsToMembers(mr, users_by_id)

        # TODO(jrobbins): re-implement FindUntrustedGroups()
        untrusted_user_group_proxies = []

        with self.profiler.Phase('gathering commitments (notes)'):
            project_commitments = self.services.project.GetProjectCommitments(
                mr.cnxn, mr.project_id)

        with self.profiler.Phase('gathering autocomple exclusion ids'):
            acexclusion_ids = self.services.project.GetProjectAutocompleteExclusion(
                mr.cnxn, mr.project_id)

        with self.profiler.Phase('making member views'):
            owner_views = self._MakeMemberViews(mr.auth.user_id, users_by_id,
                                                mr.project.owner_ids,
                                                mr.project,
                                                project_commitments,
                                                acexclusion_ids)
            committer_views = self._MakeMemberViews(
                mr.auth.user_id, users_by_id, mr.project.committer_ids,
                mr.project, project_commitments, acexclusion_ids)
            contributor_views = self._MakeMemberViews(
                mr.auth.user_id, users_by_id, mr.project.contributor_ids,
                mr.project, project_commitments, acexclusion_ids)
            all_member_views = owner_views + committer_views + contributor_views

        pagination = paginate.ArtifactPagination(mr, all_member_views,
                                                 MEMBERS_PER_PAGE,
                                                 urls.PEOPLE_LIST)

        offer_membership_editing = mr.perms.HasPerm(permissions.EDIT_PROJECT,
                                                    mr.auth.user_id,
                                                    mr.project)

        check_abandonment = permissions.ShouldCheckForAbandonment(mr)

        newly_added_views = [
            mv for mv in all_member_views
            if str(mv.user.user_id) in mr.GetParam('new', [])
        ]

        return {
            'pagination': pagination,
            'subtab_mode': None,
            'offer_membership_editing': ezt.boolean(offer_membership_editing),
            'initial_add_members': '',
            'initially_expand_form': ezt.boolean(False),
            'untrusted_user_groups': untrusted_user_group_proxies,
            'check_abandonment': ezt.boolean(check_abandonment),
            'total_num_owners': len(mr.project.owner_ids),
            'newly_added_views': newly_added_views,
            'is_hotlist': ezt.boolean(False),
        }
Ejemplo n.º 12
0
def CreateHotlistTableData(mr, hotlist_issues, services):
    """Creates the table data for the hotlistissues table."""
    with mr.profiler.Phase('getting stars'):
        starred_iid_set = set(
            services.issue_star.LookupStarredItemIDs(mr.cnxn, mr.auth.user_id))

    with mr.profiler.Phase('Computing col_spec'):
        mr.ComputeColSpec(mr.hotlist)

    issues_list = services.issue.GetIssues(
        mr.cnxn, [hotlist_issue.issue_id for hotlist_issue in hotlist_issues])
    with mr.profiler.Phase('Getting config'):
        hotlist_issues_project_ids = GetAllProjectsOfIssues(
            [issue for issue in issues_list])
        is_cross_project = len(hotlist_issues_project_ids) > 1
        config_list = GetAllConfigsOfProjects(mr.cnxn,
                                              hotlist_issues_project_ids,
                                              services)
        harmonized_config = tracker_bizobj.HarmonizeConfigs(config_list)

    (sorted_issues, hotlist_issues_context,
     issues_users_by_id) = GetSortedHotlistIssues(mr, hotlist_issues,
                                                  issues_list,
                                                  harmonized_config, services)

    with mr.profiler.Phase("getting related issues"):
        related_iids = set()
        results_needing_related = sorted_issues
        lower_cols = mr.col_spec.lower().split()
        for issue in results_needing_related:
            if 'blockedon' in lower_cols:
                related_iids.update(issue.blocked_on_iids)
            if 'blocking' in lower_cols:
                related_iids.update(issue.blocking_iids)
            if 'mergedinto' in lower_cols:
                related_iids.add(issue.merged_into)
        related_issues_list = services.issue.GetIssues(mr.cnxn,
                                                       list(related_iids))
        related_issues = {
            issue.issue_id: issue
            for issue in related_issues_list
        }

    with mr.profiler.Phase('filtering unviewable issues'):
        viewable_iids_set = {
            issue.issue_id
            for issue in tracker_helpers.GetAllowedIssues(
                mr, [related_issues.values()], services)[0]
        }

    with mr.profiler.Phase('building table'):
        context_for_all_issues = {
            issue.issue_id: hotlist_issues_context[issue.issue_id]
            for issue in sorted_issues
        }

        column_values = table_view_helpers.ExtractUniqueValues(
            mr.col_spec.lower().split(),
            sorted_issues,
            issues_users_by_id,
            harmonized_config,
            related_issues,
            hotlist_context_dict=context_for_all_issues)
        unshown_columns = table_view_helpers.ComputeUnshownColumns(
            sorted_issues, mr.col_spec.split(), harmonized_config,
            features_constants.OTHER_BUILT_IN_COLS)
        url_params = [(name, mr.GetParam(name))
                      for name in framework_helpers.RECOGNIZED_PARAMS]
        # We are passing in None for the project_name because we are not operating
        # under any project.
        pagination = paginate.ArtifactPagination(
            sorted_issues,
            mr.num,
            mr.GetPositiveIntParam('start'),
            None,
            GetURLOfHotlist(mr.cnxn, mr.hotlist, services.user),
            total_count=len(sorted_issues),
            url_params=url_params)

        sort_spec = '%s %s %s' % (mr.group_by_spec, mr.sort_spec,
                                  harmonized_config.default_sort_spec)

        table_data = _MakeTableData(
            pagination.visible_results, starred_iid_set,
            mr.col_spec.lower().split(),
            mr.group_by_spec.lower().split(), issues_users_by_id,
            tablecell.CELL_FACTORIES, related_issues, viewable_iids_set,
            harmonized_config, context_for_all_issues, mr.hotlist_id,
            sort_spec)

    table_related_dict = {
        'column_values': column_values,
        'unshown_columns': unshown_columns,
        'pagination': pagination,
        'is_cross_project': is_cross_project
    }
    return table_data, table_related_dict