From 8574a6433fab47b6f20997f024c176490dfad1c0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao <xiaolunwen@gmail.com> Date: Sat, 4 Feb 2023 22:35:08 +0800 Subject: [PATCH] Show all projects, not just repo projects and open/closed projects (#22640) This PR fixes two problems. One is when filter repository issues, only repository level projects are listed. Another is if you list open issues, only open projects will be displayed in filter options and if you list closed issues, only closed projects will be displayed in filter options. In this PR, both repository level and org/user level projects will be displayed in filter, and both open and closed projects will be listed as filter items. --------- Co-authored-by: John Olheiser <john.olheiser@gmail.com> Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: delvh <dev.lh@web.de> --- models/db/search.go | 6 ++++ models/issues/issue.go | 2 ++ options/locale/locale_en-US.ini | 3 +- routers/web/repo/issue.go | 10 ++---- templates/repo/issue/list.tmpl | 64 +++++++++++++++++++++++++++------ 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/models/db/search.go b/models/db/search.go index f5273cb6f6b..26e082756a5 100644 --- a/models/db/search.go +++ b/models/db/search.go @@ -27,3 +27,9 @@ const ( SearchOrderByForks SearchOrderBy = "num_forks ASC" SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" ) + +const ( + // Which means a condition to filter the records which don't match any id. + // It's different from zero which means the condition could be ignored. + NoneID = -1 +) diff --git a/models/issues/issue.go b/models/issues/issue.go index 78cac900523..3ddc7992709 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -1251,6 +1251,8 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) { if opts.ProjectID > 0 { sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id"). And("project_issue.project_id=?", opts.ProjectID) + } else if opts.ProjectID == db.NoneID { // show those that are in no project + sess.And(builder.NotIn("issue.id", builder.Select("issue_id").From("project_issue"))) } if opts.ProjectBoardID != 0 { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 26217293a5e..f384056613c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1306,7 +1306,8 @@ issues.filter_label_no_select = All labels issues.filter_milestone = Milestone issues.filter_milestone_no_select = All milestones issues.filter_project = Project -issues.filter_project_no_select = All projects +issues.filter_project_all = All projects +issues.filter_project_none = No project issues.filter_assignee = Assignee issues.filter_assginee_no_select = All assignees issues.filter_poster = Author diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5bff9e67f34..2193da5110e 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -363,16 +363,10 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti return 0 } - projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Type: project_model.TypeRepository, - IsClosed: util.OptionalBoolOf(isShowClosed), - }) - if err != nil { - ctx.ServerError("FindProjects", err) + retrieveProjects(ctx, repo) + if ctx.Written() { return } - ctx.Data["Projects"] = projects ctx.Data["IssueStats"] = issueStats ctx.Data["SelLabelIDs"] = labelIDs diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 23bcc60f94f..cf2a2a6bbab 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -75,19 +75,41 @@ </div> <!-- Project --> - <div class="ui {{if not .Projects}}disabled{{end}} dropdown jump item"> + <div class="ui{{if not (or .OpenProjects .ClosedProjects)}} disabled{{end}} dropdown jump item"> <span class="text"> - {{.locale.Tr "repo.issues.filter_project"}} + {{.locale.Tr "repo.issues.filter_projects"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}} </span> <div class="menu"> <div class="ui icon search input"> <i class="icon df ac jc">{{svg "octicon-search" 16}}</i> - <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_project"}}"> + <input type="text" placeholder="{{.locale.Tr "repo.issues.filter_projects"}}"> </div> - <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_no_select"}}</a> - {{range .Projects}} - <a class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.Title}}</a> + <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_all"}}</a> + <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&project=-1&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_project_none"}}</a> + {{if .OpenProjects}} + <div class="divider"></div> + <div class="header"> + {{.locale.Tr "repo.issues.new.open_projects"}} + </div> + {{range .OpenProjects}} + <a class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}"> + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} + {{.Title}} + </a> + {{end}} + {{end}} + {{if .ClosedProjects}} + <div class="divider"></div> + <div class="header"> + {{.locale.Tr "repo.issues.new.closed_projects"}} + </div> + {{range .ClosedProjects}} + <a class="{{if $.ProjectID}}{{if eq $.ProjectID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}"> + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} + {{.Title}} + </a> + {{end}} {{end}} </div> </div> @@ -222,18 +244,38 @@ </div> <!-- Projects --> - <div class="ui {{if not .Projects}}disabled{{end}} dropdown jump item"> + <div class="ui{{if not (or .OpenProjects .ClosedProjects)}} disabled{{end}} dropdown jump item"> <span class="text"> {{.locale.Tr "repo.project_board"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}} </span> <div class="menu"> <div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/projects"> - {{.locale.Tr "repo.issues.new.no_projects"}} + {{.locale.Tr "repo.issues.new.clear_projects"}} </div> - {{range .Projects}} - <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects"> - {{.Title}} + {{if .OpenProjects}} + <div class="divider"></div> + <div class="header"> + {{.locale.Tr "repo.issues.new.open_projects"}} + </div> + {{range .OpenProjects}} + <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects"> + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} + {{.Title}} + </div> + {{end}} + {{end}} + {{if .ClosedProjects}} + <div class="divider"></div> + <div class="header"> + {{.locale.Tr "repo.issues.new.closed_projects"}} + </div> + {{range .ClosedProjects}} + <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/projects"> + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} + {{.Title}} + </div> + {{end}} </div> {{end}} </div>