diff options
author | Brad Albright <32200834+bhalbright@users.noreply.github.com> | 2019-12-15 08:20:08 -0600 |
---|---|---|
committer | zeripath <art27@cantab.net> | 2019-12-15 14:20:08 +0000 |
commit | f6b29012e09d5f7770a0b1ea8659da5172e155b3 (patch) | |
tree | cf78ded22aee572c91e93e2a2a3db8a7f9fd903c /routers/user | |
parent | 7cc16740a56072465b3938cbb9cd326d40bd7ba9 (diff) | |
download | gitea-f6b29012e09d5f7770a0b1ea8659da5172e155b3.tar.gz gitea-f6b29012e09d5f7770a0b1ea8659da5172e155b3.zip |
Add /milestones endpoint (#8733)
Create a /milestones endpoint which basically serves as a dashboard view for milestones, very similar to the /issues or /pulls page.
Closes #8232
Diffstat (limited to 'routers/user')
-rw-r--r-- | routers/user/home.go | 193 | ||||
-rw-r--r-- | routers/user/home_test.go | 39 |
2 files changed, 229 insertions, 3 deletions
diff --git a/routers/user/home.go b/routers/user/home.go index a1060f371f..426f15bfa7 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -18,17 +18,20 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "github.com/keybase/go-crypto/openpgp" "github.com/keybase/go-crypto/openpgp/armor" + "github.com/unknwon/com" ) const ( - tplDashboard base.TplName = "user/dashboard/dashboard" - tplIssues base.TplName = "user/dashboard/issues" - tplProfile base.TplName = "user/profile" + tplDashboard base.TplName = "user/dashboard/dashboard" + tplIssues base.TplName = "user/dashboard/issues" + tplMilestones base.TplName = "user/dashboard/milestones" + tplProfile base.TplName = "user/profile" ) // getDashboardContextUser finds out dashboard is viewing as which context user. @@ -150,6 +153,190 @@ func Dashboard(ctx *context.Context) { ctx.HTML(200, tplDashboard) } +// Milestones render the user milestones page +func Milestones(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("milestones") + ctx.Data["PageIsMilestonesDashboard"] = true + + ctxUser := getDashboardContextUser(ctx) + if ctx.Written() { + return + } + + sortType := ctx.Query("sort") + page := ctx.QueryInt("page") + if page <= 1 { + page = 1 + } + + reposQuery := ctx.Query("repos") + isShowClosed := ctx.Query("state") == "closed" + + // Get repositories. + var err error + var userRepoIDs []int64 + if ctxUser.IsOrganization() { + env, err := ctxUser.AccessibleReposEnv(ctx.User.ID) + if err != nil { + ctx.ServerError("AccessibleReposEnv", err) + return + } + userRepoIDs, err = env.RepoIDs(1, ctxUser.NumRepos) + if err != nil { + ctx.ServerError("env.RepoIDs", err) + return + } + } else { + unitType := models.UnitTypeIssues + userRepoIDs, err = ctxUser.GetAccessRepoIDs(unitType) + if err != nil { + ctx.ServerError("ctxUser.GetAccessRepoIDs", err) + return + } + } + if len(userRepoIDs) == 0 { + userRepoIDs = []int64{-1} + } + + var repoIDs []int64 + if issueReposQueryPattern.MatchString(reposQuery) { + // remove "[" and "]" from string + reposQuery = reposQuery[1 : len(reposQuery)-1] + //for each ID (delimiter ",") add to int to repoIDs + reposSet := false + for _, rID := range strings.Split(reposQuery, ",") { + // Ensure nonempty string entries + if rID != "" && rID != "0" { + reposSet = true + rIDint64, err := strconv.ParseInt(rID, 10, 64) + if err == nil && com.IsSliceContainsInt64(userRepoIDs, rIDint64) { + repoIDs = append(repoIDs, rIDint64) + } + } + } + if reposSet && len(repoIDs) == 0 { + // force an empty result + repoIDs = []int64{-1} + } + } else { + log.Error("issueReposQueryPattern not match with query") + } + + if len(repoIDs) == 0 { + repoIDs = userRepoIDs + } + + counts, err := models.CountMilestonesByRepoIDs(userRepoIDs, isShowClosed) + if err != nil { + ctx.ServerError("CountMilestonesByRepoIDs", err) + return + } + + milestones, err := models.GetMilestonesByRepoIDs(repoIDs, page, isShowClosed, sortType) + if err != nil { + ctx.ServerError("GetMilestonesByRepoIDs", err) + return + } + + showReposMap := make(map[int64]*models.Repository, len(counts)) + for rID := range counts { + if rID == -1 { + break + } + repo, err := models.GetRepositoryByID(rID) + if err != nil { + if models.IsErrRepoNotExist(err) { + ctx.NotFound("GetRepositoryByID", err) + return + } else if err != nil { + ctx.ServerError("GetRepositoryByID", fmt.Errorf("[%d]%v", rID, err)) + return + } + } + showReposMap[rID] = repo + + // Check if user has access to given repository. + perm, err := models.GetUserRepoPermission(repo, ctxUser) + if err != nil { + ctx.ServerError("GetUserRepoPermission", fmt.Errorf("[%d]%v", rID, err)) + return + } + + if !perm.CanRead(models.UnitTypeIssues) { + if log.IsTrace() { + log.Trace("Permission Denied: User %-v cannot read %-v of repo %-v\n"+ + "User in repo has Permissions: %-+v", + ctxUser, + models.UnitTypeIssues, + repo, + perm) + } + ctx.Status(404) + return + } + } + + showRepos := models.RepositoryListOfMap(showReposMap) + sort.Sort(showRepos) + if err = showRepos.LoadAttributes(); err != nil { + ctx.ServerError("LoadAttributes", err) + return + } + + for _, m := range milestones { + m.Repo = showReposMap[m.RepoID] + m.RenderedContent = string(markdown.Render([]byte(m.Content), m.Repo.Link(), m.Repo.ComposeMetas())) + if m.Repo.IsTimetrackerEnabled() { + err := m.LoadTotalTrackedTime() + if err != nil { + ctx.ServerError("LoadTotalTrackedTime", err) + return + } + } + } + + milestoneStats, err := models.GetMilestonesStats(repoIDs) + if err != nil { + ctx.ServerError("GetMilestoneStats", err) + return + } + + totalMilestoneStats, err := models.GetMilestonesStats(userRepoIDs) + if err != nil { + ctx.ServerError("GetMilestoneStats", err) + return + } + + var pagerCount int + if isShowClosed { + ctx.Data["State"] = "closed" + ctx.Data["Total"] = totalMilestoneStats.ClosedCount + pagerCount = int(milestoneStats.ClosedCount) + } else { + ctx.Data["State"] = "open" + ctx.Data["Total"] = totalMilestoneStats.OpenCount + pagerCount = int(milestoneStats.OpenCount) + } + + ctx.Data["Milestones"] = milestones + ctx.Data["Repos"] = showRepos + ctx.Data["Counts"] = counts + ctx.Data["MilestoneStats"] = milestoneStats + ctx.Data["SortType"] = sortType + if len(repoIDs) != len(userRepoIDs) { + ctx.Data["RepoIDs"] = repoIDs + } + ctx.Data["IsShowClosed"] = isShowClosed + + pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5) + pager.AddParam(ctx, "repos", "RepoIDs") + pager.AddParam(ctx, "sort", "SortType") + pager.AddParam(ctx, "state", "State") + ctx.Data["Page"] = pager + + ctx.HTML(200, tplMilestones) +} + // Regexp for repos query var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`) diff --git a/routers/user/home_test.go b/routers/user/home_test.go index 9d4136ac8c..e5bbd0e98e 100644 --- a/routers/user/home_test.go +++ b/routers/user/home_test.go @@ -31,3 +31,42 @@ func TestIssues(t *testing.T) { assert.Len(t, ctx.Data["Issues"], 1) assert.Len(t, ctx.Data["Repos"], 1) } + +func TestMilestones(t *testing.T) { + setting.UI.IssuePagingNum = 1 + assert.NoError(t, models.LoadFixtures()) + + ctx := test.MockContext(t, "milestones") + test.LoadUser(t, ctx, 2) + ctx.SetParams("sort", "issues") + ctx.Req.Form.Set("state", "closed") + ctx.Req.Form.Set("sort", "furthestduedate") + Milestones(ctx) + assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) + assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) + assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"]) + assert.EqualValues(t, 1, ctx.Data["Total"]) + assert.Len(t, ctx.Data["Milestones"], 1) + assert.Len(t, ctx.Data["Repos"], 1) +} + +func TestMilestonesForSpecificRepo(t *testing.T) { + setting.UI.IssuePagingNum = 1 + assert.NoError(t, models.LoadFixtures()) + + ctx := test.MockContext(t, "milestones") + test.LoadUser(t, ctx, 2) + ctx.SetParams("sort", "issues") + ctx.SetParams("repo", "1") + ctx.Req.Form.Set("state", "closed") + ctx.Req.Form.Set("sort", "furthestduedate") + Milestones(ctx) + assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) + assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) + assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) + assert.EqualValues(t, "furthestduedate", ctx.Data["SortType"]) + assert.EqualValues(t, 1, ctx.Data["Total"]) + assert.Len(t, ctx.Data["Milestones"], 1) + assert.Len(t, ctx.Data["Repos"], 1) +} |