| @@ -384,6 +384,7 @@ func runWeb(ctx *cli.Context) { | |||||
| m.Group("/:org", func() { | m.Group("/:org", func() { | ||||
| m.Get("/dashboard", user.Dashboard) | m.Get("/dashboard", user.Dashboard) | ||||
| m.Get("/issues", user.Issues) | |||||
| m.Get("/members", org.Members) | m.Get("/members", org.Members) | ||||
| m.Get("/members/action/:action", org.MembersAction) | m.Get("/members/action/:action", org.MembersAction) | ||||
| @@ -404,11 +404,13 @@ func GetIssueByID(id int64) (*Issue, error) { | |||||
| } | } | ||||
| // Issues returns a list of issues by given conditions. | // Issues returns a list of issues by given conditions. | ||||
| func Issues(uid, assigneeID, repoID, posterID, milestoneID int64, page int, isClosed, isMention bool, labels, sortType string) ([]*Issue, error) { | |||||
| func Issues(uid, assigneeID, repoID, posterID, milestoneID int64, repoIDs []int64, page int, isClosed, isMention bool, labels, sortType string) ([]*Issue, error) { | |||||
| sess := x.Limit(setting.IssuePagingNum, (page-1)*setting.IssuePagingNum) | sess := x.Limit(setting.IssuePagingNum, (page-1)*setting.IssuePagingNum) | ||||
| if repoID > 0 { | if repoID > 0 { | ||||
| sess.Where("issue.repo_id=?", repoID).And("issue.is_closed=?", isClosed) | sess.Where("issue.repo_id=?", repoID).And("issue.is_closed=?", isClosed) | ||||
| } else if repoIDs != nil { | |||||
| sess.Where("issue.repo_id IN (?)", strings.Join(base.Int64sToStrings(repoIDs), ",")).And("issue.is_closed=?", isClosed) | |||||
| } else { | } else { | ||||
| sess.Where("issue.is_closed=?", isClosed) | sess.Where("issue.is_closed=?", isClosed) | ||||
| } | } | ||||
| @@ -682,7 +684,7 @@ func GetIssueStats(repoID, uid, labelID, milestoneID, assigneeID int64, filterMo | |||||
| } | } | ||||
| // GetUserIssueStats returns issue statistic information for dashboard by given conditions. | // GetUserIssueStats returns issue statistic information for dashboard by given conditions. | ||||
| func GetUserIssueStats(repoID, uid int64, filterMode int) *IssueStats { | |||||
| func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int) *IssueStats { | |||||
| stats := &IssueStats{} | stats := &IssueStats{} | ||||
| issue := new(Issue) | issue := new(Issue) | ||||
| stats.AssignCount, _ = x.Where("assignee_id=?", uid).And("is_closed=?", false).Count(issue) | stats.AssignCount, _ = x.Where("assignee_id=?", uid).And("is_closed=?", false).Count(issue) | ||||
| @@ -692,6 +694,8 @@ func GetUserIssueStats(repoID, uid int64, filterMode int) *IssueStats { | |||||
| baseCond := " WHERE issue.is_closed=?" | baseCond := " WHERE issue.is_closed=?" | ||||
| if repoID > 0 { | if repoID > 0 { | ||||
| baseCond += " AND issue.repo_id=" + com.ToStr(repoID) | baseCond += " AND issue.repo_id=" + com.ToStr(repoID) | ||||
| } else { | |||||
| baseCond += " AND issue.repo_id IN (" + strings.Join(base.Int64sToStrings(repoIDs), ",") + ")" | |||||
| } | } | ||||
| switch filterMode { | switch filterMode { | ||||
| case FM_ASSIGN: | case FM_ASSIGN: | ||||
| @@ -434,6 +434,15 @@ func StringsToInt64s(strs []string) []int64 { | |||||
| return ints | return ints | ||||
| } | } | ||||
| // Int64sToStrings converts a slice of int64 to a slice of string. | |||||
| func Int64sToStrings(ints []int64) []string { | |||||
| strs := make([]string, len(ints)) | |||||
| for i := range ints { | |||||
| strs[i] = com.ToStr(ints[i]) | |||||
| } | |||||
| return strs | |||||
| } | |||||
| // Int64sToMap converts a slice of int64 to a int64 map. | // Int64sToMap converts a slice of int64 to a int64 map. | ||||
| func Int64sToMap(ints []int64) map[int64]bool { | func Int64sToMap(ints []int64) map[int64]bool { | ||||
| m := make(map[int64]bool) | m := make(map[int64]bool) | ||||
| @@ -114,7 +114,7 @@ func Issues(ctx *middleware.Context) { | |||||
| // Get issues. | // Get issues. | ||||
| issues, err := models.Issues(uid, assigneeID, repo.ID, posterID, milestoneID, | issues, err := models.Issues(uid, assigneeID, repo.ID, posterID, milestoneID, | ||||
| page, isShowClosed, filterMode == models.FM_MENTION, selectLabels, sortType) | |||||
| nil, page, isShowClosed, filterMode == models.FM_MENTION, selectLabels, sortType) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "Issues: %v", err) | ctx.Handle(500, "Issues: %v", err) | ||||
| return | return | ||||
| @@ -190,20 +190,6 @@ func Issues(ctx *middleware.Context) { | |||||
| repoID := ctx.QueryInt64("repo") | repoID := ctx.QueryInt64("repo") | ||||
| isShowClosed := ctx.Query("state") == "closed" | isShowClosed := ctx.Query("state") == "closed" | ||||
| issueStats := models.GetUserIssueStats(repoID, ctxUser.Id, filterMode) | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 1 { | |||||
| page = 1 | |||||
| } | |||||
| var total int | |||||
| if !isShowClosed { | |||||
| total = int(issueStats.OpenCount) | |||||
| } else { | |||||
| total = int(issueStats.ClosedCount) | |||||
| } | |||||
| ctx.Data["Page"] = paginater.New(total, setting.IssuePagingNum, page, 5) | |||||
| // Get repositories. | // Get repositories. | ||||
| repos, err := models.GetRepositories(ctxUser.Id, true) | repos, err := models.GetRepositories(ctxUser.Id, true) | ||||
| @@ -212,6 +198,7 @@ func Issues(ctx *middleware.Context) { | |||||
| return | return | ||||
| } | } | ||||
| allCount := 0 | |||||
| repoIDs := make([]int64, 0, len(repos)) | repoIDs := make([]int64, 0, len(repos)) | ||||
| showRepos := make([]*models.Repository, 0, len(repos)) | showRepos := make([]*models.Repository, 0, len(repos)) | ||||
| for _, repo := range repos { | for _, repo := range repos { | ||||
| @@ -221,12 +208,9 @@ func Issues(ctx *middleware.Context) { | |||||
| repoIDs = append(repoIDs, repo.ID) | repoIDs = append(repoIDs, repo.ID) | ||||
| repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues | repo.NumOpenIssues = repo.NumIssues - repo.NumClosedIssues | ||||
| issueStats.AllCount += int64(repo.NumOpenIssues) | |||||
| allCount += repo.NumOpenIssues | |||||
| if repo.ID == repoID { | |||||
| repo.NumOpenIssues = int(issueStats.OpenCount) | |||||
| repo.NumClosedIssues = int(issueStats.ClosedCount) | |||||
| } else if filterMode != models.FM_ALL && repo.NumIssues > 0 { | |||||
| if filterMode != models.FM_ALL { | |||||
| // Calculate repository issue count with filter mode. | // Calculate repository issue count with filter mode. | ||||
| numOpen, numClosed := repo.IssueStats(ctxUser.Id, filterMode) | numOpen, numClosed := repo.IssueStats(ctxUser.Id, filterMode) | ||||
| repo.NumOpenIssues, repo.NumClosedIssues = int(numOpen), int(numClosed) | repo.NumOpenIssues, repo.NumClosedIssues = int(numOpen), int(numClosed) | ||||
| @@ -244,9 +228,25 @@ func Issues(ctx *middleware.Context) { | |||||
| repoIDs = []int64{repoID} | repoIDs = []int64{repoID} | ||||
| } | } | ||||
| issueStats := models.GetUserIssueStats(repoID, ctxUser.Id, repoIDs, filterMode) | |||||
| issueStats.AllCount = int64(allCount) | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 1 { | |||||
| page = 1 | |||||
| } | |||||
| var total int | |||||
| if !isShowClosed { | |||||
| total = int(issueStats.OpenCount) | |||||
| } else { | |||||
| total = int(issueStats.ClosedCount) | |||||
| } | |||||
| ctx.Data["Page"] = paginater.New(total, setting.IssuePagingNum, page, 5) | |||||
| // Get issues. | // Get issues. | ||||
| issues, err := models.Issues(ctxUser.Id, assigneeID, repoID, posterID, 0, | issues, err := models.Issues(ctxUser.Id, assigneeID, repoID, posterID, 0, | ||||
| page, isShowClosed, false, "", "") | |||||
| repoIDs, page, isShowClosed, false, "", "") | |||||
| if err != nil { | if err != nil { | ||||
| ctx.Handle(500, "Issues: %v", err) | ctx.Handle(500, "Issues: %v", err) | ||||
| return | return | ||||
| @@ -9,6 +9,7 @@ | |||||
| {{.i18n.Tr "home.issues.in_your_repos"}} | {{.i18n.Tr "home.issues.in_your_repos"}} | ||||
| <strong class="ui right">{{.IssueStats.AllCount}}</strong> | <strong class="ui right">{{.IssueStats.AllCount}}</strong> | ||||
| </a> | </a> | ||||
| {{if not .ContextUser.IsOrganization}} | |||||
| <a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{.Link}}?type=assigned&repo={{.RepoID}}&state={{.State}}"> | <a class="{{if eq .ViewType "assigned"}}active{{end}} item" href="{{.Link}}?type=assigned&repo={{.RepoID}}&state={{.State}}"> | ||||
| {{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}} | {{.i18n.Tr "repo.issues.filter_type.assigned_to_you"}} | ||||
| <strong class="ui right">{{.IssueStats.AssignCount}}</strong> | <strong class="ui right">{{.IssueStats.AssignCount}}</strong> | ||||
| @@ -17,6 +18,7 @@ | |||||
| {{.i18n.Tr "repo.issues.filter_type.created_by_you"}} | {{.i18n.Tr "repo.issues.filter_type.created_by_you"}} | ||||
| <strong class="ui right">{{.IssueStats.CreateCount}}</strong> | <strong class="ui right">{{.IssueStats.CreateCount}}</strong> | ||||
| </a> | </a> | ||||
| {{end}} | |||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| {{range .Repos}} | {{range .Repos}} | ||||
| <a class="{{if eq $.RepoID .ID}}active{{end}} item" href="{{$.Link}}?type={{$.ViewType}}{{if not (eq $.RepoID .ID)}}&repo={{.ID}}{{end}}&state={{$.State}}">{{$.SignedUser.Name}}/{{.Name}} <strong class="ui right">{{if $.IsShowClosed}}{{.NumClosedIssues}}{{else}}{{.NumOpenIssues}}{{end}}</strong></a> | <a class="{{if eq $.RepoID .ID}}active{{end}} item" href="{{$.Link}}?type={{$.ViewType}}{{if not (eq $.RepoID .ID)}}&repo={{.ID}}{{end}}&state={{$.State}}">{{$.SignedUser.Name}}/{{.Name}} <strong class="ui right">{{if $.IsShowClosed}}{{.NumClosedIssues}}{{else}}{{.NumOpenIssues}}{{end}}</strong></a> | ||||