Merge 5f08f1bc0a into cc9fa062e6
This commit is contained in:
commit
c40fdf384f
|
|
@ -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...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user