From 07d140625eb8d3b2a9541a0090aff04de4e0771d Mon Sep 17 00:00:00 2001
From: aceArt-GmbH <33117017+aceArt-GmbH@users.noreply.github.com>
Date: Tue, 2 Aug 2022 16:14:31 +0200
Subject: [PATCH] Display project in issue list (#20583)

Co-authored-by: lukas <lukas.walter@aceart.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
---
 models/issues/issue_list.go        | 45 ++++++++++++++++++++++++++++++
 templates/shared/issuelist.tmpl    |  5 ++++
 web_src/less/shared/issuelist.less |  3 +-
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index 20e9949b66..e5298a90ae 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models/db"
+	project_model "code.gitea.io/gitea/models/project"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/container"
@@ -222,6 +223,46 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
 	return nil
 }
 
+func (issues IssueList) getProjectIDs() []int64 {
+	ids := make(map[int64]struct{}, len(issues))
+	for _, issue := range issues {
+		projectID := issue.ProjectID()
+		if _, ok := ids[projectID]; !ok {
+			ids[projectID] = struct{}{}
+		}
+	}
+	return container.KeysInt64(ids)
+}
+
+func (issues IssueList) loadProjects(ctx context.Context) error {
+	projectIDs := issues.getProjectIDs()
+	if len(projectIDs) == 0 {
+		return nil
+	}
+
+	projectMaps := make(map[int64]*project_model.Project, len(projectIDs))
+	left := len(projectIDs)
+	for left > 0 {
+		limit := db.DefaultMaxInSize
+		if left < limit {
+			limit = left
+		}
+		err := db.GetEngine(ctx).
+			In("id", projectIDs[:limit]).
+			Find(&projectMaps)
+		if err != nil {
+			return err
+		}
+		left -= limit
+		projectIDs = projectIDs[limit:]
+	}
+
+	for _, issue := range issues {
+		issue.Project = projectMaps[issue.ProjectID()]
+	}
+	return nil
+}
+
 func (issues IssueList) loadAssignees(ctx context.Context) error {
 	if len(issues) == 0 {
 		return nil
@@ -495,6 +536,10 @@ func (issues IssueList) loadAttributes(ctx context.Context) error {
 		return fmt.Errorf("issue.loadAttributes: loadMilestones: %v", err)
 	}
 
+	if err := issues.loadProjects(ctx); err != nil {
+		return fmt.Errorf("issue.loadAttributes: loadProjects: %v", err)
+	}
+
 	if err := issues.loadAssignees(ctx); err != nil {
 		return fmt.Errorf("issue.loadAttributes: loadAssignees: %v", err)
 	}
diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl
index 5392365dd1..a1b1919701 100644
--- a/templates/shared/issuelist.tmpl
+++ b/templates/shared/issuelist.tmpl
@@ -85,6 +85,11 @@
 							{{svg "octicon-milestone" 14 "mr-2"}}{{.Milestone.Name}}
 						</a>
 					{{end}}
+					{{if .Project}}
+						<a class="project" {{if $.RepoLink}}href="{{$.RepoLink}}/projects/{{.Project.ID}}"{{else}}href="{{.Repo.Link}}/projects/{{.Project.ID}}"{{end}}>
+							{{svg "octicon-project" 14 "mr-2"}}{{.Project.Title}}
+						</a>
+					{{end}}
 					{{if .Ref}}
 						<a class="ref" {{if $.RepoLink}}href="{{index $.IssueRefURLs .ID}}"{{else}}href="{{.Repo.Link}}{{index $.IssueRefURLs .ID}}"{{end}}>
 							{{svg "octicon-git-branch" 14 "mr-2"}}{{index $.IssueRefEndNames .ID}}
diff --git a/web_src/less/shared/issuelist.less b/web_src/less/shared/issuelist.less
index 775fc98478..a0331a1cbe 100644
--- a/web_src/less/shared/issuelist.less
+++ b/web_src/less/shared/issuelist.less
@@ -101,7 +101,8 @@
         padding-left: 5px;
       }
 
-      a.milestone {
+      a.milestone,
+      a.project {
         margin-left: 5px;
       }