This commit is contained in:
Song Guo 2018-07-20 12:03:19 +00:00 committed by GitHub
commit c40fdf384f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 5 deletions

View File

@ -1227,10 +1227,20 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) error {
if err != nil { if err != nil {
return err return err
} }
if len(labelIDs) > 0 { if len(labelIDs) == 1 {
sess. sess.
Join("INNER", "issue_label", "issue.id = issue_label.issue_id"). Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
In("issue_label.label_id", labelIDs) In("issue_label.label_id", labelIDs)
} else if len(labelIDs) > 1 {
cond, args, _ := builder.ToSQL(builder.In("issue_label.label_id", labelIDs))
sess.
Where(fmt.Sprintf(`issue.id IN (
SELECT issue_label.issue_id
FROM issue_label
WHERE %s
GROUP BY issue_label.issue_id
HAVING COUNT(issue_label.label_id) = %d
)`, cond, len(labelIDs)), args...)
} }
} }
return nil return nil
@ -1409,9 +1419,19 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ",")) labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
if err != nil { if err != nil {
log.Warn("Malformed Labels argument: %s", opts.Labels) log.Warn("Malformed Labels argument: %s", opts.Labels)
} else if len(labelIDs) > 0 { } else if len(labelIDs) == 1 {
sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id"). sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
In("issue_label.label_id", labelIDs) In("issue_label.label_id", labelIDs)
} else if len(labelIDs) > 1 {
cond, args, _ := builder.ToSQL(builder.In("issue_label.label_id", labelIDs))
sess.
Where(fmt.Sprintf(`issue.id IN (
SELECT issue_label.issue_id
FROM issue_label
WHERE %s
GROUP BY issue_label.issue_id
HAVING COUNT(issue_label.label_id) = %d
)`, cond, len(labelIDs)), args...)
} }
} }

View File

@ -69,6 +69,8 @@ type Label struct {
NumClosedIssues int NumClosedIssues int
NumOpenIssues int `xorm:"-"` NumOpenIssues int `xorm:"-"`
IsChecked bool `xorm:"-"` IsChecked bool `xorm:"-"`
QueryString string
IsSelected bool
} }
// APIFormat converts a Label to the api.Label format // APIFormat converts a Label to the api.Label format
@ -85,6 +87,25 @@ func (label *Label) CalOpenIssues() {
label.NumOpenIssues = label.NumIssues - label.NumClosedIssues label.NumOpenIssues = label.NumIssues - label.NumClosedIssues
} }
// LoadSelectedLabelsAfterClick calculates the set of selected labels when a label is clicked
func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []string) {
var labelQuerySlice []string
labelSelected := false
labelID := fmt.Sprint(label.ID)
for _, s := range currentSelectedLabels {
if s == labelID {
labelSelected = true
} else if s != "" {
labelQuerySlice = append(labelQuerySlice, s)
}
}
if !labelSelected {
labelQuerySlice = append(labelQuerySlice, labelID)
}
label.IsSelected = labelSelected
label.QueryString = strings.Join(labelQuerySlice, ",")
}
// ForegroundColor calculates the text color for labels based // ForegroundColor calculates the text color for labels based
// on their background color. // on their background color.
func (label *Label) ForegroundColor() template.CSS { func (label *Label) ForegroundColor() template.CSS {

View File

@ -191,13 +191,21 @@ func TestIssues(t *testing.T) {
}, },
[]int64{1, 2, 3, 5}, []int64{1, 2, 3, 5},
}, },
{
IssuesOptions{
Labels: "1",
Page: 1,
PageSize: 4,
},
[]int64{2, 1},
},
{ {
IssuesOptions{ IssuesOptions{
Labels: "1,2", Labels: "1,2",
Page: 1, Page: 1,
PageSize: 4, PageSize: 4,
}, },
[]int64{5, 2, 1}, []int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
}, },
} { } {
issues, err := Issues(&test.Opts) issues, err := Issues(&test.Opts)

View File

@ -5,6 +5,8 @@
package repo package repo
import ( import (
"strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@ -70,6 +72,23 @@ func RetrieveLabels(ctx *context.Context) {
ctx.Data["SortType"] = ctx.Query("sort") ctx.Data["SortType"] = ctx.Query("sort")
} }
// RetrieveLabelsAndLoadSelectedLabels calculate query string when filtering issues/pulls
func RetrieveLabelsAndLoadSelectedLabels(ctx *context.Context) {
labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort"))
if err != nil {
ctx.ServerError("RetrieveLabelsAndLoadSelectedLabels.GetLabels", err)
return
}
selectLabels := strings.Split(ctx.Query("labels"), ",")
for _, l := range labels {
l.CalOpenIssues()
l.LoadSelectedLabelsAfterClick(selectLabels)
}
ctx.Data["Labels"] = labels
ctx.Data["NumLabels"] = len(labels)
ctx.Data["SortType"] = ctx.Query("sort")
}
// NewLabel create new label for repository // NewLabel create new label for repository
func NewLabel(ctx *context.Context, form auth.CreateLabelForm) { func NewLabel(ctx *context.Context, form auth.CreateLabelForm) {
ctx.Data["Title"] = ctx.Tr("repo.labels") ctx.Data["Title"] = ctx.Tr("repo.labels")

View File

@ -633,7 +633,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/:username/:reponame", func() { m.Group("/:username/:reponame", func() {
m.Group("", func() { m.Group("", func() {
m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues) m.Get("/^:type(issues|pulls)$", repo.RetrieveLabelsAndLoadSelectedLabels, repo.Issues)
m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue) m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
m.Get("/labels/", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.RetrieveLabels, repo.Labels) m.Get("/labels/", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.Milestones) m.Get("/milestones", context.CheckAnyUnit(models.UnitTypeIssues, models.UnitTypePullRequests), repo.Milestones)

View File

@ -42,7 +42,7 @@
<div class="menu"> <div class="menu">
<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> <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>
{{range .Labels}} {{range .Labels}}
<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> <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>
{{end}} {{end}}
</div> </div>
</div> </div>