diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2018-11-29 09:46:30 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-29 09:46:30 +0800 |
commit | 2dc805c0c6e85099f3f346ba78f3a52abf032ce4 (patch) | |
tree | 2adeae8beaa207c80908914d3f6e48d82e2ae21d /routers/repo | |
parent | d5d847e5c4f0cf1470fc51f96d57917e4d9f5d83 (diff) | |
download | gitea-2dc805c0c6e85099f3f346ba78f3a52abf032ce4.tar.gz gitea-2dc805c0c6e85099f3f346ba78f3a52abf032ce4.zip |
Milestone issues and pull requests (#5293)
* add milestone issues and pulls page instead of redirecting issues page
* add milestone when creating issue from milestone page
* refactor to merge similiar codes as a new function issues
* remove milestone info on milestone issues list
* fix missing params
Diffstat (limited to 'routers/repo')
-rw-r--r-- | routers/repo/issue.go | 307 | ||||
-rw-r--r-- | routers/repo/milestone.go | 262 |
2 files changed, 313 insertions, 256 deletions
diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 8d95f33940..34a01617e4 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -14,7 +14,6 @@ import ( "net/http" "strconv" "strings" - "time" "github.com/Unknwon/com" "github.com/Unknwon/paginater" @@ -37,10 +36,6 @@ const ( tplIssueNew base.TplName = "repo/issue/new" tplIssueView base.TplName = "repo/issue/view" - tplMilestone base.TplName = "repo/issue/milestones" - tplMilestoneNew base.TplName = "repo/issue/milestone_new" - tplMilestoneEdit base.TplName = "repo/issue/milestone_edit" - tplReactions base.TplName = "repo/issue/view_content/reactions" issueTemplateKey = "IssueTemplate" @@ -91,26 +86,8 @@ func MustAllowPulls(ctx *context.Context) { } } -// Issues render issues page -func Issues(ctx *context.Context) { - isPullList := ctx.Params(":type") == "pulls" - if isPullList { - MustAllowPulls(ctx) - if ctx.Written() { - return - } - ctx.Data["Title"] = ctx.Tr("repo.pulls") - ctx.Data["PageIsPullList"] = true - - } else { - MustEnableIssues(ctx) - if ctx.Written() { - return - } - ctx.Data["Title"] = ctx.Tr("repo.issues") - ctx.Data["PageIsIssueList"] = true - } - +func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalBool) { + var err error viewType := ctx.Query("type") sortType := ctx.Query("sort") types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned"} @@ -136,7 +113,7 @@ func Issues(ctx *context.Context) { repo := ctx.Repo.Repository selectLabels := ctx.Query("labels") - milestoneID := ctx.QueryInt64("milestone") + isShowClosed := ctx.Query("state") == "closed" keyword := strings.Trim(ctx.Query("q"), " ") @@ -145,7 +122,6 @@ func Issues(ctx *context.Context) { } var issueIDs []int64 - var err error if len(keyword) > 0 { issueIDs, err = indexer.SearchIssuesByKeyword(repo.ID, keyword) if len(issueIDs) == 0 { @@ -157,7 +133,6 @@ func Issues(ctx *context.Context) { if forceEmpty { issueStats = &models.IssueStats{} } else { - var err error issueStats, err = models.GetIssueStats(&models.IssueStatsOptions{ RepoID: repo.ID, Labels: selectLabels, @@ -165,7 +140,7 @@ func Issues(ctx *context.Context) { AssigneeID: assigneeID, MentionedID: mentionedID, PosterID: posterID, - IsPull: isPullList, + IsPull: isPullOption, IssueIDs: issueIDs, }) if err != nil { @@ -200,7 +175,7 @@ func Issues(ctx *context.Context) { Page: pager.Current(), PageSize: setting.UI.IssuePagingNum, IsClosed: util.OptionalBoolOf(isShowClosed), - IsPull: util.OptionalBoolOf(isPullList), + IsPull: isPullOption, Labels: selectLabels, SortType: sortType, IssueIDs: issueIDs, @@ -223,19 +198,19 @@ func Issues(ctx *context.Context) { } ctx.Data["Issues"] = issues - // Get milestones. - ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(repo.ID) + // Get assignees. + ctx.Data["Assignees"], err = repo.GetAssignees() if err != nil { - ctx.ServerError("GetAllRepoMilestones", err) + ctx.ServerError("GetAssignees", err) return } - // Get assignees. - ctx.Data["Assignees"], err = repo.GetAssignees() + labels, err := models.GetLabelsByRepoID(repo.ID, "") if err != nil { - ctx.ServerError("GetAssignees", err) + ctx.ServerError("GetLabelsByRepoID", err) return } + ctx.Data["Labels"] = labels if ctx.QueryInt64("assignee") == 0 { assigneeID = 0 // Reset ID to prevent unexpected selection of assignee. @@ -254,6 +229,36 @@ func Issues(ctx *context.Context) { } else { ctx.Data["State"] = "open" } +} + +// Issues render issues page +func Issues(ctx *context.Context) { + isPullList := ctx.Params(":type") == "pulls" + if isPullList { + MustAllowPulls(ctx) + if ctx.Written() { + return + } + ctx.Data["Title"] = ctx.Tr("repo.pulls") + ctx.Data["PageIsPullList"] = true + } else { + MustEnableIssues(ctx) + if ctx.Written() { + return + } + ctx.Data["Title"] = ctx.Tr("repo.issues") + ctx.Data["PageIsIssueList"] = true + } + + issues(ctx, ctx.QueryInt64("milestone"), util.OptionalBoolOf(isPullList)) + + var err error + // Get milestones. + ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(ctx.Repo.Repository.ID) + if err != nil { + ctx.ServerError("GetAllRepoMilestones", err) + return + } ctx.HTML(200, tplIssues) } @@ -358,6 +363,16 @@ func NewIssue(ctx *context.Context) { ctx.Data["RequireSimpleMDE"] = true ctx.Data["RequireTribute"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes + + milestoneID := ctx.QueryInt64("milestone") + milestone, err := models.GetMilestoneByID(milestoneID) + if err != nil { + log.Error(4, "GetMilestoneByID: %d: %v", milestoneID, err) + } else { + ctx.Data["milestone_id"] = milestoneID + ctx.Data["Milestone"] = milestone + } + setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates) renderAttachmentSettings(ctx) @@ -1218,226 +1233,6 @@ func DeleteComment(ctx *context.Context) { ctx.Status(200) } -// Milestones render milestones page -func Milestones(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.milestones") - ctx.Data["PageIsIssueList"] = true - ctx.Data["PageIsMilestones"] = true - - isShowClosed := ctx.Query("state") == "closed" - openCount, closedCount, err := models.MilestoneStats(ctx.Repo.Repository.ID) - if err != nil { - ctx.ServerError("MilestoneStats", err) - return - } - ctx.Data["OpenCount"] = openCount - ctx.Data["ClosedCount"] = closedCount - - sortType := ctx.Query("sort") - page := ctx.QueryInt("page") - if page <= 1 { - page = 1 - } - - var total int - if !isShowClosed { - total = int(openCount) - } else { - total = int(closedCount) - } - ctx.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5) - - miles, err := models.GetMilestones(ctx.Repo.Repository.ID, page, isShowClosed, sortType) - if err != nil { - ctx.ServerError("GetMilestones", err) - return - } - if ctx.Repo.Repository.IsTimetrackerEnabled() { - if miles.LoadTotalTrackedTimes(); err != nil { - ctx.ServerError("LoadTotalTrackedTimes", err) - return - } - } - for _, m := range miles { - m.RenderedContent = string(markdown.Render([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) - } - ctx.Data["Milestones"] = miles - - if isShowClosed { - ctx.Data["State"] = "closed" - } else { - ctx.Data["State"] = "open" - } - - ctx.Data["SortType"] = sortType - ctx.Data["IsShowClosed"] = isShowClosed - ctx.HTML(200, tplMilestone) -} - -// NewMilestone render creating milestone page -func NewMilestone(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.milestones.new") - ctx.Data["PageIsIssueList"] = true - ctx.Data["PageIsMilestones"] = true - ctx.Data["RequireDatetimepicker"] = true - ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) - ctx.HTML(200, tplMilestoneNew) -} - -// NewMilestonePost response for creating milestone -func NewMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { - ctx.Data["Title"] = ctx.Tr("repo.milestones.new") - ctx.Data["PageIsIssueList"] = true - ctx.Data["PageIsMilestones"] = true - ctx.Data["RequireDatetimepicker"] = true - ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) - - if ctx.HasError() { - ctx.HTML(200, tplMilestoneNew) - return - } - - if len(form.Deadline) == 0 { - form.Deadline = "9999-12-31" - } - deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local) - if err != nil { - ctx.Data["Err_Deadline"] = true - ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form) - return - } - - if err = models.NewMilestone(&models.Milestone{ - RepoID: ctx.Repo.Repository.ID, - Name: form.Title, - Content: form.Content, - DeadlineUnix: util.TimeStamp(deadline.Unix()), - }); err != nil { - ctx.ServerError("NewMilestone", err) - return - } - - ctx.Flash.Success(ctx.Tr("repo.milestones.create_success", form.Title)) - ctx.Redirect(ctx.Repo.RepoLink + "/milestones") -} - -// EditMilestone render edting milestone page -func EditMilestone(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.milestones.edit") - ctx.Data["PageIsMilestones"] = true - ctx.Data["PageIsEditMilestone"] = true - ctx.Data["RequireDatetimepicker"] = true - ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) - - m, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) - if err != nil { - if models.IsErrMilestoneNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetMilestoneByRepoID", err) - } - return - } - ctx.Data["title"] = m.Name - ctx.Data["content"] = m.Content - if len(m.DeadlineString) > 0 { - ctx.Data["deadline"] = m.DeadlineString - } - ctx.HTML(200, tplMilestoneNew) -} - -// EditMilestonePost response for edting milestone -func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { - ctx.Data["Title"] = ctx.Tr("repo.milestones.edit") - ctx.Data["PageIsMilestones"] = true - ctx.Data["PageIsEditMilestone"] = true - ctx.Data["RequireDatetimepicker"] = true - ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) - - if ctx.HasError() { - ctx.HTML(200, tplMilestoneNew) - return - } - - if len(form.Deadline) == 0 { - form.Deadline = "9999-12-31" - } - deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local) - if err != nil { - ctx.Data["Err_Deadline"] = true - ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form) - return - } - - m, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) - if err != nil { - if models.IsErrMilestoneNotExist(err) { - ctx.NotFound("", nil) - } else { - ctx.ServerError("GetMilestoneByRepoID", err) - } - return - } - m.Name = form.Title - m.Content = form.Content - m.DeadlineUnix = util.TimeStamp(deadline.Unix()) - if err = models.UpdateMilestone(m); err != nil { - ctx.ServerError("UpdateMilestone", err) - return - } - - ctx.Flash.Success(ctx.Tr("repo.milestones.edit_success", m.Name)) - ctx.Redirect(ctx.Repo.RepoLink + "/milestones") -} - -// ChangeMilestonStatus response for change a milestone's status -func ChangeMilestonStatus(ctx *context.Context) { - m, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) - if err != nil { - if models.IsErrMilestoneNotExist(err) { - ctx.NotFound("", err) - } else { - ctx.ServerError("GetMilestoneByRepoID", err) - } - return - } - - switch ctx.Params(":action") { - case "open": - if m.IsClosed { - if err = models.ChangeMilestoneStatus(m, false); err != nil { - ctx.ServerError("ChangeMilestoneStatus", err) - return - } - } - ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=open") - case "close": - if !m.IsClosed { - m.ClosedDateUnix = util.TimeStampNow() - if err = models.ChangeMilestoneStatus(m, true); err != nil { - ctx.ServerError("ChangeMilestoneStatus", err) - return - } - } - ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=closed") - default: - ctx.Redirect(ctx.Repo.RepoLink + "/milestones") - } -} - -// DeleteMilestone delete a milestone -func DeleteMilestone(ctx *context.Context) { - if err := models.DeleteMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil { - ctx.Flash.Error("DeleteMilestoneByRepoID: " + err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("repo.milestones.deletion_success")) - } - - ctx.JSON(200, map[string]interface{}{ - "redirect": ctx.Repo.RepoLink + "/milestones", - }) -} - // ChangeIssueReaction create a reaction for issue func ChangeIssueReaction(ctx *context.Context, form auth.ReactionForm) { issue := GetActionIssue(ctx) diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go new file mode 100644 index 0000000000..eb2141e995 --- /dev/null +++ b/routers/repo/milestone.go @@ -0,0 +1,262 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "time" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + "github.com/Unknwon/paginater" +) + +const ( + tplMilestone base.TplName = "repo/issue/milestones" + tplMilestoneNew base.TplName = "repo/issue/milestone_new" + tplMilestoneEdit base.TplName = "repo/issue/milestone_edit" + tplMilestoneIssues base.TplName = "repo/issue/milestone_issues" +) + +// Milestones render milestones page +func Milestones(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.milestones") + ctx.Data["PageIsIssueList"] = true + ctx.Data["PageIsMilestones"] = true + + isShowClosed := ctx.Query("state") == "closed" + openCount, closedCount, err := models.MilestoneStats(ctx.Repo.Repository.ID) + if err != nil { + ctx.ServerError("MilestoneStats", err) + return + } + ctx.Data["OpenCount"] = openCount + ctx.Data["ClosedCount"] = closedCount + + sortType := ctx.Query("sort") + page := ctx.QueryInt("page") + if page <= 1 { + page = 1 + } + + var total int + if !isShowClosed { + total = int(openCount) + } else { + total = int(closedCount) + } + ctx.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5) + + miles, err := models.GetMilestones(ctx.Repo.Repository.ID, page, isShowClosed, sortType) + if err != nil { + ctx.ServerError("GetMilestones", err) + return + } + if ctx.Repo.Repository.IsTimetrackerEnabled() { + if miles.LoadTotalTrackedTimes(); err != nil { + ctx.ServerError("LoadTotalTrackedTimes", err) + return + } + } + for _, m := range miles { + m.RenderedContent = string(markdown.Render([]byte(m.Content), ctx.Repo.RepoLink, ctx.Repo.Repository.ComposeMetas())) + } + ctx.Data["Milestones"] = miles + + if isShowClosed { + ctx.Data["State"] = "closed" + } else { + ctx.Data["State"] = "open" + } + + ctx.Data["SortType"] = sortType + ctx.Data["IsShowClosed"] = isShowClosed + ctx.HTML(200, tplMilestone) +} + +// NewMilestone render creating milestone page +func NewMilestone(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.milestones.new") + ctx.Data["PageIsIssueList"] = true + ctx.Data["PageIsMilestones"] = true + ctx.Data["RequireDatetimepicker"] = true + ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) + ctx.HTML(200, tplMilestoneNew) +} + +// NewMilestonePost response for creating milestone +func NewMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { + ctx.Data["Title"] = ctx.Tr("repo.milestones.new") + ctx.Data["PageIsIssueList"] = true + ctx.Data["PageIsMilestones"] = true + ctx.Data["RequireDatetimepicker"] = true + ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) + + if ctx.HasError() { + ctx.HTML(200, tplMilestoneNew) + return + } + + if len(form.Deadline) == 0 { + form.Deadline = "9999-12-31" + } + deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local) + if err != nil { + ctx.Data["Err_Deadline"] = true + ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form) + return + } + + if err = models.NewMilestone(&models.Milestone{ + RepoID: ctx.Repo.Repository.ID, + Name: form.Title, + Content: form.Content, + DeadlineUnix: util.TimeStamp(deadline.Unix()), + }); err != nil { + ctx.ServerError("NewMilestone", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.milestones.create_success", form.Title)) + ctx.Redirect(ctx.Repo.RepoLink + "/milestones") +} + +// EditMilestone render edting milestone page +func EditMilestone(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.milestones.edit") + ctx.Data["PageIsMilestones"] = true + ctx.Data["PageIsEditMilestone"] = true + ctx.Data["RequireDatetimepicker"] = true + ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) + + m, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrMilestoneNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetMilestoneByRepoID", err) + } + return + } + ctx.Data["title"] = m.Name + ctx.Data["content"] = m.Content + if len(m.DeadlineString) > 0 { + ctx.Data["deadline"] = m.DeadlineString + } + ctx.HTML(200, tplMilestoneNew) +} + +// EditMilestonePost response for edting milestone +func EditMilestonePost(ctx *context.Context, form auth.CreateMilestoneForm) { + ctx.Data["Title"] = ctx.Tr("repo.milestones.edit") + ctx.Data["PageIsMilestones"] = true + ctx.Data["PageIsEditMilestone"] = true + ctx.Data["RequireDatetimepicker"] = true + ctx.Data["DateLang"] = setting.DateLang(ctx.Locale.Language()) + + if ctx.HasError() { + ctx.HTML(200, tplMilestoneNew) + return + } + + if len(form.Deadline) == 0 { + form.Deadline = "9999-12-31" + } + deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local) + if err != nil { + ctx.Data["Err_Deadline"] = true + ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form) + return + } + + m, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrMilestoneNotExist(err) { + ctx.NotFound("", nil) + } else { + ctx.ServerError("GetMilestoneByRepoID", err) + } + return + } + m.Name = form.Title + m.Content = form.Content + m.DeadlineUnix = util.TimeStamp(deadline.Unix()) + if err = models.UpdateMilestone(m); err != nil { + ctx.ServerError("UpdateMilestone", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.milestones.edit_success", m.Name)) + ctx.Redirect(ctx.Repo.RepoLink + "/milestones") +} + +// ChangeMilestonStatus response for change a milestone's status +func ChangeMilestonStatus(ctx *context.Context) { + m, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) + if err != nil { + if models.IsErrMilestoneNotExist(err) { + ctx.NotFound("", err) + } else { + ctx.ServerError("GetMilestoneByRepoID", err) + } + return + } + + switch ctx.Params(":action") { + case "open": + if m.IsClosed { + if err = models.ChangeMilestoneStatus(m, false); err != nil { + ctx.ServerError("ChangeMilestoneStatus", err) + return + } + } + ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=open") + case "close": + if !m.IsClosed { + m.ClosedDateUnix = util.TimeStampNow() + if err = models.ChangeMilestoneStatus(m, true); err != nil { + ctx.ServerError("ChangeMilestoneStatus", err) + return + } + } + ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=closed") + default: + ctx.Redirect(ctx.Repo.RepoLink + "/milestones") + } +} + +// DeleteMilestone delete a milestone +func DeleteMilestone(ctx *context.Context) { + if err := models.DeleteMilestoneByRepoID(ctx.Repo.Repository.ID, ctx.QueryInt64("id")); err != nil { + ctx.Flash.Error("DeleteMilestoneByRepoID: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("repo.milestones.deletion_success")) + } + + ctx.JSON(200, map[string]interface{}{ + "redirect": ctx.Repo.RepoLink + "/milestones", + }) +} + +// MilestoneIssuesAndPulls lists all the issues and pull requests of the milestone +func MilestoneIssuesAndPulls(ctx *context.Context) { + milestoneID := ctx.ParamsInt64(":id") + milestone, err := models.GetMilestoneByID(milestoneID) + if err != nil { + ctx.ServerError("GetMilestoneByID", err) + return + } + + ctx.Data["Title"] = milestone.Name + ctx.Data["Milestone"] = milestone + + issues(ctx, milestoneID, util.OptionalBoolNone) + + ctx.HTML(200, tplMilestoneIssues) +} |