Skip to content

Commit b807d2f

Browse files
lunnywolfogre
andauthored
Support no label/assignee filter and batch clearing labels/assignees (#24707)
Since milestones has been implemented, this PR will fix #3407 --------- Co-authored-by: Jason Song <[email protected]>
1 parent e7c2231 commit b807d2f

File tree

4 files changed

+40
-15
lines changed

4 files changed

+40
-15
lines changed

models/issues/issue.go

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,6 +1251,8 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
12511251

12521252
if opts.AssigneeID > 0 {
12531253
applyAssigneeCondition(sess, opts.AssigneeID)
1254+
} else if opts.AssigneeID == db.NoConditionID {
1255+
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
12541256
}
12551257

12561258
if opts.PosterID > 0 {
@@ -1312,13 +1314,17 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
13121314
sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()})
13131315
}
13141316

1315-
if opts.LabelIDs != nil {
1316-
for i, labelID := range opts.LabelIDs {
1317-
if labelID > 0 {
1318-
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
1319-
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
1320-
} else {
1321-
sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
1317+
if len(opts.LabelIDs) > 0 {
1318+
if opts.LabelIDs[0] == 0 {
1319+
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)")
1320+
} else {
1321+
for i, labelID := range opts.LabelIDs {
1322+
if labelID > 0 {
1323+
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
1324+
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
1325+
} else if labelID < 0 { // 0 is not supported here, so just ignore it
1326+
sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
1327+
}
13221328
}
13231329
}
13241330
}
@@ -1705,17 +1711,21 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
17051711
sess.In("issue.id", issueIDs)
17061712
}
17071713

1708-
if len(opts.Labels) > 0 && opts.Labels != "0" {
1714+
if len(opts.Labels) > 0 {
17091715
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
17101716
if err != nil {
17111717
log.Warn("Malformed Labels argument: %s", opts.Labels)
17121718
} else {
1713-
for i, labelID := range labelIDs {
1714-
if labelID > 0 {
1715-
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
1716-
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
1717-
} else {
1718-
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID)
1719+
if labelIDs[0] == 0 {
1720+
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)")
1721+
} else {
1722+
for i, labelID := range labelIDs {
1723+
if labelID > 0 {
1724+
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
1725+
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
1726+
} else if labelID < 0 { // 0 is not supported here, so just ignore it
1727+
sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID)
1728+
}
17191729
}
17201730
}
17211731
}
@@ -1734,6 +1744,8 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
17341744

17351745
if opts.AssigneeID > 0 {
17361746
applyAssigneeCondition(sess, opts.AssigneeID)
1747+
} else if opts.AssigneeID == db.NoConditionID {
1748+
sess.Where("id NOT IN (SELECT issue_id FROM issue_assignees)")
17371749
}
17381750

17391751
if opts.PosterID > 0 {

options/locale/locale_en-US.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@ issues.delete_branch_at = `deleted branch <b>%s</b> %s`
13611361
issues.filter_label = Label
13621362
issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> to exclude labels`
13631363
issues.filter_label_no_select = All labels
1364+
issues.filter_label_select_no_label = No Label
13641365
issues.filter_milestone = Milestone
13651366
issues.filter_milestone_all = All milestones
13661367
issues.filter_milestone_none = No milestones
@@ -1371,6 +1372,7 @@ issues.filter_project_all = All projects
13711372
issues.filter_project_none = No project
13721373
issues.filter_assignee = Assignee
13731374
issues.filter_assginee_no_select = All assignees
1375+
issues.filter_assginee_no_assignee = No assignee
13741376
issues.filter_poster = Author
13751377
issues.filter_poster_no_select = All authors
13761378
issues.filter_type = Type

routers/web/repo/issue.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,11 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
170170

171171
repo := ctx.Repo.Repository
172172
var labelIDs []int64
173+
// 1,-2 means including label 1 and excluding label 2
174+
// 0 means issues with no label
175+
// blank means labels will not be filtered for issues
173176
selectLabels := ctx.FormString("labels")
174-
if len(selectLabels) > 0 && selectLabels != "0" {
177+
if len(selectLabels) > 0 {
175178
labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
176179
if err != nil {
177180
ctx.ServerError("StringsToInt64s", err)

templates/repo/issue/list.tmpl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_label"}}">
3939
</div>
4040
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
41+
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels=0&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_select_no_label"}}</a>
4142
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
4243
{{$previousExclusiveScope := "_no_scope"}}
4344
{{range .Labels}}
@@ -156,6 +157,7 @@
156157
<i class="icon gt-df gt-ac gt-jc">{{svg "octicon-search" 16}}</i>
157158
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignee"}}">
158159
</div>
160+
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee=-1&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_assginee_no_assignee"}}</a>
159161
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
160162
{{range .Assignees}}
161163
<a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item gt-df" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{.ID}}&poster={{$.PosterID}}">
@@ -226,6 +228,9 @@
226228
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
227229
</span>
228230
<div class="menu">
231+
<div class="item issue-action" data-action="clear" data-url="{{$.RepoLink}}/issues/labels">
232+
{{.locale.Tr "repo.issues.new.clear_labels"}}
233+
</div>
229234
{{$previousExclusiveScope := "_no_scope"}}
230235
{{range .Labels}}
231236
{{$exclusiveScope := .ExclusiveScope}}
@@ -313,6 +318,9 @@
313318
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
314319
</span>
315320
<div class="menu">
321+
<div class="item issue-action" data-action="clear" data-url="{{$.Link}}/assignee">
322+
{{.locale.Tr "repo.issues.new.clear_assignees"}}
323+
</div>
316324
<div class="item issue-action" data-element-id="0" data-url="{{$.Link}}/assignee">
317325
{{.locale.Tr "repo.issues.action_assignee_no_select"}}
318326
</div>

0 commit comments

Comments
 (0)