From d0932ef1473b9ce27474ccf24acfca106dd93fec Mon Sep 17 00:00:00 2001
From: Ethan Koenig <etk39@cornell.edu>
Date: Sat, 24 Dec 2016 05:33:21 -0500
Subject: [PATCH] Bug fixes for Issues filters (#413)

Correctly handle simultaneous assignee/poster filters, and conflicting assignee filters
---
 models/issue.go       | 79 +++++++++++++++-------------------------
 routers/repo/issue.go | 84 ++++++++++++++++++++++++-------------------
 routers/user/home.go  |  1 -
 3 files changed, 76 insertions(+), 88 deletions(-)

diff --git a/models/issue.go b/models/issue.go
index 13d27b22c6..57490bbc5b 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -855,15 +855,14 @@ func GetIssueByID(id int64) (*Issue, error) {
 
 // IssuesOptions represents options of an issue.
 type IssuesOptions struct {
-	UserID      int64
-	AssigneeID  int64
 	RepoID      int64
+	AssigneeID  int64
 	PosterID    int64
+	MentionedID int64
 	MilestoneID int64
 	RepoIDs     []int64
 	Page        int
 	IsClosed    bool
-	IsMention   bool
 	IsPull      bool
 	Labels      string
 	SortType    string
@@ -887,10 +886,18 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
 
 	if opts.AssigneeID > 0 {
 		sess.And("issue.assignee_id=?", opts.AssigneeID)
-	} else if opts.PosterID > 0 {
+	}
+
+	if opts.PosterID > 0 {
 		sess.And("issue.poster_id=?", opts.PosterID)
 	}
 
+	if opts.MentionedID > 0 {
+		sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
+			And("issue_user.is_mentioned = ?", true).
+			And("issue_user.uid = ?", opts.MentionedID)
+	}
+
 	if opts.MilestoneID > 0 {
 		sess.And("issue.milestone_id=?", opts.MilestoneID)
 	}
@@ -926,16 +933,6 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
 		}
 	}
 
-	if opts.IsMention {
-		sess.
-			Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
-			And("issue_user.is_mentioned = ?", true)
-
-		if opts.UserID > 0 {
-			sess.And("issue_user.uid = ?", opts.UserID)
-		}
-	}
-
 	issues := make([]*Issue, 0, setting.UI.IssuePagingNum)
 	if err := sess.Find(&issues); err != nil {
 		return nil, fmt.Errorf("Find: %v", err)
@@ -1156,11 +1153,11 @@ func parseCountResult(results []map[string][]byte) int64 {
 // IssueStatsOptions contains parameters accepted by GetIssueStats.
 type IssueStatsOptions struct {
 	RepoID      int64
-	UserID      int64
 	Labels      string
 	MilestoneID int64
 	AssigneeID  int64
-	FilterMode  int
+	MentionedID int64
+	PosterID    int64
 	IsPull      bool
 }
 
@@ -1191,43 +1188,25 @@ func GetIssueStats(opts *IssueStatsOptions) *IssueStats {
 			sess.And("assignee_id = ?", opts.AssigneeID)
 		}
 
+		if opts.PosterID > 0 {
+			sess.And("poster_id = ?", opts.PosterID)
+		}
+
+		if opts.MentionedID > 0 {
+			sess.Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
+			And("issue_user.uid = ?", opts.MentionedID).
+			And("issue_user.is_mentioned = ?", true)
+		}
+
 		return sess
 	}
 
-	switch opts.FilterMode {
-	case FilterModeAll, FilterModeAssign:
-		stats.OpenCount, _ = countSession(opts).
-			And("is_closed = ?", false).
-			Count(&Issue{})
-
-		stats.ClosedCount, _ = countSession(opts).
-			And("is_closed = ?", true).
-			Count(&Issue{})
-	case FilterModeCreate:
-		stats.OpenCount, _ = countSession(opts).
-			And("poster_id = ?", opts.UserID).
-			And("is_closed = ?", false).
-			Count(&Issue{})
-
-		stats.ClosedCount, _ = countSession(opts).
-			And("poster_id = ?", opts.UserID).
-			And("is_closed = ?", true).
-			Count(&Issue{})
-	case FilterModeMention:
-		stats.OpenCount, _ = countSession(opts).
-			Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
-			And("issue_user.uid = ?", opts.UserID).
-			And("issue_user.is_mentioned = ?", true).
-			And("issue.is_closed = ?", false).
-			Count(&Issue{})
-
-		stats.ClosedCount, _ = countSession(opts).
-			Join("INNER", "issue_user", "issue.id = issue_user.issue_id").
-			And("issue_user.uid = ?", opts.UserID).
-			And("issue_user.is_mentioned = ?", true).
-			And("issue.is_closed = ?", true).
-			Count(&Issue{})
-	}
+	stats.OpenCount, _ = countSession(opts).
+		And("is_closed = ?", false).
+		Count(&Issue{})
+	stats.ClosedCount, _ = countSession(opts).
+		And("is_closed = ?", true).
+		Count(&Issue{})
 	return stats
 }
 
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 343b93e273..14c1ea1aa7 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -130,38 +130,42 @@ func Issues(ctx *context.Context) {
 
 	var (
 		assigneeID = ctx.QueryInt64("assignee")
-		posterID   int64
+		posterID     int64
+		mentionedID  int64
+		forceEmpty   bool
 	)
-	filterMode := models.FilterModeAll
 	switch viewType {
 	case "assigned":
-		filterMode = models.FilterModeAssign
-		assigneeID = ctx.User.ID
+		if assigneeID > 0 && ctx.User.ID != assigneeID {
+			// two different assignees, must be empty
+			forceEmpty = true
+		} else {
+			assigneeID = ctx.User.ID
+		}
 	case "created_by":
-		filterMode = models.FilterModeCreate
 		posterID = ctx.User.ID
 	case "mentioned":
-		filterMode = models.FilterModeMention
-	}
-
-	var uid int64 = -1
-	if ctx.IsSigned {
-		uid = ctx.User.ID
+		mentionedID = ctx.User.ID
 	}
 
 	repo := ctx.Repo.Repository
 	selectLabels := ctx.Query("labels")
 	milestoneID := ctx.QueryInt64("milestone")
 	isShowClosed := ctx.Query("state") == "closed"
-	issueStats := models.GetIssueStats(&models.IssueStatsOptions{
-		RepoID:      repo.ID,
-		UserID:      uid,
-		Labels:      selectLabels,
-		MilestoneID: milestoneID,
-		AssigneeID:  assigneeID,
-		FilterMode:  filterMode,
-		IsPull:      isPullList,
-	})
+
+	var issueStats *models.IssueStats
+	if forceEmpty {
+		issueStats = &models.IssueStats{}
+	} else {
+		issueStats = models.GetIssueStats(&models.IssueStatsOptions{
+			RepoID:      repo.ID,
+			Labels:      selectLabels,
+			MilestoneID: milestoneID,
+			AssigneeID:  assigneeID,
+			MentionedID: mentionedID,
+			IsPull:      isPullList,
+		})
+	}
 
 	page := ctx.QueryInt("page")
 	if page <= 1 {
@@ -177,22 +181,28 @@ func Issues(ctx *context.Context) {
 	pager := paginater.New(total, setting.UI.IssuePagingNum, page, 5)
 	ctx.Data["Page"] = pager
 
-	issues, err := models.Issues(&models.IssuesOptions{
-		UserID:      uid,
-		AssigneeID:  assigneeID,
-		RepoID:      repo.ID,
-		PosterID:    posterID,
-		MilestoneID: milestoneID,
-		Page:        pager.Current(),
-		IsClosed:    isShowClosed,
-		IsMention:   filterMode == models.FilterModeMention,
-		IsPull:      isPullList,
-		Labels:      selectLabels,
-		SortType:    sortType,
-	})
-	if err != nil {
-		ctx.Handle(500, "Issues", err)
-		return
+
+	var issues []*models.Issue
+	if forceEmpty {
+		issues = []*models.Issue{}
+	} else {
+		var err error
+		issues, err = models.Issues(&models.IssuesOptions{
+			AssigneeID:  assigneeID,
+			RepoID:      repo.ID,
+			PosterID:    posterID,
+			MentionedID: mentionedID,
+			MilestoneID: milestoneID,
+			Page:        pager.Current(),
+			IsClosed:    isShowClosed,
+			IsPull:      isPullList,
+			Labels:      selectLabels,
+			SortType:    sortType,
+		})
+		if err != nil {
+			ctx.Handle(500, "Issues", err)
+			return
+		}
 	}
 
 	// Get issue-user relations.
@@ -233,7 +243,7 @@ func Issues(ctx *context.Context) {
 		return
 	}
 
-	if viewType == "assigned" {
+	if ctx.QueryInt64("assignee") == 0 {
 		assigneeID = 0 // Reset ID to prevent unexpected selection of assignee.
 	}
 
diff --git a/routers/user/home.go b/routers/user/home.go
index 513dd9ce06..571849df30 100644
--- a/routers/user/home.go
+++ b/routers/user/home.go
@@ -272,7 +272,6 @@ func Issues(ctx *context.Context) {
 
 	// Get issues.
 	issues, err := models.Issues(&models.IssuesOptions{
-		UserID:     ctxUser.ID,
 		AssigneeID: assigneeID,
 		RepoID:     repoID,
 		PosterID:   posterID,