diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 0554d18bab2..20ee2c24e30 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -365,25 +365,10 @@ func ViewProject(ctx *context.Context) { ctx.Data["issuesAttachmentMap"] = issuesAttachmentMap } - linkedPrsMap := make(map[int64][]*issues_model.Issue) - for _, issuesList := range issuesMap { - for _, issue := range issuesList { - var referencedIDs []int64 - for _, comment := range issue.Comments { - if comment.RefIssueID != 0 && comment.RefIsPull { - referencedIDs = append(referencedIDs, comment.RefIssueID) - } - } - - if len(referencedIDs) > 0 { - if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ - IssueIDs: referencedIDs, - IsPull: optional.Some(true), - }); err == nil { - linkedPrsMap[issue.ID] = linkedPrs - } - } - } + linkedPrsMap, err := project_service.LoadLinkedPullRequests(ctx, issuesMap, ctx.Doer) + if err != nil { + ctx.ServerError("LoadLinkedPullRequests", err) + return } // TODO: Add option to filter also by repository specific labels diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 48990320e87..aef974bbcb8 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -343,25 +343,10 @@ func ViewProject(ctx *context.Context) { ctx.Data["issuesAttachmentMap"] = issuesAttachmentMap } - linkedPrsMap := make(map[int64][]*issues_model.Issue) - for _, issuesList := range issuesMap { - for _, issue := range issuesList { - var referencedIDs []int64 - for _, comment := range issue.Comments { - if comment.RefIssueID != 0 && comment.RefIsPull { - referencedIDs = append(referencedIDs, comment.RefIssueID) - } - } - - if len(referencedIDs) > 0 { - if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ - IssueIDs: referencedIDs, - IsPull: optional.Some(true), - }); err == nil { - linkedPrsMap[issue.ID] = linkedPrs - } - } - } + linkedPrsMap, err := project_service.LoadLinkedPullRequests(ctx, issuesMap, ctx.Doer) + if err != nil { + ctx.ServerError("LoadLinkedPullRequests", err) + return } ctx.Data["LinkedPRs"] = linkedPrsMap diff --git a/services/projects/issue.go b/services/projects/issue.go index d8119460c8d..47c3273172d 100644 --- a/services/projects/issue.go +++ b/services/projects/issue.go @@ -10,6 +10,7 @@ import ( "gitea.dev/models/db" issues_model "gitea.dev/models/issues" + access_model "gitea.dev/models/perm/access" project_model "gitea.dev/models/project" user_model "gitea.dev/models/user" "gitea.dev/modules/optional" @@ -208,6 +209,48 @@ func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, return results, nil } +// LoadLinkedPullRequests returns, per issue, the pull requests referencing it that the doer can read +func LoadLinkedPullRequests(ctx context.Context, issuesMap map[int64]issues_model.IssueList, doer *user_model.User) (map[int64][]*issues_model.Issue, error) { + linkedPrsMap := make(map[int64][]*issues_model.Issue) + canReadPulls := make(map[int64]bool) + for _, issuesList := range issuesMap { + for _, issue := range issuesList { + var referencedIDs []int64 + for _, comment := range issue.Comments { + if comment.RefIssueID != 0 && comment.RefIsPull { + referencedIDs = append(referencedIDs, comment.RefIssueID) + } + } + if len(referencedIDs) == 0 { + continue + } + + linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ + IssueIDs: referencedIDs, + IsPull: optional.Some(true), + }) + if err != nil { + return nil, err + } + for _, pr := range linkedPrs { + canRead, ok := canReadPulls[pr.RepoID] + if !ok { + perm, err := access_model.GetDoerRepoPermission(ctx, pr.Repo, doer) + if err != nil { + return nil, err + } + canRead = perm.CanReadIssuesOrPulls(true) + canReadPulls[pr.RepoID] = canRead + } + if canRead { + linkedPrsMap[issue.ID] = append(linkedPrsMap[issue.ID], pr) + } + } + } + } + return linkedPrsMap, nil +} + // NumClosedIssues return counter of closed issues assigned to a project func loadNumClosedIssues(ctx context.Context, p *project_model.Project) error { cnt, err := db.GetEngine(ctx).Table("project_issue"). diff --git a/tests/integration/project_test.go b/tests/integration/project_test.go index 6ef3cb6157d..034a0ffeff8 100644 --- a/tests/integration/project_test.go +++ b/tests/integration/project_test.go @@ -379,6 +379,23 @@ func TestOrgProjectFilterByMilestone(t *testing.T) { func TestProjects(t *testing.T) { defer tests.PrepareTestEnv(t)() + t.Run("PrivateLinkedPullRequest", func(t *testing.T) { + require.NoError(t, db.Insert(t.Context(), &issues_model.Comment{ + Type: issues_model.CommentTypePullRef, + PosterID: 2, + IssueID: 1, + RefRepoID: 3, + RefIssueID: 12, + RefIsPull: true, + })) + const linkedPR = `href="/org3/repo3/pulls/2"` + + resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/projects/1"), http.StatusOK) + assert.NotContains(t, resp.Body.String(), linkedPR) + + resp = loginUser(t, "user2").MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/projects/1"), http.StatusOK) + assert.Contains(t, resp.Body.String(), linkedPR) + }) t.Run("LoadIssuesAssigneesForProject", func(t *testing.T) { _ = db.TruncateBeans(t.Context(), "project_issue", "issue_assignees") _ = db.Insert(t.Context(),