You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pull_review.go 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "fmt"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/auth"
  9. "code.gitea.io/gitea/modules/context"
  10. "code.gitea.io/gitea/modules/log"
  11. "code.gitea.io/gitea/modules/notification"
  12. )
  13. // CreateCodeComment will create a code comment including an pending review if required
  14. func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) {
  15. issue := GetActionIssue(ctx)
  16. if !issue.IsPull {
  17. return
  18. }
  19. if ctx.Written() {
  20. return
  21. }
  22. if ctx.HasError() {
  23. ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
  24. ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
  25. return
  26. }
  27. var comment *models.Comment
  28. defer func() {
  29. if comment != nil {
  30. ctx.Redirect(comment.HTMLURL())
  31. } else {
  32. ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
  33. }
  34. }()
  35. signedLine := form.Line
  36. if form.Side == "previous" {
  37. signedLine *= -1
  38. }
  39. review := new(models.Review)
  40. if form.IsReview {
  41. var err error
  42. // Check if the user has already a pending review for this issue
  43. if review, err = models.GetCurrentReview(ctx.User, issue); err != nil {
  44. if !models.IsErrReviewNotExist(err) {
  45. ctx.ServerError("CreateCodeComment", err)
  46. return
  47. }
  48. // No pending review exists
  49. // Create a new pending review for this issue & user
  50. if review, err = models.CreateReview(models.CreateReviewOptions{
  51. Type: models.ReviewTypePending,
  52. Reviewer: ctx.User,
  53. Issue: issue,
  54. }); err != nil {
  55. ctx.ServerError("CreateCodeComment", err)
  56. return
  57. }
  58. }
  59. }
  60. if review.ID == 0 {
  61. review.ID = form.Reply
  62. }
  63. //FIXME check if line, commit and treepath exist
  64. comment, err := models.CreateCodeComment(
  65. ctx.User,
  66. issue.Repo,
  67. issue,
  68. form.Content,
  69. form.TreePath,
  70. signedLine,
  71. review.ID,
  72. )
  73. if err != nil {
  74. ctx.ServerError("CreateCodeComment", err)
  75. return
  76. }
  77. // Send no notification if comment is pending
  78. if !form.IsReview || form.Reply != 0 {
  79. notification.NotifyCreateIssueComment(ctx.User, issue.Repo, issue, comment)
  80. }
  81. log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID)
  82. }
  83. // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
  84. func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
  85. issue := GetActionIssue(ctx)
  86. if !issue.IsPull {
  87. return
  88. }
  89. if ctx.Written() {
  90. return
  91. }
  92. if ctx.HasError() {
  93. ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
  94. ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
  95. return
  96. }
  97. var review *models.Review
  98. var err error
  99. reviewType := form.ReviewType()
  100. switch reviewType {
  101. case models.ReviewTypeUnknown:
  102. ctx.ServerError("GetCurrentReview", fmt.Errorf("unknown ReviewType: %s", form.Type))
  103. return
  104. // can not approve/reject your own PR
  105. case models.ReviewTypeApprove, models.ReviewTypeReject:
  106. if issue.Poster.ID == ctx.User.ID {
  107. var translated string
  108. if reviewType == models.ReviewTypeApprove {
  109. translated = ctx.Tr("repo.issues.review.self.approval")
  110. } else {
  111. translated = ctx.Tr("repo.issues.review.self.rejection")
  112. }
  113. ctx.Flash.Error(translated)
  114. ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
  115. return
  116. }
  117. }
  118. review, err = models.GetCurrentReview(ctx.User, issue)
  119. if err == nil {
  120. review.Issue = issue
  121. if errl := review.LoadCodeComments(); errl != nil {
  122. ctx.ServerError("LoadCodeComments", err)
  123. return
  124. }
  125. }
  126. if ((err == nil && len(review.CodeComments) == 0) ||
  127. (err != nil && models.IsErrReviewNotExist(err))) &&
  128. form.HasEmptyContent() {
  129. ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
  130. ctx.Redirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
  131. return
  132. }
  133. if err != nil {
  134. if !models.IsErrReviewNotExist(err) {
  135. ctx.ServerError("GetCurrentReview", err)
  136. return
  137. }
  138. // No current review. Create a new one!
  139. if review, err = models.CreateReview(models.CreateReviewOptions{
  140. Type: reviewType,
  141. Issue: issue,
  142. Reviewer: ctx.User,
  143. Content: form.Content,
  144. }); err != nil {
  145. ctx.ServerError("CreateReview", err)
  146. return
  147. }
  148. } else {
  149. review.Content = form.Content
  150. review.Type = reviewType
  151. if err = models.UpdateReview(review); err != nil {
  152. ctx.ServerError("UpdateReview", err)
  153. return
  154. }
  155. }
  156. comm, err := models.CreateComment(&models.CreateCommentOptions{
  157. Type: models.CommentTypeReview,
  158. Doer: ctx.User,
  159. Content: review.Content,
  160. Issue: issue,
  161. Repo: issue.Repo,
  162. ReviewID: review.ID,
  163. })
  164. if err != nil || comm == nil {
  165. ctx.ServerError("CreateComment", err)
  166. return
  167. }
  168. if err = review.Publish(); err != nil {
  169. ctx.ServerError("Publish", err)
  170. return
  171. }
  172. pr, err := issue.GetPullRequest()
  173. if err != nil {
  174. ctx.ServerError("GetPullRequest", err)
  175. return
  176. }
  177. notification.NotifyPullRequestReview(pr, review, comm)
  178. ctx.Redirect(fmt.Sprintf("%s/pulls/%d#%s", ctx.Repo.RepoLink, issue.Index, comm.HashTag()))
  179. }