Skip to content

Commit 1332b39

Browse files
committed
still revert go-gitea#28304
1 parent ec30295 commit 1332b39

File tree

7 files changed

+221
-46
lines changed

7 files changed

+221
-46
lines changed

models/issues/issue_search.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
type IssuesOptions struct { //nolint
2424
db.Paginator
2525
RepoIDs []int64 // overwrites RepoCond if the length is not 0
26-
AllPublic bool // include also all public repositories
2726
RepoCond builder.Cond
2827
AssigneeID int64
2928
PosterID int64
@@ -198,12 +197,6 @@ func applyRepoConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session
198197
} else if len(opts.RepoIDs) > 1 {
199198
opts.RepoCond = builder.In("issue.repo_id", opts.RepoIDs)
200199
}
201-
if opts.AllPublic {
202-
if opts.RepoCond == nil {
203-
opts.RepoCond = builder.NewCond()
204-
}
205-
opts.RepoCond = opts.RepoCond.Or(builder.In("issue.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"is_private": false})))
206-
}
207200
if opts.RepoCond != nil {
208201
sess.And(opts.RepoCond)
209202
}

modules/indexer/issues/db/options.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
5656
opts := &issue_model.IssuesOptions{
5757
Paginator: options.Paginator,
5858
RepoIDs: options.RepoIDs,
59-
AllPublic: options.AllPublic,
6059
RepoCond: nil,
6160
AssigneeID: convertID(options.AssigneeID),
6261
PosterID: convertID(options.PosterID),

modules/indexer/issues/dboptions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
1212
searchOpt := &SearchOptions{
1313
Keyword: keyword,
1414
RepoIDs: opts.RepoIDs,
15-
AllPublic: opts.AllPublic,
15+
AllPublic: false,
1616
IsPull: opts.IsPull,
1717
IsClosed: opts.IsClosed,
1818
}

modules/indexer/issues/indexer.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
db_model "code.gitea.io/gitea/models/db"
1515
repo_model "code.gitea.io/gitea/models/repo"
16+
"code.gitea.io/gitea/modules/container"
1617
"code.gitea.io/gitea/modules/graceful"
1718
"code.gitea.io/gitea/modules/indexer/issues/bleve"
1819
"code.gitea.io/gitea/modules/indexer/issues/db"
@@ -313,3 +314,30 @@ func CountIssues(ctx context.Context, opts *SearchOptions) (int64, error) {
313314
_, total, err := SearchIssues(ctx, opts)
314315
return total, err
315316
}
317+
318+
// CountIssuesByRepo counts issues by options and group by repo id.
319+
// It's not a complete implementation, since it requires the caller should provide the repo ids.
320+
// That means opts.RepoIDs must be specified, and opts.AllPublic must be false.
321+
// It's good enough for the current usage, and it can be improved if needed.
322+
// TODO: use "group by" of the indexer engines to implement it.
323+
func CountIssuesByRepo(ctx context.Context, opts *SearchOptions) (map[int64]int64, error) {
324+
if len(opts.RepoIDs) == 0 {
325+
return nil, fmt.Errorf("opts.RepoIDs must be specified")
326+
}
327+
if opts.AllPublic {
328+
return nil, fmt.Errorf("opts.AllPublic must be false")
329+
}
330+
331+
repoIDs := container.SetOf(opts.RepoIDs...).Values()
332+
ret := make(map[int64]int64, len(repoIDs))
333+
// TODO: it could be faster if do it in parallel for some indexer engines. Improve it if users report it's slow.
334+
for _, repoID := range repoIDs {
335+
count, err := CountIssues(ctx, opts.Copy(func(o *internal.SearchOptions) { o.RepoIDs = []int64{repoID} }))
336+
if err != nil {
337+
return nil, err
338+
}
339+
ret[repoID] = count
340+
}
341+
342+
return ret, nil
343+
}

routers/web/user/home.go

Lines changed: 138 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"code.gitea.io/gitea/modules/base"
2626
"code.gitea.io/gitea/modules/container"
2727
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
28+
"code.gitea.io/gitea/modules/json"
2829
"code.gitea.io/gitea/modules/log"
2930
"code.gitea.io/gitea/modules/markup"
3031
"code.gitea.io/gitea/modules/markup/markdown"
@@ -348,6 +349,7 @@ func Pulls(ctx *context.Context) {
348349

349350
ctx.Data["Title"] = ctx.Tr("pull_requests")
350351
ctx.Data["PageIsPulls"] = true
352+
ctx.Data["SingleRepoAction"] = "pull"
351353
buildIssueOverview(ctx, unit.TypePullRequests)
352354
}
353355

@@ -361,6 +363,7 @@ func Issues(ctx *context.Context) {
361363

362364
ctx.Data["Title"] = ctx.Tr("issues")
363365
ctx.Data["PageIsIssues"] = true
366+
ctx.Data["SingleRepoAction"] = "issue"
364367
buildIssueOverview(ctx, unit.TypeIssues)
365368
}
366369

@@ -486,13 +489,6 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
486489
opts.RepoIDs = []int64{0}
487490
}
488491
}
489-
if ctx.Doer.ID == ctxUser.ID && filterMode != issues_model.FilterModeYourRepositories {
490-
// If the doer is the same as the context user, which means the doer is viewing his own dashboard,
491-
// it's not enough to show the repos that the doer owns or has been explicitly granted access to,
492-
// because the doer may create issues or be mentioned in any public repo.
493-
// So we need search issues in all public repos.
494-
opts.AllPublic = true
495-
}
496492

497493
switch filterMode {
498494
case issues_model.FilterModeAll:
@@ -517,6 +513,14 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
517513
isShowClosed := ctx.FormString("state") == "closed"
518514
opts.IsClosed = optional.Some(isShowClosed)
519515

516+
// Filter repos and count issues in them. Count will be used later.
517+
// USING NON-FINAL STATE OF opts FOR A QUERY.
518+
issueCountByRepo, err := issue_indexer.CountIssuesByRepo(ctx, issue_indexer.ToSearchOptions(keyword, opts))
519+
if err != nil {
520+
ctx.ServerError("CountIssuesByRepo", err)
521+
return
522+
}
523+
520524
// Make sure page number is at least 1. Will be posted to ctx.Data.
521525
page := ctx.FormInt("page")
522526
if page <= 1 {
@@ -541,6 +545,17 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
541545
}
542546
opts.LabelIDs = labelIDs
543547

548+
// Parse ctx.FormString("repos") and remember matched repo IDs for later.
549+
// Gets set when clicking filters on the issues overview page.
550+
selectedRepoIDs := getRepoIDs(ctx.FormString("repos"))
551+
// Remove repo IDs that are not accessible to the user.
552+
selectedRepoIDs = slices.DeleteFunc(selectedRepoIDs, func(v int64) bool {
553+
return !accessibleRepos.Contains(v)
554+
})
555+
if len(selectedRepoIDs) > 0 {
556+
opts.RepoIDs = selectedRepoIDs
557+
}
558+
544559
// ------------------------------
545560
// Get issues as defined by opts.
546561
// ------------------------------
@@ -561,6 +576,41 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
561576
}
562577
}
563578

579+
// ----------------------------------
580+
// Add repository pointers to Issues.
581+
// ----------------------------------
582+
583+
// Remove repositories that should not be shown,
584+
// which are repositories that have no issues and are not selected by the user.
585+
selectedRepos := container.SetOf(selectedRepoIDs...)
586+
for k, v := range issueCountByRepo {
587+
if v == 0 && !selectedRepos.Contains(k) {
588+
delete(issueCountByRepo, k)
589+
}
590+
}
591+
592+
// showReposMap maps repository IDs to their Repository pointers.
593+
showReposMap, err := loadRepoByIDs(ctx, ctxUser, issueCountByRepo, unitType)
594+
if err != nil {
595+
if repo_model.IsErrRepoNotExist(err) {
596+
ctx.NotFound("GetRepositoryByID", err)
597+
return
598+
}
599+
ctx.ServerError("loadRepoByIDs", err)
600+
return
601+
}
602+
603+
// a RepositoryList
604+
showRepos := repo_model.RepositoryListOfMap(showReposMap)
605+
sort.Sort(showRepos)
606+
607+
// maps pull request IDs to their CommitStatus. Will be posted to ctx.Data.
608+
for _, issue := range issues {
609+
if issue.Repo == nil {
610+
issue.Repo = showReposMap[issue.RepoID]
611+
}
612+
}
613+
564614
commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues)
565615
if err != nil {
566616
ctx.ServerError("GetIssuesLastCommitStatus", err)
@@ -570,7 +620,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
570620
// -------------------------------
571621
// Fill stats to post to ctx.Data.
572622
// -------------------------------
573-
issueStats, err := getUserIssueStats(ctx, ctxUser, filterMode, issue_indexer.ToSearchOptions(keyword, opts))
623+
issueStats, err := getUserIssueStats(ctx, filterMode, issue_indexer.ToSearchOptions(keyword, opts), ctx.Doer.ID)
574624
if err != nil {
575625
ctx.ServerError("getUserIssueStats", err)
576626
return
@@ -583,6 +633,25 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
583633
} else {
584634
shownIssues = int(issueStats.ClosedCount)
585635
}
636+
if len(opts.RepoIDs) != 0 {
637+
shownIssues = 0
638+
for _, repoID := range opts.RepoIDs {
639+
shownIssues += int(issueCountByRepo[repoID])
640+
}
641+
}
642+
643+
var allIssueCount int64
644+
for _, issueCount := range issueCountByRepo {
645+
allIssueCount += issueCount
646+
}
647+
ctx.Data["TotalIssueCount"] = allIssueCount
648+
649+
if len(opts.RepoIDs) == 1 {
650+
repo := showReposMap[opts.RepoIDs[0]]
651+
if repo != nil {
652+
ctx.Data["SingleRepoLink"] = repo.Link()
653+
}
654+
}
586655

587656
ctx.Data["IsShowClosed"] = isShowClosed
588657

@@ -619,9 +688,12 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
619688
}
620689
ctx.Data["CommitLastStatus"] = lastStatus
621690
ctx.Data["CommitStatuses"] = commitStatuses
691+
ctx.Data["Repos"] = showRepos
692+
ctx.Data["Counts"] = issueCountByRepo
622693
ctx.Data["IssueStats"] = issueStats
623694
ctx.Data["ViewType"] = viewType
624695
ctx.Data["SortType"] = sortType
696+
ctx.Data["RepoIDs"] = selectedRepoIDs
625697
ctx.Data["IsShowClosed"] = isShowClosed
626698
ctx.Data["SelectLabels"] = selectedLabels
627699

@@ -631,9 +703,15 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
631703
ctx.Data["State"] = "open"
632704
}
633705

706+
// Convert []int64 to string
707+
reposParam, _ := json.Marshal(opts.RepoIDs)
708+
709+
ctx.Data["ReposParam"] = string(reposParam)
710+
634711
pager := context.NewPagination(shownIssues, setting.UI.IssuePagingNum, page, 5)
635712
pager.AddParam(ctx, "q", "Keyword")
636713
pager.AddParam(ctx, "type", "ViewType")
714+
pager.AddParam(ctx, "repos", "ReposParam")
637715
pager.AddParam(ctx, "sort", "SortType")
638716
pager.AddParam(ctx, "state", "State")
639717
pager.AddParam(ctx, "labels", "SelectLabels")
@@ -644,6 +722,55 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
644722
ctx.HTML(http.StatusOK, tplIssues)
645723
}
646724

725+
func getRepoIDs(reposQuery string) []int64 {
726+
if len(reposQuery) == 0 || reposQuery == "[]" {
727+
return []int64{}
728+
}
729+
if !issueReposQueryPattern.MatchString(reposQuery) {
730+
log.Warn("issueReposQueryPattern does not match query: %q", reposQuery)
731+
return []int64{}
732+
}
733+
734+
var repoIDs []int64
735+
// remove "[" and "]" from string
736+
reposQuery = reposQuery[1 : len(reposQuery)-1]
737+
// for each ID (delimiter ",") add to int to repoIDs
738+
for _, rID := range strings.Split(reposQuery, ",") {
739+
// Ensure nonempty string entries
740+
if rID != "" && rID != "0" {
741+
rIDint64, err := strconv.ParseInt(rID, 10, 64)
742+
if err == nil {
743+
repoIDs = append(repoIDs, rIDint64)
744+
}
745+
}
746+
}
747+
748+
return repoIDs
749+
}
750+
751+
func loadRepoByIDs(ctx *context.Context, ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
752+
totalRes := make(map[int64]*repo_model.Repository, len(issueCountByRepo))
753+
repoIDs := make([]int64, 0, 500)
754+
for id := range issueCountByRepo {
755+
if id <= 0 {
756+
continue
757+
}
758+
repoIDs = append(repoIDs, id)
759+
if len(repoIDs) == 500 {
760+
if err := repo_model.FindReposMapByIDs(ctx, repoIDs, totalRes); err != nil {
761+
return nil, err
762+
}
763+
repoIDs = repoIDs[:0]
764+
}
765+
}
766+
if len(repoIDs) > 0 {
767+
if err := repo_model.FindReposMapByIDs(ctx, repoIDs, totalRes); err != nil {
768+
return nil, err
769+
}
770+
}
771+
return totalRes, nil
772+
}
773+
647774
// ShowSSHKeys output all the ssh keys of user by uid
648775
func ShowSSHKeys(ctx *context.Context) {
649776
keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
@@ -760,15 +887,8 @@ func UsernameSubRoute(ctx *context.Context) {
760887
}
761888
}
762889

763-
func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMode int, opts *issue_indexer.SearchOptions) (*issues_model.IssueStats, error) {
764-
doerID := ctx.Doer.ID
765-
890+
func getUserIssueStats(ctx *context.Context, filterMode int, opts *issue_indexer.SearchOptions, doerID int64) (*issues_model.IssueStats, error) {
766891
opts = opts.Copy(func(o *issue_indexer.SearchOptions) {
767-
// If the doer is the same as the context user, which means the doer is viewing his own dashboard,
768-
// it's not enough to show the repos that the doer owns or has been explicitly granted access to,
769-
// because the doer may create issues or be mentioned in any public repo.
770-
// So we need search issues in all public repos.
771-
o.AllPublic = doerID == ctxUser.ID
772892
o.AssigneeID = nil
773893
o.PosterID = nil
774894
o.MentionID = nil
@@ -784,10 +904,7 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
784904
{
785905
openClosedOpts := opts.Copy()
786906
switch filterMode {
787-
case issues_model.FilterModeAll:
788-
// no-op
789-
case issues_model.FilterModeYourRepositories:
790-
openClosedOpts.AllPublic = false
907+
case issues_model.FilterModeAll, issues_model.FilterModeYourRepositories:
791908
case issues_model.FilterModeAssign:
792909
openClosedOpts.AssigneeID = &doerID
793910
case issues_model.FilterModeCreate:
@@ -811,7 +928,7 @@ func getUserIssueStats(ctx *context.Context, ctxUser *user_model.User, filterMod
811928
}
812929
}
813930

814-
ret.YourRepositoriesCount, err = issue_indexer.CountIssues(ctx, opts.Copy(func(o *issue_indexer.SearchOptions) { o.AllPublic = false }))
931+
ret.YourRepositoriesCount, err = issue_indexer.CountIssues(ctx, opts)
815932
if err != nil {
816933
return nil, err
817934
}

routers/web/user/home_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ func TestArchivedIssues(t *testing.T) {
4747
// Assert: One Issue (ID 30) from one Repo (ID 50) is retrieved, while nothing from archived Repo 51 is retrieved
4848
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
4949

50+
assert.EqualValues(t, map[int64]int64{50: 1}, ctx.Data["Counts"])
5051
assert.Len(t, ctx.Data["Issues"], 1)
52+
assert.Len(t, ctx.Data["Repos"], 1)
5153
}
5254

5355
func TestIssues(t *testing.T) {
@@ -60,8 +62,10 @@ func TestIssues(t *testing.T) {
6062
Issues(ctx)
6163
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
6264

65+
assert.EqualValues(t, map[int64]int64{1: 1, 2: 1}, ctx.Data["Counts"])
6366
assert.EqualValues(t, true, ctx.Data["IsShowClosed"])
6467
assert.Len(t, ctx.Data["Issues"], 1)
68+
assert.Len(t, ctx.Data["Repos"], 2)
6569
}
6670

6771
func TestPulls(t *testing.T) {

0 commit comments

Comments
 (0)