diff options
author | kolaente <konrad@kola-entertainments.de> | 2018-07-17 23:23:58 +0200 |
---|---|---|
committer | techknowlogick <techknowlogick@users.noreply.github.com> | 2018-07-17 17:23:58 -0400 |
commit | 1bff02de55331e11de3627d5c5628feb2cd97387 (patch) | |
tree | d6d6ace5f246c1555b294bf096763260f7d74d7b /routers | |
parent | 7be5935c55dcdf198efdf1306bbeb2b54aa0b900 (diff) | |
download | gitea-1bff02de55331e11de3627d5c5628feb2cd97387.tar.gz gitea-1bff02de55331e11de3627d5c5628feb2cd97387.zip |
Added dependencies for issues (#2196) (#2531)
Diffstat (limited to 'routers')
-rw-r--r-- | routers/api/v1/repo/issue.go | 9 | ||||
-rw-r--r-- | routers/api/v1/repo/pull.go | 5 | ||||
-rw-r--r-- | routers/repo/issue.go | 33 | ||||
-rw-r--r-- | routers/repo/issue_dependency.go | 119 | ||||
-rw-r--r-- | routers/repo/pull.go | 12 | ||||
-rw-r--r-- | routers/repo/setting.go | 1 | ||||
-rw-r--r-- | routers/routes/routes.go | 4 |
7 files changed, 183 insertions, 0 deletions
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 76f58b244e..f8ef0fe3d9 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -7,6 +7,7 @@ package repo import ( "fmt" + "net/http" "strings" "code.gitea.io/gitea/models" @@ -208,6 +209,10 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) { if form.Closed { if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, true); err != nil { + if models.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") + return + } ctx.Error(500, "ChangeStatus", err) return } @@ -325,6 +330,10 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { } if form.State != nil { if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil { + if models.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") + return + } ctx.Error(500, "ChangeStatus", err) return } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 78d96c647f..c346d81e33 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -6,6 +6,7 @@ package repo import ( "fmt" + "net/http" "strings" "code.gitea.io/git" @@ -378,6 +379,10 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) { } if form.State != nil { if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil { + if models.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies") + return + } ctx.Error(500, "ChangeStatus", err) return } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 6f7dc44595..27c95cd9dc 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "io/ioutil" + "net/http" "strconv" "strings" "time" @@ -302,6 +303,9 @@ func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models. } ctx.Data["Branches"] = brs + // Contains true if the user can create issue dependencies + ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User) + return labels } @@ -665,6 +669,9 @@ func ViewIssue(ctx *context.Context) { } } + // Check if the user can use the dependencies + ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User) + // Render comments and and fetch participants. participants[0] = issue.Poster for _, comment = range issue.Comments { @@ -721,6 +728,11 @@ func ViewIssue(ctx *context.Context) { ctx.ServerError("LoadAssigneeUser", err) return } + } else if comment.Type == models.CommentTypeRemoveDependency || comment.Type == models.CommentTypeAddDependency { + if err = comment.LoadDepIssueDetails(); err != nil { + ctx.ServerError("LoadDepIssueDetails", err) + return + } } } @@ -774,6 +786,10 @@ func ViewIssue(ctx *context.Context) { ctx.Data["IsPullBranchDeletable"] = canDelete && pull.HeadRepo != nil && git.IsBranchExist(pull.HeadRepo.RepoPath(), pull.HeadBranch) } + // Get Dependencies + ctx.Data["BlockedByDependencies"], err = issue.BlockedByDependencies() + ctx.Data["BlockingDependencies"], err = issue.BlockingDependencies() + ctx.Data["Participants"] = participants ctx.Data["NumParticipants"] = len(participants) ctx.Data["Issue"] = issue @@ -971,6 +987,12 @@ func UpdateIssueStatus(ctx *context.Context) { } for _, issue := range issues { if err := issue.ChangeStatus(ctx.User, issue.Repo, isClosed); err != nil { + if models.IsErrDependenciesLeft(err) { + ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{ + "error": "cannot close this issue because it still has open dependencies", + }) + return + } ctx.ServerError("ChangeStatus", err) return } @@ -1034,6 +1056,17 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) { } else { if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, form.Status == "close"); err != nil { log.Error(4, "ChangeStatus: %v", err) + + if models.IsErrDependenciesLeft(err) { + if issue.IsPull { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked")) + ctx.Redirect(fmt.Sprintf("%s/pulls/%d", ctx.Repo.RepoLink, issue.Index), http.StatusSeeOther) + } else { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.issue_close_blocked")) + ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index), http.StatusSeeOther) + } + return + } } else { log.Trace("Issue [%d] status changed to closed: %v", issue.ID, issue.IsClosed) diff --git a/routers/repo/issue_dependency.go b/routers/repo/issue_dependency.go new file mode 100644 index 0000000000..730271126d --- /dev/null +++ b/routers/repo/issue_dependency.go @@ -0,0 +1,119 @@ +// 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 ( + "fmt" + "net/http" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" +) + +// AddDependency adds new dependencies +func AddDependency(ctx *context.Context) { + // Check if the Repo is allowed to have dependencies + if !ctx.Repo.CanCreateIssueDependencies(ctx.User) { + ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies") + return + } + + depID := ctx.QueryInt64("newDependency") + + issueIndex := ctx.ParamsInt64("index") + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex) + if err != nil { + ctx.ServerError("GetIssueByIndex", err) + return + } + + // Redirect + defer ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther) + + // Dependency + dep, err := models.GetIssueByID(depID) + if err != nil { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_dep_issue_not_exist")) + return + } + + // Check if both issues are in the same repo + if issue.RepoID != dep.RepoID { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_dep_not_same_repo")) + return + } + + // Check if issue and dependency is the same + if dep.Index == issueIndex { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_same_issue")) + return + } + + err = models.CreateIssueDependency(ctx.User, issue, dep) + if err != nil { + if models.IsErrDependencyExists(err) { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_dep_exists")) + return + } else if models.IsErrCircularDependency(err) { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_cannot_create_circular")) + return + } else { + ctx.ServerError("CreateOrUpdateIssueDependency", err) + return + } + } +} + +// RemoveDependency removes the dependency +func RemoveDependency(ctx *context.Context) { + // Check if the Repo is allowed to have dependencies + if !ctx.Repo.CanCreateIssueDependencies(ctx.User) { + ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies") + return + } + + depID := ctx.QueryInt64("removeDependencyID") + + issueIndex := ctx.ParamsInt64("index") + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, issueIndex) + if err != nil { + ctx.ServerError("GetIssueByIndex", err) + return + } + + // Redirect + ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issueIndex), http.StatusSeeOther) + + // Dependency Type + depTypeStr := ctx.Req.PostForm.Get("dependencyType") + + var depType models.DependencyType + + switch depTypeStr { + case "blockedBy": + depType = models.DependencyTypeBlockedBy + case "blocking": + depType = models.DependencyTypeBlocking + default: + ctx.Error(http.StatusBadRequest, "GetDependecyType") + return + } + + // Dependency + dep, err := models.GetIssueByID(depID) + if err != nil { + ctx.ServerError("GetIssueByID", err) + return + } + + if err = models.RemoveIssueDependency(ctx.User, issue, dep, depType); err != nil { + if models.IsErrDependencyNotExists(err) { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.add_error_dep_not_exist")) + return + } + ctx.ServerError("RemoveIssueDependency", err) + return + } +} diff --git a/routers/repo/pull.go b/routers/repo/pull.go index dfe07ca44a..c8d65731ee 100644 --- a/routers/repo/pull.go +++ b/routers/repo/pull.go @@ -524,6 +524,18 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) { pr.Issue = issue pr.Issue.Repo = ctx.Repo.Repository + + noDeps, err := models.IssueNoDependenciesLeft(issue) + if err != nil { + return + } + + if !noDeps { + ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked")) + ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index)) + return + } + if err = pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil { if models.IsErrInvalidMergeStyle(err) { ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option")) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 0d44cb50a8..fa3bd434d5 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -202,6 +202,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { Config: &models.IssuesConfig{ EnableTimetracker: form.EnableTimetracker, AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, + EnableDependencies: form.EnableIssueDependencies, }, }) } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 84158de21b..3eaaff60b6 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -523,6 +523,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/title", repo.UpdateIssueTitle) m.Post("/content", repo.UpdateIssueContent) m.Post("/watch", repo.IssueWatch) + m.Group("/dependency", func() { + m.Post("/add", repo.AddDependency) + m.Post("/delete", repo.RemoveDependency) + }) m.Combo("/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment) m.Group("/times", func() { m.Post("/add", bindIgnErr(auth.AddTimeManuallyForm{}), repo.AddTimeManually) |