forgejo/routers/web/repo/pull_review.go
zeripath 5cb0c9aa0d
Propagate context and ensure git commands run in request context ()
This PR continues the work in  by progressively ensuring that git
commands run within the request context.

This now means that the if there is a git repo already open in the context it will be used instead of reopening it.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-01-19 23:26:57 +00:00

245 lines
6.5 KiB
Go

// 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/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
)
const (
tplConversation base.TplName = "repo/diff/conversation"
tplNewComment base.TplName = "repo/diff/new_comment"
)
// RenderNewCodeCommentForm will render the form for creating a new review comment
func RenderNewCodeCommentForm(ctx *context.Context) {
issue := GetActionIssue(ctx)
if !issue.IsPull {
return
}
currentReview, err := models.GetCurrentReview(ctx.User, issue)
if err != nil && !models.IsErrReviewNotExist(err) {
ctx.ServerError("GetCurrentReview", err)
return
}
ctx.Data["PageIsPullFiles"] = true
ctx.Data["Issue"] = issue
ctx.Data["CurrentReview"] = currentReview
pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(issue.PullRequest.GetGitRefName())
if err != nil {
ctx.ServerError("GetRefCommitID", err)
return
}
ctx.Data["AfterCommitID"] = pullHeadCommitID
ctx.HTML(http.StatusOK, tplNewComment)
}
// CreateCodeComment will create a code comment including an pending review if required
func CreateCodeComment(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.CodeCommentForm)
issue := GetActionIssue(ctx)
if !issue.IsPull {
return
}
if ctx.Written() {
return
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
signedLine := form.Line
if form.Side == "previous" {
signedLine *= -1
}
comment, err := pull_service.CreateCodeComment(ctx,
ctx.User,
ctx.Repo.GitRepo,
issue,
signedLine,
form.Content,
form.TreePath,
form.IsReview,
form.Reply,
form.LatestCommitID,
)
if err != nil {
ctx.ServerError("CreateCodeComment", err)
return
}
if comment == nil {
log.Trace("Comment not created: %-v #%d[%d]", ctx.Repo.Repository, issue.Index, issue.ID)
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
log.Trace("Comment created: %-v #%d[%d] Comment[%d]", ctx.Repo.Repository, issue.Index, issue.ID, comment.ID)
if form.Origin == "diff" {
renderConversation(ctx, comment)
return
}
ctx.Redirect(comment.HTMLURL())
}
// UpdateResolveConversation add or remove an Conversation resolved mark
func UpdateResolveConversation(ctx *context.Context) {
origin := ctx.FormString("origin")
action := ctx.FormString("action")
commentID := ctx.FormInt64("comment_id")
comment, err := models.GetCommentByID(commentID)
if err != nil {
ctx.ServerError("GetIssueByID", err)
return
}
if err = comment.LoadIssue(); err != nil {
ctx.ServerError("comment.LoadIssue", err)
return
}
var permResult bool
if permResult, err = models.CanMarkConversation(comment.Issue, ctx.User); err != nil {
ctx.ServerError("CanMarkConversation", err)
return
}
if !permResult {
ctx.Error(http.StatusForbidden)
return
}
if !comment.Issue.IsPull {
ctx.Error(http.StatusBadRequest)
return
}
if action == "Resolve" || action == "UnResolve" {
err = models.MarkConversation(comment, ctx.User, action == "Resolve")
if err != nil {
ctx.ServerError("MarkConversation", err)
return
}
} else {
ctx.Error(http.StatusBadRequest)
return
}
if origin == "diff" {
renderConversation(ctx, comment)
return
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"ok": true,
})
}
func renderConversation(ctx *context.Context, comment *models.Comment) {
comments, err := models.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.User, comment.TreePath, comment.Line)
if err != nil {
ctx.ServerError("FetchCodeCommentsByLine", err)
return
}
ctx.Data["PageIsPullFiles"] = true
ctx.Data["comments"] = comments
ctx.Data["CanMarkConversation"] = true
ctx.Data["Issue"] = comment.Issue
if err = comment.Issue.LoadPullRequest(); err != nil {
ctx.ServerError("comment.Issue.LoadPullRequest", err)
return
}
pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(comment.Issue.PullRequest.GetGitRefName())
if err != nil {
ctx.ServerError("GetRefCommitID", err)
return
}
ctx.Data["AfterCommitID"] = pullHeadCommitID
ctx.HTML(http.StatusOK, tplConversation)
}
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
func SubmitReview(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.SubmitReviewForm)
issue := GetActionIssue(ctx)
if !issue.IsPull {
return
}
if ctx.Written() {
return
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
reviewType := form.ReviewType()
switch reviewType {
case models.ReviewTypeUnknown:
ctx.ServerError("ReviewType", fmt.Errorf("unknown ReviewType: %s", form.Type))
return
// can not approve/reject your own PR
case models.ReviewTypeApprove, models.ReviewTypeReject:
if issue.IsPoster(ctx.User.ID) {
var translated string
if reviewType == models.ReviewTypeApprove {
translated = ctx.Tr("repo.issues.review.self.approval")
} else {
translated = ctx.Tr("repo.issues.review.self.rejection")
}
ctx.Flash.Error(translated)
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
return
}
}
var attachments []string
if setting.Attachment.Enabled {
attachments = form.Files
}
_, comm, err := pull_service.SubmitReview(ctx, ctx.User, ctx.Repo.GitRepo, issue, reviewType, form.Content, form.CommitID, attachments)
if err != nil {
if models.IsContentEmptyErr(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
} else {
ctx.ServerError("SubmitReview", err)
}
return
}
ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, issue.Index, comm.HashTag()))
}
// DismissReview dismissing stale review by repo admin
func DismissReview(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.DismissReviewForm)
comm, err := pull_service.DismissReview(ctx, form.ReviewID, form.Message, ctx.User, true)
if err != nil {
ctx.ServerError("pull_service.DismissReview", err)
return
}
ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, comm.Issue.Index, comm.HashTag()))
}