aboutsummaryrefslogtreecommitdiffstats
path: root/routers/web/shared
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2024-12-11 14:33:24 +0800
committerGitHub <noreply@github.com>2024-12-11 06:33:24 +0000
commite619384098419569e570796a57ee6af4948067ae (patch)
tree9c5413be580d91ed9912878f80336eab89543670 /routers/web/shared
parent734ddf71180a9bf843d12dd9664eed28ba1a5748 (diff)
downloadgitea-e619384098419569e570796a57ee6af4948067ae.tar.gz
gitea-e619384098419569e570796a57ee6af4948067ae.zip
Add label/author/assignee filters to the user/org home issue list (#32779)
Replace #26661, fix #25979 Not perfect, but usable and much better than before. Since it is quite complex, I am not quite sure whether there would be any regression, if any, I will fix in first time. I have tested the related pages many times: issue list, milestone issue list, project view, user issue list, org issue list.
Diffstat (limited to 'routers/web/shared')
-rw-r--r--routers/web/shared/issue/issue_label.go71
-rw-r--r--routers/web/shared/user/helper.go17
2 files changed, 82 insertions, 6 deletions
diff --git a/routers/web/shared/issue/issue_label.go b/routers/web/shared/issue/issue_label.go
new file mode 100644
index 0000000000..eacea36b02
--- /dev/null
+++ b/routers/web/shared/issue/issue_label.go
@@ -0,0 +1,71 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package issue
+
+import (
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+ issues_model "code.gitea.io/gitea/models/issues"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/services/context"
+)
+
+// PrepareFilterIssueLabels reads the "labels" query parameter, sets `ctx.Data["Labels"]` and `ctx.Data["SelectLabels"]`
+func PrepareFilterIssueLabels(ctx *context.Context, repoID int64, owner *user_model.User) (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 selectLabels != "" {
+ var err error
+ labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
+ if err != nil {
+ ctx.Flash.Error(ctx.Tr("invalid_data", selectLabels), true)
+ }
+ }
+
+ var allLabels []*issues_model.Label
+ if repoID != 0 {
+ repoLabels, err := issues_model.GetLabelsByRepoID(ctx, repoID, "", db.ListOptions{})
+ if err != nil {
+ ctx.ServerError("GetLabelsByRepoID", err)
+ return nil
+ }
+ allLabels = append(allLabels, repoLabels...)
+ }
+
+ if owner != nil && owner.IsOrganization() {
+ orgLabels, err := issues_model.GetLabelsByOrgID(ctx, owner.ID, "", db.ListOptions{})
+ if err != nil {
+ ctx.ServerError("GetLabelsByOrgID", err)
+ return nil
+ }
+ allLabels = append(allLabels, orgLabels...)
+ }
+
+ // Get the exclusive scope for every label ID
+ labelExclusiveScopes := make([]string, 0, len(labelIDs))
+ for _, labelID := range labelIDs {
+ foundExclusiveScope := false
+ for _, label := range allLabels {
+ if label.ID == labelID || label.ID == -labelID {
+ labelExclusiveScopes = append(labelExclusiveScopes, label.ExclusiveScope())
+ foundExclusiveScope = true
+ break
+ }
+ }
+ if !foundExclusiveScope {
+ labelExclusiveScopes = append(labelExclusiveScopes, "")
+ }
+ }
+
+ for _, l := range allLabels {
+ l.LoadSelectedLabelsAfterClick(labelIDs, labelExclusiveScopes)
+ }
+ ctx.Data["Labels"] = allLabels
+ ctx.Data["SelectLabels"] = selectLabels
+ return labelIDs
+}
diff --git a/routers/web/shared/user/helper.go b/routers/web/shared/user/helper.go
index 7268767e0a..b82181a1df 100644
--- a/routers/web/shared/user/helper.go
+++ b/routers/web/shared/user/helper.go
@@ -8,7 +8,9 @@ import (
"slices"
"strconv"
+ "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/optional"
)
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
@@ -31,17 +33,20 @@ func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
// Before, the "issue filter" passes user ID to query the list, but in many cases, it's impossible to pre-fetch the full user list.
// So it's better to make it work like GitHub: users could input username directly.
// Since it only converts the username to ID directly and is only used internally (to search issues), so no permission check is needed.
-// Old usage: poster=123, new usage: poster=the-username (at the moment, non-existing username is treated as poster=0, not ideal but acceptable)
-func GetFilterUserIDByName(ctx context.Context, name string) int64 {
+// Return values:
+// * nil: no filter
+// * some(id): match the id, the id could be -1 to match the issues without assignee
+// * some(NonExistingID): match no issue (due to the user doesn't exist)
+func GetFilterUserIDByName(ctx context.Context, name string) optional.Option[int64] {
if name == "" {
- return 0
+ return optional.None[int64]()
}
u, err := user.GetUserByName(ctx, name)
if err != nil {
if id, err := strconv.ParseInt(name, 10, 64); err == nil {
- return id
+ return optional.Some(id)
}
- return 0
+ return optional.Some(db.NonExistingID)
}
- return u.ID
+ return optional.Some(u.ID)
}