mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-24 14:25:02 +02:00
Backport #39958 by @SHIVANSHGARG07 The project milestone filter listed every milestone of the repo, or of all accessible org repos for org projects, including ones with no issue in the project. That made the dropdown noisy and unhelpful. Both repo and org projects now list only milestones that have at least one issue linked to the project, via a new `ProjectID` option on `FindMilestoneOptions`. Fixes: https://github.com/go-gitea/gitea/issues/39150 Co-authored-by: Shivansh Garg <shivanshgarg587@gmail.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
5 files changed
+28
-11
No files matched your search
@@ -39,12 +39,13 @@ func (milestones MilestoneList) SplitByOpenClosed() (open, closed MilestoneList)
|
||||
// FindMilestoneOptions contain options to get milestones
|
||||
type FindMilestoneOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
IsClosed optional.Option[bool]
|
||||
Name string
|
||||
SortType string
|
||||
RepoCond builder.Cond
|
||||
RepoIDs []int64
|
||||
RepoID int64
|
||||
IsClosed optional.Option[bool]
|
||||
Name string
|
||||
SortType string
|
||||
RepoCond builder.Cond
|
||||
RepoIDs []int64
|
||||
ProjectID int64
|
||||
}
|
||||
|
||||
func (opts FindMilestoneOptions) ToConds() builder.Cond {
|
||||
@@ -64,6 +65,11 @@ func (opts FindMilestoneOptions) ToConds() builder.Cond {
|
||||
if len(opts.Name) != 0 {
|
||||
cond = cond.And(db.BuildCaseInsensitiveLike("name", opts.Name))
|
||||
}
|
||||
if opts.ProjectID != 0 {
|
||||
cond = cond.And(builder.In("id", builder.Select("issue.milestone_id").From("issue").
|
||||
InnerJoin("project_issue", "project_issue.issue_id = issue.id").
|
||||
Where(builder.Eq{"project_issue.project_id": opts.ProjectID})))
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
@@ -438,7 +438,8 @@ func ViewProject(ctx *context.Context) {
|
||||
accessCond,
|
||||
)
|
||||
milestones, err = db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
RepoCond: repoCond,
|
||||
RepoCond: repoCond,
|
||||
ProjectID: project.ID,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetOrgMilestones", err)
|
||||
|
||||
@@ -380,10 +380,11 @@ func prepareIssueFilterExclusiveOrderScopes(ctx *context.Context, allLabels []*i
|
||||
ctx.Data["ExclusiveLabelScopes"] = scopes
|
||||
}
|
||||
|
||||
func renderMilestones(ctx *context.Context) {
|
||||
func renderMilestones(ctx *context.Context, projectID int64) {
|
||||
// Get milestones
|
||||
milestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ProjectID: projectID,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("GetAllRepoMilestones", err)
|
||||
@@ -679,7 +680,7 @@ func Issues(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
renderMilestones(ctx)
|
||||
renderMilestones(ctx, 0)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ func ViewProject(ctx *context.Context) {
|
||||
ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
|
||||
ctx.Data["AssigneeID"] = assigneeID
|
||||
|
||||
renderMilestones(ctx)
|
||||
renderMilestones(ctx, project.ID)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -228,6 +228,12 @@ func getProjectIssueIDs(t *testing.T, htmlDoc *HTMLDoc) map[int64]struct{} {
|
||||
return ids
|
||||
}
|
||||
|
||||
func getProjectMilestoneNames(htmlDoc *HTMLDoc) []string {
|
||||
return htmlDoc.Find(".list-header-filters a:has(.octicon-milestone)").Map(func(_ int, s *goquery.Selection) string {
|
||||
return strings.TrimSpace(s.Text())
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepoProjectFilterByMilestone(t *testing.T) {
|
||||
// Project 1 is on repo 1 (user2/repo1) and has issues:
|
||||
// issue 1 (milestone_id=0), issue 2 (milestone_id=1), issue 3 (milestone_id=3), issue 5 (milestone_id=0)
|
||||
@@ -245,6 +251,7 @@ func TestRepoProjectFilterByMilestone(t *testing.T) {
|
||||
assert.Contains(t, issueIDs, int64(2))
|
||||
assert.Contains(t, issueIDs, int64(3))
|
||||
assert.Contains(t, issueIDs, int64(5))
|
||||
assert.Equal(t, []string{"milestone1", "milestone3"}, getProjectMilestoneNames(htmlDoc))
|
||||
})
|
||||
|
||||
t.Run("FilterByMilestone", func(t *testing.T) {
|
||||
@@ -300,6 +307,7 @@ func TestOrgProjectFilterByMilestone(t *testing.T) {
|
||||
Name: "org-test-milestone",
|
||||
}
|
||||
require.NoError(t, issues_model.NewMilestone(t.Context(), milestone))
|
||||
require.NoError(t, issues_model.NewMilestone(t.Context(), &issues_model.Milestone{RepoID: repo.ID, Name: "unlinked-milestone"}))
|
||||
|
||||
issue16.MilestoneID = milestone.ID
|
||||
require.NoError(t, issues_model.UpdateIssueCols(t.Context(), issue16, "milestone_id"))
|
||||
@@ -327,6 +335,7 @@ func TestOrgProjectFilterByMilestone(t *testing.T) {
|
||||
issueIDs := getProjectIssueIDs(t, htmlDoc)
|
||||
assert.Contains(t, issueIDs, issue16.ID)
|
||||
assert.Contains(t, issueIDs, issue17.ID)
|
||||
assert.Equal(t, []string{milestone.Name}, getProjectMilestoneNames(htmlDoc))
|
||||
})
|
||||
|
||||
t.Run("FilterByMilestone", func(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user