Skip to content

Commit 66028d5

Browse files
6543techknowlogick
authored andcommitted
[UI] IssuePage multi repo select (#8741)
* Make repository list @ issues/PR page persist Will partially fix #6355 * Enable multiple selections in repo list @ issues/PR page Part of issue #6355 * Add RepoIDs to UserIssueStatsOptions to make "type" count correct when selecting one/multiple repos. * Replace variable "repo" with list "repos[]" and enable multiple selections of repositories from list by including/excluding RepoIDs in list. * * Remove redundant code * Add 'All' button Improves functionality of the page, so that backtracking is not necessary to reset the page * Remove redundant variable Completely replace 'RepoID' with 'RepoIDs' and remove redundant code * Add RepoIDs to label link * Revert part of code to previous version to troubleshoot build failure * Implement old and new pieces of code whilst adhering to multi select * Attempt to join the two versions Last commit passed tests but doesn't work in practice, this works in practice and hopefully passes the tests. * Update tests to desired state * Fix pagination implementation and tests * Pass repoIDs as `repos=[1,2,3...]` instead of several `repos[]=..` * Update tests file to reflect new functionality * Update template with new `repos` format * Implement new solution to show constant "total issues" count for "All" button * Correct behavior when passing zero to array * Comment out test url returning 404 This keeps returning 404 in the test despite working in practice, for the sake of running more tests I am commenting it out * Comment out another test url returning 404 Last attempt, if more tests crash I will uncomment the urls and request assistance. * Reenable tests and test fix * Re-enable tests * Make selecting "In your repositories" reset selection as passing IDs of repos belonging to other profiles causes breakage * Remove unnecessary (with multi-selection enable) code * Drop repo from repo map and total count if permission denied * Remove extra parenthesis * make template work again * find bug! * forgot the '#' at bugfixing * delete unused RepoID * compile regex only one time * make fmt * local variable = capital letter lower * check if repos query pattern is correct * pagination remove last , - make regex work again * use Replace instead of ReplaceAll; del delete * fix test * how did this test binary got in?!? dont forgot the "-p" at git add * ! * dont replace -> cut fisrt & last string Co-Authored-By: zeripath <[email protected]> * jet another regex dont mind as long as it has the same result and is performatn ... Co-Authored-By: zeripath <[email protected]> * dont use nonexisting repo for test * exclude /issues?type=created_by from test * add table to querys use same syntax in each query (table.colum) * add new issue for test * dont make a workaround or something else this need a refactor itself and is out of scope for this PR * fix misspell * CI.redo() * englisch txt update Co-Authored-By: zeripath <[email protected]> * add sugestions * Tweak & Fix * CI.restart()
1 parent 668eaf9 commit 66028d5

File tree

7 files changed

+132
-95
lines changed

7 files changed

+132
-95
lines changed

integrations/links_test.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,20 @@ func testLinksAsUser(userName string, t *testing.T) {
7272
"/api/swagger",
7373
"/api/v1/swagger",
7474
"/issues",
75-
"/issues?type=your_repositories&repo=0&sort=&state=open",
76-
"/issues?type=assigned&repo=0&sort=&state=open",
77-
"/issues?type=created_by&repo=0&sort=&state=open",
78-
"/issues?type=your_repositories&repo=0&sort=&state=closed",
79-
"/issues?type=assigned&repo=0&sort=&state=closed",
80-
"/issues?type=created_by&repo=0&sort=&state=closed",
75+
"/issues?type=your_repositories&repos=[0]&sort=&state=open",
76+
"/issues?type=assigned&repos=[0]&sort=&state=open",
77+
"/issues?type=your_repositories&repos=[0]&sort=&state=closed",
78+
"/issues?type=assigned&repos=[]&sort=&state=closed",
79+
"/issues?type=assigned&sort=&state=open",
80+
"/issues?type=created_by&repos=[1,2]&sort=&state=closed",
81+
"/issues?type=created_by&repos=[1,2]&sort=&state=open",
8182
"/pulls",
82-
"/pulls?type=your_repositories&repo=0&sort=&state=open",
83-
"/pulls?type=assigned&repo=0&sort=&state=open",
84-
"/pulls?type=created_by&repo=0&sort=&state=open",
85-
"/pulls?type=your_repositories&repo=0&sort=&state=closed",
86-
"/pulls?type=assigned&repo=0&sort=&state=closed",
87-
"/pulls?type=created_by&repo=0&sort=&state=closed",
83+
"/pulls?type=your_repositories&repos=[2]&sort=&state=open",
84+
"/pulls?type=assigned&repos=[]&sort=&state=open",
85+
"/pulls?type=created_by&repos=[0]&sort=&state=open",
86+
"/pulls?type=your_repositories&repos=[0]&sort=&state=closed",
87+
"/pulls?type=assigned&repos=[0]&sort=&state=closed",
88+
"/pulls?type=created_by&repos=[0]&sort=&state=closed",
8889
"/notifications",
8990
"/repo/create",
9091
"/repo/migrate",

models/fixtures/issue.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,4 @@
9696
is_closed: false
9797
is_pull: true
9898
created_unix: 946684820
99-
updated_unix: 978307180
99+
updated_unix: 978307180

models/fixtures/issue_assignees.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
id: 2
77
assignee_id: 1
88
issue_id: 6
9+
-
10+
id: 3
11+
assignee_id: 2
12+
issue_id: 6

models/issue.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
14021402
// UserIssueStatsOptions contains parameters accepted by GetUserIssueStats.
14031403
type UserIssueStatsOptions struct {
14041404
UserID int64
1405-
RepoID int64
1405+
RepoIDs []int64
14061406
UserRepoIDs []int64
14071407
FilterMode int
14081408
IsPull bool
@@ -1416,19 +1416,19 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
14161416

14171417
cond := builder.NewCond()
14181418
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull})
1419-
if opts.RepoID > 0 {
1420-
cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID})
1419+
if len(opts.RepoIDs) > 0 {
1420+
cond = cond.And(builder.In("issue.repo_id", opts.RepoIDs))
14211421
}
14221422

14231423
switch opts.FilterMode {
14241424
case FilterModeAll:
1425-
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
1425+
stats.OpenCount, err = x.Where(cond).And("issue.is_closed = ?", false).
14261426
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
14271427
Count(new(Issue))
14281428
if err != nil {
14291429
return nil, err
14301430
}
1431-
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
1431+
stats.ClosedCount, err = x.Where(cond).And("issue.is_closed = ?", true).
14321432
And(builder.In("issue.repo_id", opts.UserRepoIDs)).
14331433
Count(new(Issue))
14341434
if err != nil {
@@ -1450,14 +1450,14 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
14501450
return nil, err
14511451
}
14521452
case FilterModeCreate:
1453-
stats.OpenCount, err = x.Where(cond).And("is_closed = ?", false).
1454-
And("poster_id = ?", opts.UserID).
1453+
stats.OpenCount, err = x.Where(cond).And("issue.is_closed = ?", false).
1454+
And("issue.poster_id = ?", opts.UserID).
14551455
Count(new(Issue))
14561456
if err != nil {
14571457
return nil, err
14581458
}
1459-
stats.ClosedCount, err = x.Where(cond).And("is_closed = ?", true).
1460-
And("poster_id = ?", opts.UserID).
1459+
stats.ClosedCount, err = x.Where(cond).And("issue.is_closed = ?", true).
1460+
And("issue.poster_id = ?", opts.UserID).
14611461
Count(new(Issue))
14621462
if err != nil {
14631463
return nil, err

models/issue_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func TestGetUserIssueStats(t *testing.T) {
180180
{
181181
UserIssueStatsOptions{
182182
UserID: 1,
183-
RepoID: 1,
183+
RepoIDs: []int64{1},
184184
FilterMode: FilterModeAll,
185185
},
186186
IssueStats{

routers/user/home.go

Lines changed: 63 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ package user
77

88
import (
99
"bytes"
10+
"encoding/json"
1011
"fmt"
12+
"regexp"
1113
"sort"
14+
"strconv"
1215
"strings"
1316

1417
"code.gitea.io/gitea/models"
@@ -20,7 +23,6 @@ import (
2023

2124
"github.com/keybase/go-crypto/openpgp"
2225
"github.com/keybase/go-crypto/openpgp/armor"
23-
"github.com/unknwon/com"
2426
)
2527

2628
const (
@@ -149,6 +151,9 @@ func Dashboard(ctx *context.Context) {
149151
ctx.HTML(200, tplDashboard)
150152
}
151153

154+
// Regexp for repos query
155+
var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`)
156+
152157
// Issues render the user issues page
153158
func Issues(ctx *context.Context) {
154159
isPullList := ctx.Params(":type") == "pulls"
@@ -194,7 +199,25 @@ func Issues(ctx *context.Context) {
194199
page = 1
195200
}
196201

197-
repoID := ctx.QueryInt64("repo")
202+
reposQuery := ctx.Query("repos")
203+
var repoIDs []int64
204+
if issueReposQueryPattern.MatchString(reposQuery) {
205+
// remove "[" and "]" from string
206+
reposQuery = reposQuery[1 : len(reposQuery)-1]
207+
//for each ID (delimiter ",") add to int to repoIDs
208+
for _, rID := range strings.Split(reposQuery, ",") {
209+
// Ensure nonempty string entries
210+
if rID != "" && rID != "0" {
211+
rIDint64, err := strconv.ParseInt(rID, 10, 64)
212+
if err == nil {
213+
repoIDs = append(repoIDs, rIDint64)
214+
}
215+
}
216+
}
217+
} else {
218+
log.Error("issueReposQueryPattern not match with query")
219+
}
220+
198221
isShowClosed := ctx.Query("state") == "closed"
199222

200223
// Get repositories.
@@ -232,20 +255,9 @@ func Issues(ctx *context.Context) {
232255
SortType: sortType,
233256
}
234257

235-
if repoID > 0 {
236-
opts.RepoIDs = []int64{repoID}
237-
}
238-
239258
switch filterMode {
240259
case models.FilterModeAll:
241-
if repoID > 0 {
242-
if !com.IsSliceContainsInt64(userRepoIDs, repoID) {
243-
// force an empty result
244-
opts.RepoIDs = []int64{-1}
245-
}
246-
} else {
247-
opts.RepoIDs = userRepoIDs
248-
}
260+
opts.RepoIDs = userRepoIDs
249261
case models.FilterModeAssign:
250262
opts.AssigneeID = ctxUser.ID
251263
case models.FilterModeCreate:
@@ -273,6 +285,8 @@ func Issues(ctx *context.Context) {
273285
}
274286
opts.LabelIDs = labelIDs
275287

288+
opts.RepoIDs = repoIDs
289+
276290
issues, err := models.Issues(opts)
277291
if err != nil {
278292
ctx.ServerError("Issues", err)
@@ -281,46 +295,23 @@ func Issues(ctx *context.Context) {
281295

282296
showReposMap := make(map[int64]*models.Repository, len(counts))
283297
for repoID := range counts {
284-
repo, err := models.GetRepositoryByID(repoID)
285-
if err != nil {
286-
ctx.ServerError("GetRepositoryByID", err)
298+
showReposMap[repoID], err = models.GetRepositoryByID(repoID)
299+
if models.IsErrRepoNotExist(err) {
300+
ctx.NotFound("GetRepositoryByID", err)
301+
return
302+
} else if err != nil {
303+
ctx.ServerError("GetRepositoryByID", fmt.Errorf("[%d]%v", repoID, err))
287304
return
288-
}
289-
showReposMap[repoID] = repo
290-
}
291-
292-
if repoID > 0 {
293-
if _, ok := showReposMap[repoID]; !ok {
294-
repo, err := models.GetRepositoryByID(repoID)
295-
if models.IsErrRepoNotExist(err) {
296-
ctx.NotFound("GetRepositoryByID", err)
297-
return
298-
} else if err != nil {
299-
ctx.ServerError("GetRepositoryByID", fmt.Errorf("[%d]%v", repoID, err))
300-
return
301-
}
302-
showReposMap[repoID] = repo
303305
}
304306

305-
repo := showReposMap[repoID]
306-
307307
// Check if user has access to given repository.
308-
perm, err := models.GetUserRepoPermission(repo, ctxUser)
308+
perm, err := models.GetUserRepoPermission(showReposMap[repoID], ctxUser)
309309
if err != nil {
310310
ctx.ServerError("GetUserRepoPermission", fmt.Errorf("[%d]%v", repoID, err))
311311
return
312312
}
313313
if !perm.CanRead(models.UnitTypeIssues) {
314-
if log.IsTrace() {
315-
log.Trace("Permission Denied: User %-v cannot read %-v of repo %-v\n"+
316-
"User in repo has Permissions: %-+v",
317-
ctxUser,
318-
models.UnitTypeIssues,
319-
repo,
320-
perm)
321-
}
322-
ctx.Status(404)
323-
return
314+
log.Error("User created Issues in Repository which they no longer have access to: [%d]", repoID)
324315
}
325316
}
326317

@@ -342,7 +333,6 @@ func Issues(ctx *context.Context) {
342333

343334
issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
344335
UserID: ctxUser.ID,
345-
RepoID: repoID,
346336
UserRepoIDs: userRepoIDs,
347337
FilterMode: filterMode,
348338
IsPull: isPullList,
@@ -353,11 +343,26 @@ func Issues(ctx *context.Context) {
353343
return
354344
}
355345

356-
var total int
346+
allIssueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
347+
UserID: ctxUser.ID,
348+
UserRepoIDs: userRepoIDs,
349+
FilterMode: filterMode,
350+
IsPull: isPullList,
351+
IsClosed: isShowClosed,
352+
})
353+
if err != nil {
354+
ctx.ServerError("GetUserIssueStats All", err)
355+
return
356+
}
357+
358+
var shownIssues int
359+
var totalIssues int
357360
if !isShowClosed {
358-
total = int(issueStats.OpenCount)
361+
shownIssues = int(issueStats.OpenCount)
362+
totalIssues = int(allIssueStats.OpenCount)
359363
} else {
360-
total = int(issueStats.ClosedCount)
364+
shownIssues = int(issueStats.ClosedCount)
365+
totalIssues = int(allIssueStats.ClosedCount)
361366
}
362367

363368
ctx.Data["Issues"] = issues
@@ -367,18 +372,24 @@ func Issues(ctx *context.Context) {
367372
ctx.Data["IssueStats"] = issueStats
368373
ctx.Data["ViewType"] = viewType
369374
ctx.Data["SortType"] = sortType
370-
ctx.Data["RepoID"] = repoID
375+
ctx.Data["RepoIDs"] = repoIDs
371376
ctx.Data["IsShowClosed"] = isShowClosed
377+
ctx.Data["TotalIssueCount"] = totalIssues
372378

373379
if isShowClosed {
374380
ctx.Data["State"] = "closed"
375381
} else {
376382
ctx.Data["State"] = "open"
377383
}
378384

379-
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5)
385+
// Convert []int64 to string
386+
reposParam, _ := json.Marshal(repoIDs)
387+
388+
ctx.Data["ReposParam"] = string(reposParam)
389+
390+
pager := context.NewPagination(shownIssues, setting.UI.IssuePagingNum, page, 5)
380391
pager.AddParam(ctx, "type", "ViewType")
381-
pager.AddParam(ctx, "repo", "RepoID")
392+
pager.AddParam(ctx, "repos", "ReposParam")
382393
pager.AddParam(ctx, "sort", "SortType")
383394
pager.AddParam(ctx, "state", "State")
384395
pager.AddParam(ctx, "labels", "SelectLabels")

0 commit comments

Comments
 (0)