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/repo | |
parent | 7be5935c55dcdf198efdf1306bbeb2b54aa0b900 (diff) | |
download | gitea-1bff02de55331e11de3627d5c5628feb2cd97387.tar.gz gitea-1bff02de55331e11de3627d5c5628feb2cd97387.zip |
Added dependencies for issues (#2196) (#2531)
Diffstat (limited to 'routers/repo')
-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 |
4 files changed, 165 insertions, 0 deletions
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, }, }) } |