Skip to content

Commit e6e650b

Browse files
lafriksrichmahn
authored andcommitted
Add the ability to use multiple labels as filters(go-gitea#5786)
1 parent 45b0108 commit e6e650b

File tree

9 files changed

+74
-24
lines changed

9 files changed

+74
-24
lines changed

models/issue.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,7 @@ type IssuesOptions struct {
12111211
PageSize int
12121212
IsClosed util.OptionalBool
12131213
IsPull util.OptionalBool
1214-
Labels string
1214+
LabelIDs []int64
12151215
SortType string
12161216
IssueIDs []int64
12171217
}
@@ -1290,15 +1290,10 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
12901290
sess.And("issue.is_pull=?", false)
12911291
}
12921292

1293-
if len(opts.Labels) > 0 && opts.Labels != "0" {
1294-
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
1295-
if err != nil {
1296-
return err
1297-
}
1298-
if len(labelIDs) > 0 {
1299-
sess.
1300-
Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
1301-
In("issue_label.label_id", labelIDs)
1293+
if opts.LabelIDs != nil {
1294+
for i, labelID := range opts.LabelIDs {
1295+
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
1296+
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
13021297
}
13031298
}
13041299
return nil
@@ -1476,9 +1471,11 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
14761471
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
14771472
if err != nil {
14781473
log.Warn("Malformed Labels argument: %s", opts.Labels)
1479-
} else if len(labelIDs) > 0 {
1480-
sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
1481-
In("issue_label.label_id", labelIDs)
1474+
} else {
1475+
for i, labelID := range labelIDs {
1476+
sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
1477+
fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
1478+
}
14821479
}
14831480
}
14841481

models/issue_label.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ type Label struct {
6969
NumClosedIssues int
7070
NumOpenIssues int `xorm:"-"`
7171
IsChecked bool `xorm:"-"`
72+
QueryString string
73+
IsSelected bool
7274
}
7375

7476
// APIFormat converts a Label to the api.Label format
@@ -85,6 +87,25 @@ func (label *Label) CalOpenIssues() {
8587
label.NumOpenIssues = label.NumIssues - label.NumClosedIssues
8688
}
8789

90+
// LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked
91+
func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64) {
92+
var labelQuerySlice []string
93+
labelSelected := false
94+
labelID := strconv.FormatInt(label.ID, 10)
95+
for _, s := range currentSelectedLabels {
96+
if s == label.ID {
97+
labelSelected = true
98+
} else if s > 0 {
99+
labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10))
100+
}
101+
}
102+
if !labelSelected {
103+
labelQuerySlice = append(labelQuerySlice, labelID)
104+
}
105+
label.IsSelected = labelSelected
106+
label.QueryString = strings.Join(labelQuerySlice, ",")
107+
}
108+
88109
// ForegroundColor calculates the text color for labels based
89110
// on their background color.
90111
func (label *Label) ForegroundColor() template.CSS {

models/issue_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,19 @@ func TestIssues(t *testing.T) {
193193
},
194194
{
195195
IssuesOptions{
196-
Labels: "1,2",
196+
LabelIDs: []int64{1},
197197
Page: 1,
198198
PageSize: 4,
199199
},
200-
[]int64{5, 2, 1},
200+
[]int64{2, 1},
201+
},
202+
{
203+
IssuesOptions{
204+
LabelIDs: []int64{1, 2},
205+
Page: 1,
206+
PageSize: 4,
207+
},
208+
[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
201209
},
202210
} {
203211
issues, err := Issues(&test.Opts)

public/css/index.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/less/_repository.less

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,11 @@
129129
margin: 5px -7px 0 -5px;
130130
width: 16px;
131131
}
132-
.text{
133-
margin-left: 0.9em;
132+
&.labels .octicon {
133+
margin: -2px -7px 0 -5px;
134+
}
135+
.text {
136+
margin-left: 0.9em;
134137
}
135138
.menu {
136139
max-height: 300px;

routers/repo/issue.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,15 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
112112
}
113113

114114
repo := ctx.Repo.Repository
115+
var labelIDs []int64
115116
selectLabels := ctx.Query("labels")
116-
117+
if len(selectLabels) > 0 && selectLabels != "0" {
118+
labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
119+
if err != nil {
120+
ctx.ServerError("StringsToInt64s", err)
121+
return
122+
}
123+
}
117124
isShowClosed := ctx.Query("state") == "closed"
118125

119126
keyword := strings.Trim(ctx.Query("q"), " ")
@@ -176,7 +183,7 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
176183
PageSize: setting.UI.IssuePagingNum,
177184
IsClosed: util.OptionalBoolOf(isShowClosed),
178185
IsPull: isPullOption,
179-
Labels: selectLabels,
186+
LabelIDs: labelIDs,
180187
SortType: sortType,
181188
IssueIDs: issueIDs,
182189
})
@@ -210,7 +217,11 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
210217
ctx.ServerError("GetLabelsByRepoID", err)
211218
return
212219
}
220+
for _, l := range labels {
221+
l.LoadSelectedLabelsAfterClick(labelIDs)
222+
}
213223
ctx.Data["Labels"] = labels
224+
ctx.Data["NumLabels"] = len(labels)
214225

215226
if ctx.QueryInt64("assignee") == 0 {
216227
assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.

routers/routes/routes.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ func RegisterRoutes(m *macaron.Macaron) {
652652

653653
m.Group("/:username/:reponame", func() {
654654
m.Group("", func() {
655-
m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues)
655+
m.Get("/^:type(issues|pulls)$", repo.Issues)
656656
m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
657657
m.Get("/labels/", reqRepoIssuesOrPullsReader, repo.RetrieveLabels, repo.Labels)
658658
m.Get("/milestones", reqRepoIssuesOrPullsReader, repo.Milestones)

routers/user/home.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"bytes"
1010
"fmt"
1111
"sort"
12+
"strings"
1213

1314
"code.gitea.io/gitea/models"
1415
"code.gitea.io/gitea/modules/base"
@@ -257,7 +258,16 @@ func Issues(ctx *context.Context) {
257258

258259
opts.Page = page
259260
opts.PageSize = setting.UI.IssuePagingNum
260-
opts.Labels = ctx.Query("labels")
261+
var labelIDs []int64
262+
selectLabels := ctx.Query("labels")
263+
if len(selectLabels) > 0 && selectLabels != "0" {
264+
labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
265+
if err != nil {
266+
ctx.ServerError("StringsToInt64s", err)
267+
return
268+
}
269+
}
270+
opts.LabelIDs = labelIDs
261271

262272
issues, err := models.Issues(opts)
263273
if err != nil {

templates/repo/issue/list.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
</div>
3333
</div>
3434
<div class="ten wide right aligned column">
35-
<div class="ui secondary filter stackable menu">
35+
<div class="ui secondary filter stackable menu labels">
3636
<!-- Label -->
3737
<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item" style="margin-left: auto">
3838
<span class="text">
@@ -42,7 +42,7 @@
4242
<div class="menu">
4343
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
4444
{{range .Labels}}
45-
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if eq $.SelectLabels .ID}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
45+
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if .IsSelected}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
4646
{{end}}
4747
</div>
4848
</div>

0 commit comments

Comments
 (0)