aboutsummaryrefslogtreecommitdiffstats
path: root/routers/repo
diff options
context:
space:
mode:
author赵智超 <1012112796@qq.com>2020-04-07 00:33:34 +0800
committerGitHub <noreply@github.com>2020-04-06 19:33:34 +0300
commitef89e75d0eb232e98ca7a7ef278b8681c7f4fe50 (patch)
treec7b17df4d92441f2ce8420dbf8aa79aee47a3c13 /routers/repo
parent88c14326b1a5d9216d5f6905dcbe44e43efdb5b3 (diff)
downloadgitea-ef89e75d0eb232e98ca7a7ef278b8681c7f4fe50.tar.gz
gitea-ef89e75d0eb232e98ca7a7ef278b8681c7f4fe50.zip
add request review from specific reviewers feature in pull request (#10756)
* add request review feature in pull request add a way to notify specific reviewers to review like github , by add or delet a special type review . The acton is is similar to Assign , so many code reuse the function and items of Assignee, but the meaning and result is different. The Permission style is is similar to github, that only writer can add a review request from Reviewers, but the poster can recall and remove a review request after a reviwer has revied even if he don't have Write Premission. only manager , the poster and reviewer of a request review can remove it. The reviewers can be requested to review contain all readers for private repo , for public, contain all writers and watchers. The offical Review Request will block merge if Reject can block it. an other change: add ui otify for Assignees. Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv> Signed-off-by: a1012112796 <1012112796@qq.com> * new change * add placeholder string * do some changes follow #10238 to add review requests num on lists also change icon for review requests to eye Co-authored-by: Lauris BH <lauris@nix.lv>
Diffstat (limited to 'routers/repo')
-rw-r--r--routers/repo/issue.go152
1 files changed, 151 insertions, 1 deletions
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 0697d11a66..0a059bf789 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -289,6 +289,8 @@ func issues(ctx *context.Context, milestoneID int64, isPullOption util.OptionalB
reviewTyp := models.ReviewTypeApprove
if typ == "reject" {
reviewTyp = models.ReviewTypeReject
+ } else if typ == "waiting" {
+ reviewTyp = models.ReviewTypeRequest
}
for _, count := range counts {
if count.Type == reviewTyp {
@@ -377,6 +379,16 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
}
}
+// RetrieveRepoReviewers find all reviewers of a repository
+func RetrieveRepoReviewers(ctx *context.Context, repo *models.Repository, issuePosterID int64) {
+ var err error
+ ctx.Data["Reviewers"], err = repo.GetReviewers(ctx.User.ID, issuePosterID)
+ if err != nil {
+ ctx.ServerError("GetReviewers", err)
+ return
+ }
+}
+
// RetrieveRepoMetas find all the meta information of a repository
func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull bool) []*models.Label {
if !ctx.Repo.CanWriteIssuesOrPulls(isPull) {
@@ -815,6 +827,28 @@ func ViewIssue(ctx *context.Context) {
}
}
+ if issue.IsPull {
+ canChooseReviewer := ctx.Repo.CanWrite(models.UnitTypePullRequests)
+ if !canChooseReviewer && ctx.User != nil && ctx.IsSigned {
+ canChooseReviewer, err = models.IsOfficialReviewer(issue, ctx.User)
+ if err != nil {
+ ctx.ServerError("IsOfficialReviewer", err)
+ return
+ }
+ }
+
+ if canChooseReviewer {
+ RetrieveRepoReviewers(ctx, repo, issue.PosterID)
+ ctx.Data["CanChooseReviewer"] = true
+ } else {
+ ctx.Data["CanChooseReviewer"] = false
+ }
+
+ if ctx.Written() {
+ return
+ }
+ }
+
if ctx.IsSigned {
// Update issue-user.
if err = issue.ReadBy(ctx.User.ID); err != nil {
@@ -926,7 +960,7 @@ func ViewIssue(ctx *context.Context) {
if comment.MilestoneID > 0 && comment.Milestone == nil {
comment.Milestone = ghostMilestone
}
- } else if comment.Type == models.CommentTypeAssignees {
+ } else if comment.Type == models.CommentTypeAssignees || comment.Type == models.CommentTypeReviewRequest {
if err = comment.LoadAssigneeUser(); err != nil {
ctx.ServerError("LoadAssigneeUser", err)
return
@@ -1273,6 +1307,122 @@ func UpdateIssueAssignee(ctx *context.Context) {
})
}
+func isLegalReviewRequest(reviewer, doer *models.User, isAdd bool, issue *models.Issue) error {
+ if reviewer.IsOrganization() {
+ return fmt.Errorf("Organization can't be added as reviewer [user_id: %d, repo_id: %d]", reviewer.ID, issue.PullRequest.BaseRepo.ID)
+ }
+ if doer.IsOrganization() {
+ return fmt.Errorf("Organization can't be doer to add reviewer [user_id: %d, repo_id: %d]", doer.ID, issue.PullRequest.BaseRepo.ID)
+ }
+
+ permReviewer, err := models.GetUserRepoPermission(issue.Repo, reviewer)
+ if err != nil {
+ return err
+ }
+
+ permDoer, err := models.GetUserRepoPermission(issue.Repo, doer)
+ if err != nil {
+ return err
+ }
+
+ lastreview, err := models.GetReviewerByIssueIDAndUserID(issue.ID, reviewer.ID)
+ if err != nil {
+ return err
+ }
+
+ var pemResult bool
+ if isAdd {
+ pemResult = permReviewer.CanAccessAny(models.AccessModeRead, models.UnitTypePullRequests)
+ if !pemResult {
+ return fmt.Errorf("Reviewer can't read [user_id: %d, repo_name: %s]", reviewer.ID, issue.Repo.Name)
+ }
+
+ if doer.ID == issue.PosterID && lastreview != nil && lastreview.Type != models.ReviewTypeRequest {
+ return nil
+ }
+
+ pemResult = permDoer.CanAccessAny(models.AccessModeWrite, models.UnitTypePullRequests)
+ if !pemResult {
+ pemResult, err = models.IsOfficialReviewer(issue, doer)
+ if err != nil {
+ return err
+ }
+ if !pemResult {
+ return fmt.Errorf("Doer can't choose reviewer [user_id: %d, repo_name: %s, issue_id: %d]", doer.ID, issue.Repo.Name, issue.ID)
+ }
+ }
+
+ if doer.ID == reviewer.ID {
+ return fmt.Errorf("doer can't be reviewer [user_id: %d, repo_name: %s]", doer.ID, issue.Repo.Name)
+ }
+
+ if reviewer.ID == issue.PosterID {
+ return fmt.Errorf("poster of pr can't be reviewer [user_id: %d, repo_name: %s]", reviewer.ID, issue.Repo.Name)
+ }
+ } else {
+ if lastreview.Type == models.ReviewTypeRequest && lastreview.ReviewerID == doer.ID {
+ return nil
+ }
+
+ pemResult = permDoer.IsAdmin()
+ if !pemResult {
+ return fmt.Errorf("Doer is not admin [user_id: %d, repo_name: %s]", doer.ID, issue.Repo.Name)
+ }
+ }
+
+ return nil
+}
+
+// updatePullReviewRequest change pull's request reviewers
+func updatePullReviewRequest(ctx *context.Context) {
+ issues := getActionIssues(ctx)
+ if ctx.Written() {
+ return
+ }
+
+ reviewID := ctx.QueryInt64("id")
+ event := ctx.Query("is_add")
+
+ if event != "add" && event != "remove" {
+ ctx.ServerError("updatePullReviewRequest", fmt.Errorf("is_add should not be \"%s\"", event))
+ return
+ }
+
+ for _, issue := range issues {
+ if issue.IsPull {
+
+ reviewer, err := models.GetUserByID(reviewID)
+ if err != nil {
+ ctx.ServerError("GetUserByID", err)
+ return
+ }
+
+ err = isLegalReviewRequest(reviewer, ctx.User, event == "add", issue)
+ if err != nil {
+ ctx.ServerError("isLegalRequestReview", err)
+ return
+ }
+
+ err = issue_service.ReviewRequest(issue, ctx.User, reviewer, event == "add")
+ if err != nil {
+ ctx.ServerError("ReviewRequest", err)
+ return
+ }
+ } else {
+ ctx.ServerError("updatePullReviewRequest", fmt.Errorf("%d in %d is not Pull Request", issue.ID, issue.Repo.ID))
+ }
+ }
+
+ ctx.JSON(200, map[string]interface{}{
+ "ok": true,
+ })
+}
+
+// UpdatePullReviewRequest add or remove review request
+func UpdatePullReviewRequest(ctx *context.Context) {
+ updatePullReviewRequest(ctx)
+}
+
// UpdateIssueStatus change issue's status
func UpdateIssueStatus(ctx *context.Context) {
issues := getActionIssues(ctx)