diff --git a/models/issues/issue.go b/models/issues/issue.go
index 8c173433f..df38e6851 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -1251,6 +1251,8 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
if opts.AssigneeID > 0 {
applyAssigneeCondition(sess, opts.AssigneeID)
+ } else if opts.AssigneeID == db.NoConditionID {
+ sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_assignees)")
}
if opts.PosterID > 0 {
@@ -1312,13 +1314,17 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
sess.And(builder.Eq{"repository.is_archived": opts.IsArchived.IsTrue()})
}
- if opts.LabelIDs != nil {
- for i, labelID := range opts.LabelIDs {
- if labelID > 0 {
- sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
- fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
- } else {
- sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
+ if len(opts.LabelIDs) > 0 {
+ if opts.LabelIDs[0] == 0 {
+ sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)")
+ } else {
+ for i, labelID := range opts.LabelIDs {
+ if labelID > 0 {
+ sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
+ fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
+ } else if labelID < 0 { // 0 is not supported here, so just ignore it
+ sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
+ }
}
}
}
@@ -1705,17 +1711,21 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
sess.In("issue.id", issueIDs)
}
- if len(opts.Labels) > 0 && opts.Labels != "0" {
+ if len(opts.Labels) > 0 {
labelIDs, err := base.StringsToInt64s(strings.Split(opts.Labels, ","))
if err != nil {
log.Warn("Malformed Labels argument: %s", opts.Labels)
} else {
- for i, labelID := range labelIDs {
- if labelID > 0 {
- sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
- fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
- } else {
- sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID)
+ if labelIDs[0] == 0 {
+ sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label)")
+ } else {
+ for i, labelID := range labelIDs {
+ if labelID > 0 {
+ sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
+ fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
+ } else if labelID < 0 { // 0 is not supported here, so just ignore it
+ sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID)
+ }
}
}
}
@@ -1734,6 +1744,8 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
if opts.AssigneeID > 0 {
applyAssigneeCondition(sess, opts.AssigneeID)
+ } else if opts.AssigneeID == db.NoConditionID {
+ sess.Where("id NOT IN (SELECT issue_id FROM issue_assignees)")
}
if opts.PosterID > 0 {
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 2167471aa..78dbb3c9c 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1361,6 +1361,7 @@ issues.delete_branch_at = `deleted branch %s %s`
issues.filter_label = Label
issues.filter_label_exclude = `Use alt
+ click/enter
to exclude labels`
issues.filter_label_no_select = All labels
+issues.filter_label_select_no_label = No Label
issues.filter_milestone = Milestone
issues.filter_milestone_all = All milestones
issues.filter_milestone_none = No milestones
@@ -1371,6 +1372,7 @@ issues.filter_project_all = All projects
issues.filter_project_none = No project
issues.filter_assignee = Assignee
issues.filter_assginee_no_select = All assignees
+issues.filter_assginee_no_assignee = No assignee
issues.filter_poster = Author
issues.filter_poster_no_select = All authors
issues.filter_type = Type
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index c2f30a01f..66a498613 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -170,8 +170,11 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
repo := ctx.Repo.Repository
var labelIDs []int64
+ // 1,-2 means including label 1 and excluding label 2
+ // 0 means issues with no label
+ // blank means labels will not be filtered for issues
selectLabels := ctx.FormString("labels")
- if len(selectLabels) > 0 && selectLabels != "0" {
+ if len(selectLabels) > 0 {
labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
if err != nil {
ctx.ServerError("StringsToInt64s", err)
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl
index 68d40ffea..7c2f73ca5 100644
--- a/templates/repo/issue/list.tmpl
+++ b/templates/repo/issue/list.tmpl
@@ -38,6 +38,7 @@
{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}
+ {{.locale.Tr "repo.issues.filter_label_select_no_label"}}
{{.locale.Tr "repo.issues.filter_label_no_select"}}
{{$previousExclusiveScope := "_no_scope"}}
{{range .Labels}}
@@ -156,6 +157,7 @@
{{svg "octicon-search" 16}}
+ {{.locale.Tr "repo.issues.filter_assginee_no_assignee"}}
{{.locale.Tr "repo.issues.filter_assginee_no_select"}}
{{range .Assignees}}
@@ -226,6 +228,9 @@
{{svg "octicon-triangle-down" 14 "dropdown icon"}}