Skip to content

Add no labels and no milestones and no assignees filters #20047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7b651c8
Add no labels and no milestones and no assignees filters.
tyroneyeh Jun 20, 2022
41e9045
Fix in milestone page 500 error issue.
tyroneyeh Jun 20, 2022
7137988
Improve the code.
tyroneyeh Jun 21, 2022
468d7b5
Fix top menu issue 500 bug.
tyroneyeh Jun 21, 2022
ad8ad91
Fix build failed.
tyroneyeh Jun 21, 2022
8dcaa83
Fix build failed again, maybe?
tyroneyeh Jun 21, 2022
e113012
Trigger build and test.
tyroneyeh Jun 21, 2022
5f4e96b
Revert "Improve the code." for test build failed issue.
tyroneyeh Jun 21, 2022
ed16f95
improve milestone code.
tyroneyeh Jun 21, 2022
77fb81c
Improve label and assignee code
tyroneyeh Jun 21, 2022
fc4972a
Refine codes
tyroneyeh Jun 22, 2022
375f3bf
Revert milestone code and fix js error.
tyroneyeh Jun 22, 2022
3d05644
Fix no milestone no work issue
tyroneyeh Jun 22, 2022
41718e0
Fix build failed on multi milestones
tyroneyeh Jun 22, 2022
ba3b5c3
Adjust milestone condition for no milestone function
tyroneyeh Jun 22, 2022
7f045e0
Remove assignee ID in milestone template as not used
tyroneyeh Jun 22, 2022
a9407dc
Remove not used class in list template
tyroneyeh Jun 22, 2022
012526a
Fix issue count exclude issue
tyroneyeh Jun 29, 2022
8e614fb
Merge branch 'main' into 3407_add_nolables_nomilestones_noassignees_f…
tyroneyeh Jul 5, 2022
e9dbdd7
Merge branch 'main' into 3407_add_nolables_nomilestones_noassignees_f…
tyroneyeh Aug 18, 2022
923888e
Remove conflict word
tyroneyeh Aug 18, 2022
07637a9
Merge branch 'main' into 3407_add_nolables_nomilestones_noassignees_f…
6543 Aug 25, 2022
df8c1d0
Modify notin to notexists
tyroneyeh Oct 19, 2022
bc9b0f1
Revert last modified
tyroneyeh Oct 19, 2022
96ecf1e
Merge branch 'main' into 3407_add_nolables_nomilestones_noassignees_f…
6543 Oct 20, 2022
014cd82
refactor
6543 Oct 20, 2022
5c05e53
use JOIN directly
6543 Oct 20, 2022
176b24d
Update models/issues/issue.go
6543 Oct 20, 2022
c8436d3
Merge branch 'main' into 3407_add_nolables_nomilestones_noassignees_f…
6543 Oct 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions models/issues/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
sess.And("issue.is_closed=?", opts.IsClosed.IsTrue())
}

if opts.AssigneeID > 0 {
if opts.AssigneeID != 0 {
applyAssigneeCondition(sess, opts.AssigneeID)
}

Expand All @@ -1309,7 +1309,11 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
}

if len(opts.MilestoneIDs) > 0 {
sess.In("issue.milestone_id", opts.MilestoneIDs)
if opts.MilestoneIDs[0] > 0 {
sess.In("issue.milestone_id", opts.MilestoneIDs)
} else if opts.MilestoneIDs[0] == -1 {
sess.And("issue.milestone_id = 0")
}
}

if opts.UpdatedAfterUnix != 0 {
Expand Down Expand Up @@ -1344,14 +1348,23 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
}

if opts.LabelIDs != nil {
var noLabelsIDs []int64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are excluded Labels, not no Label. So you still missed real No Label issues.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is done by set all lables who you dould select as exclude ...
... so it works

the question is if se should use a keyword instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are hundreds labels in this repository, it's difficult to select labels from the UI?

hasNoLabel := false
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)
} else if labelID < 0 {
noLabelsIDs = append(noLabelsIDs, -labelID)
hasNoLabel = true
}
}
if hasNoLabel {
sess.NotIn("issue.id",
builder.Select("issue_id").
From("issue_label").
Where(builder.In("label_id", noLabelsIDs)))
}
}

if len(opts.IncludedLabelNames) > 0 {
Expand Down Expand Up @@ -1449,6 +1462,11 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
}

func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
if assigneeID == -1 {
return sess.Join("LEFT", "issue_assignees", "issue.id = issue_assignees.issue_id").
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use NotExist is better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you provide an example - never saw an example of builder.NotExists()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you provide an example - never saw an example of builder.NotExists()

I have done that in the previous comment.

And(builder.IsNull{"issue_assignees.issue_id"})
}

return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", assigneeID)
}
Expand Down Expand Up @@ -1702,27 +1720,36 @@ 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 {
var noLabelsIDs []int64
hasNoLabel := false
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)
} else if labelID < 0 {
// sess.Where("issue.id NOT IN (SELECT issue_id FROM issue_label WHERE label_id = ?)", -labelID)
noLabelsIDs = append(noLabelsIDs, -labelID)
hasNoLabel = true
}
}
if hasNoLabel {
sess.NotIn("issue.id", builder.Select("issue_id").From("issue_label").Where(builder.In("label_id", noLabelsIDs)))
}
}
}

if opts.MilestoneID > 0 {
sess.And("issue.milestone_id = ?", opts.MilestoneID)
} else if opts.MilestoneID == -1 {
sess.And("issue.milestone_id = 0")
}

if opts.AssigneeID > 0 {
if opts.AssigneeID != 0 {
applyAssigneeCondition(sess, opts.AssigneeID)
}

Expand Down
3 changes: 3 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1279,10 +1279,13 @@ issues.delete_branch_at = `deleted branch <b>%s</b> %s`
issues.filter_label = Label
issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> to exclude labels`
issues.filter_label_no_select = All labels
issues.filter_nolabels = No labels
issues.filter_milestone = Milestone
issues.filter_milestone_no_select = All milestones
issues.filter_nomilestones = No milestones
issues.filter_assignee = Assignee
issues.filter_assginee_no_select = All assignees
issues.filter_noassginees = No assignees
issues.filter_poster = Author
issues.filter_poster_no_select = All authors
issues.filter_type = Type
Expand Down
2 changes: 1 addition & 1 deletion routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5)

var mileIDs []int64
if milestoneID > 0 {
if milestoneID != 0 {
mileIDs = []int64{milestoneID}
}

Expand Down
3 changes: 3 additions & 0 deletions templates/repo/issue/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
</div>
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
<a class="item nolabels">{{.locale.Tr "repo.issues.filter_nolabels"}}</a>
{{range .Labels}}
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a>
{{end}}
Expand All @@ -69,6 +70,7 @@
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_milestone"}}">
</div>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_milestone_no_select"}}</a>
<a class="item nomilestones">{{.locale.Tr "repo.issues.filter_nomilestones"}}</a></a>
{{range .Milestones}}
<a class="{{if $.MilestoneID}}{{if eq $.MilestoneID .ID}}active selected{{end}}{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.Name}}</a>
{{end}}
Expand Down Expand Up @@ -107,6 +109,7 @@
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignee"}}">
</div>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
<a class="item noassignees">{{.locale.Tr "repo.issues.filter_noassginees"}}</a>
{{range .Assignees}}
<a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{.ID}}&poster={{$.PosterID}}">
{{avatar .}} {{.GetDisplayName}}
Expand Down
2 changes: 2 additions & 0 deletions templates/repo/issue/milestone_issues.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
</div>
<span class="info">{{.locale.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_label_no_select"}}</a>
<a class="item nolabels">{{.locale.Tr "repo.issues.filter_nolabels"}}</a>
{{range .Labels}}
<a class="item label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}" data-label-id="{{.ID}}">{{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if contain $.SelLabelIDs .ID}}{{svg "octicon-check"}}{{end}}<span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}}</a>
{{end}}
Expand Down Expand Up @@ -91,6 +92,7 @@
<input type="text" placeholder="{{.locale.Tr "repo.issues.filter_assignee"}}">
</div>
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_assginee_no_select"}}</a>
<a class="item noassignees">{{.locale.Tr "repo.issues.filter_noassginees"}}</a>
{{range .Assignees}}
<a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&assignee={{.ID}}&poster={{$.PosterID}}">
{{avatar . 28 "mr-2"}}
Expand Down
39 changes: 39 additions & 0 deletions web_src/js/features/repo-issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,45 @@ export function initRepoIssueList() {
}
}
});

$('.menu a.nolabels').on('click', () => {
let labelurl = 'labels=', labelpos = [...document.querySelectorAll('.label-filter-item')].map((i) => { return i.getAttribute('data-label-id') }).join(',-');
const nolabelids = `-${labelpos}`;
if ((labelpos = window.location.search.indexOf(labelurl)) !== -1) {
labelurl = window.location.search.slice(labelpos);
if ((labelpos = labelurl.indexOf('&')) !== -1) {
return window.location.search = window.location.search.replace(labelurl.slice(0, labelpos), `labels=${nolabelids}`);
}
return window.location.search = window.location.search.replace(labelurl, `labels=${nolabelids}`);
}
return window.location.search += (!window.location.search.includes('?') ? '?' : '&') + labelurl + nolabelids;
});

$('.menu a.nomilestones').on('click', () => {
let milestoneurl = 'milestone=', milestonepos;
if ((milestonepos = window.location.search.indexOf(milestoneurl)) !== -1) {
milestoneurl = window.location.search.slice(milestonepos);
if ((milestonepos = milestoneurl.indexOf('&')) !== -1) {
return window.location.search = window.location.search.replace(milestoneurl.slice(0, milestonepos), 'milestone=-1');
}
return window.location.search = window.location.search.replace(milestoneurl, 'milestone=-1');
}
milestonepos = (!window.location.search.includes('?') ? '?' : '&');
return window.location.search += `${milestonepos}${milestoneurl}-1`;
});

$('.menu a.noassignees').on('click', () => {
let assigneeurl = 'assignee=', assigneepos;
if ((assigneepos = window.location.search.indexOf(assigneeurl)) !== -1) {
assigneeurl = window.location.search.slice(assigneepos);
if ((assigneepos = assigneeurl.indexOf('&')) !== -1) {
return window.location.search = window.location.search.replace(assigneeurl.slice(0, assigneepos), `assignee=-1`);
}
return window.location.search = window.location.search.replace(assigneeurl, `assignee=-1`);
}
assigneepos = (!window.location.search.includes('?') ? '?' : '&');
return window.location.search += `${assigneepos}${assigneeurl}-1`;
});
}

export function initRepoIssueCommentDelete() {
Expand Down