From f540d0ac87fe776e71c5caeba20049f46a667efe Mon Sep 17 00:00:00 2001
From: Lunny Xiao <xiaolunwen@gmail.com>
Date: Sun, 19 Jan 2020 14:43:38 +0800
Subject: [PATCH] Fix issues/pulls dependencies problems (#9842)

* Fix issues/pulls dependencies problems

* fix swagger and api param name

* fix js
---
 modules/context/repo.go                       |  4 +--
 routers/api/v1/repo/issue.go                  | 15 +++++++++
 routers/repo/compare.go                       |  2 +-
 routers/repo/issue.go                         | 18 ++++++++---
 routers/repo/issue_dependency.go              | 32 +++++++++----------
 .../repo/issue/view_content/sidebar.tmpl      |  1 +
 templates/swagger/v1_json.tmpl                |  6 ++++
 web_src/js/index.js                           |  5 +--
 8 files changed, 57 insertions(+), 26 deletions(-)

diff --git a/modules/context/repo.go b/modules/context/repo.go
index 66700a6937..3815fc8cea 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -138,8 +138,8 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
 }
 
 // CanCreateIssueDependencies returns whether or not a user can create dependencies.
-func (r *Repository) CanCreateIssueDependencies(user *models.User) bool {
-	return r.Permission.CanWrite(models.UnitTypeIssues) && r.Repository.IsDependenciesEnabled()
+func (r *Repository) CanCreateIssueDependencies(user *models.User, isPull bool) bool {
+	return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
 }
 
 // GetCommitsCount returns cached commit count for current view
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index 1219ef2e41..2463586e71 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -51,6 +51,10 @@ func SearchIssues(ctx *context.APIContext) {
 	//   description: repository to prioritize in the results
 	//   type: integer
 	//   format: int64
+	// - name: type
+	//   in: query
+	//   description: filter by type (issues / pulls) if set
+	//   type: string
 	// responses:
 	//   "200":
 	//     "$ref": "#/responses/IssueList"
@@ -128,6 +132,16 @@ func SearchIssues(ctx *context.APIContext) {
 		}
 	}
 
+	var isPull util.OptionalBool
+	switch ctx.Query("type") {
+	case "pulls":
+		isPull = util.OptionalBoolTrue
+	case "issues":
+		isPull = util.OptionalBoolFalse
+	default:
+		isPull = util.OptionalBoolNone
+	}
+
 	// Only fetch the issues if we either don't have a keyword or the search returned issues
 	// This would otherwise return all issues if no issues were found by the search.
 	if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
@@ -140,6 +154,7 @@ func SearchIssues(ctx *context.APIContext) {
 			LabelIDs:       labelIDs,
 			SortType:       "priorityrepo",
 			PriorityRepoID: ctx.QueryInt64("priority_repo_id"),
+			IsPull:         isPull,
 		})
 	}
 
diff --git a/routers/repo/compare.go b/routers/repo/compare.go
index 5d8f52b007..833b7d9182 100644
--- a/routers/repo/compare.go
+++ b/routers/repo/compare.go
@@ -413,7 +413,7 @@ func CompareDiff(ctx *context.Context) {
 
 		if !nothingToCompare {
 			// Setup information for new form.
-			RetrieveRepoMetas(ctx, ctx.Repo.Repository)
+			RetrieveRepoMetas(ctx, ctx.Repo.Repository, true)
 			if ctx.Written() {
 				return
 			}
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 4d83773774..c2aa1a83bb 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -343,7 +343,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *models.Repos
 }
 
 // RetrieveRepoMetas find all the meta information of a repository
-func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository) []*models.Label {
+func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull bool) []*models.Label {
 	if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
 		return nil
 	}
@@ -368,7 +368,7 @@ 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)
+	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, isPull)
 
 	return labels
 }
@@ -438,7 +438,7 @@ func NewIssue(ctx *context.Context) {
 	setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates)
 	renderAttachmentSettings(ctx)
 
-	RetrieveRepoMetas(ctx, ctx.Repo.Repository)
+	RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
 	if ctx.Written() {
 		return
 	}
@@ -453,7 +453,7 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b
 		err  error
 	)
 
-	labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository)
+	labels := RetrieveRepoMetas(ctx, ctx.Repo.Repository, isPull)
 	if ctx.Written() {
 		return nil, nil, 0
 	}
@@ -665,6 +665,14 @@ func ViewIssue(ctx *context.Context) {
 		ctx.Data["PageIsIssueList"] = true
 	}
 
+	if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) {
+		ctx.Data["IssueType"] = "pulls"
+	} else if !issue.IsPull && !ctx.Repo.CanRead(models.UnitTypePullRequests) {
+		ctx.Data["IssueType"] = "issues"
+	} else {
+		ctx.Data["IssueType"] = "all"
+	}
+
 	ctx.Data["RequireHighlightJS"] = true
 	ctx.Data["RequireDropzone"] = true
 	ctx.Data["RequireTribute"] = true
@@ -802,7 +810,7 @@ func ViewIssue(ctx *context.Context) {
 	}
 
 	// Check if the user can use the dependencies
-	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User)
+	ctx.Data["CanCreateIssueDependencies"] = ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull)
 
 	// check if dependencies can be created across repositories
 	ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies
diff --git a/routers/repo/issue_dependency.go b/routers/repo/issue_dependency.go
index d865a56518..055b5ed2af 100644
--- a/routers/repo/issue_dependency.go
+++ b/routers/repo/issue_dependency.go
@@ -14,14 +14,6 @@ import (
 
 // 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 {
@@ -29,6 +21,14 @@ func AddDependency(ctx *context.Context) {
 		return
 	}
 
+	// Check if the Repo is allowed to have dependencies
+	if !ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull) {
+		ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
+		return
+	}
+
+	depID := ctx.QueryInt64("newDependency")
+
 	if err = issue.LoadRepo(); err != nil {
 		ctx.ServerError("LoadRepo", err)
 		return
@@ -73,14 +73,6 @@ func AddDependency(ctx *context.Context) {
 
 // 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 {
@@ -88,6 +80,14 @@ func RemoveDependency(ctx *context.Context) {
 		return
 	}
 
+	// Check if the Repo is allowed to have dependencies
+	if !ctx.Repo.CanCreateIssueDependencies(ctx.User, issue.IsPull) {
+		ctx.Error(http.StatusForbidden, "CanCreateIssueDependencies")
+		return
+	}
+
+	depID := ctx.QueryInt64("removeDependencyID")
+
 	if err = issue.LoadRepo(); err != nil {
 		ctx.ServerError("LoadRepo", err)
 		return
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl
index dd58e8b216..02564643cc 100644
--- a/templates/repo/issue/view_content/sidebar.tmpl
+++ b/templates/repo/issue/view_content/sidebar.tmpl
@@ -428,6 +428,7 @@
 	<input type="hidden" id="repolink" value="{{$.RepoRelPath}}">
 	<input type="hidden" id="repoId" value="{{.Repository.ID}}">
 	<input type="hidden" id="crossRepoSearch" value="{{.AllowCrossRepositoryDependencies}}">
+	<input type="hidden" id="type" value="{{.IssueType}}">
 	<!-- I know, there is probably a better way to do this -->
 	<input type="hidden" id="issueIndex" value="{{.Issue.Index}}"/>
 
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 8ff4597b2e..95932d9e02 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -1397,6 +1397,12 @@
             "description": "repository to prioritize in the results",
             "name": "priority_repo_id",
             "in": "query"
+          },
+          {
+            "type": "string",
+            "description": "filter by type (issues / pulls) if set",
+            "name": "type",
+            "in": "query"
           }
         ],
         "responses": {
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 7c3749c08b..619ca74e52 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -3455,9 +3455,10 @@ function initIssueList() {
   const repolink = $('#repolink').val();
   const repoId = $('#repoId').val();
   const crossRepoSearch = $('#crossRepoSearch').val();
-  let issueSearchUrl = `${suburl}/api/v1/repos/${repolink}/issues?q={query}`;
+  const tp = $('#type').val();
+  let issueSearchUrl = `${suburl}/api/v1/repos/${repolink}/issues?q={query}&type=${tp}`;
   if (crossRepoSearch === 'true') {
-    issueSearchUrl = `${suburl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}`;
+    issueSearchUrl = `${suburl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`;
   }
   $('#new-dependency-drop-list')
     .dropdown({