2014-04-13 09:57:42 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-02-08 19:45:43 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2014-04-13 09:57:42 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package user
import (
2014-11-23 10:33:47 +03:00
"bytes"
2019-12-02 06:50:36 +03:00
"encoding/json"
2014-04-13 09:57:42 +04:00
"fmt"
2019-12-02 06:50:36 +03:00
"regexp"
2017-12-04 07:39:01 +03:00
"sort"
2019-12-02 06:50:36 +03:00
"strconv"
2019-01-23 07:10:38 +03:00
"strings"
2014-04-13 09:57:42 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
2020-02-29 09:52:05 +03:00
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
2019-10-08 20:55:16 +03:00
"code.gitea.io/gitea/modules/log"
2019-12-15 17:20:08 +03:00
"code.gitea.io/gitea/modules/markup/markdown"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2017-01-25 05:43:02 +03:00
"code.gitea.io/gitea/modules/util"
2017-12-26 02:25:16 +03:00
2019-04-14 19:43:56 +03:00
"github.com/keybase/go-crypto/openpgp"
"github.com/keybase/go-crypto/openpgp/armor"
2019-12-15 17:20:08 +03:00
"github.com/unknwon/com"
2014-04-13 09:57:42 +04:00
)
2014-06-23 07:11:12 +04:00
const (
2019-12-15 17:20:08 +03:00
tplDashboard base . TplName = "user/dashboard/dashboard"
tplIssues base . TplName = "user/dashboard/issues"
tplMilestones base . TplName = "user/dashboard/milestones"
tplProfile base . TplName = "user/profile"
2014-06-23 07:11:12 +04:00
)
2016-07-23 20:08:22 +03:00
// getDashboardContextUser finds out dashboard is viewing as which context user.
2016-03-11 19:56:52 +03:00
func getDashboardContextUser ( ctx * context . Context ) * models . User {
2015-08-25 17:58:34 +03:00
ctxUser := ctx . User
2014-07-27 07:53:16 +04:00
orgName := ctx . Params ( ":org" )
if len ( orgName ) > 0 {
// Organization.
org , err := models . GetUserByName ( orgName )
if err != nil {
2015-08-05 06:14:17 +03:00
if models . IsErrUserNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetUserByName" , err )
2014-07-27 07:53:16 +04:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserByName" , err )
2014-07-27 07:53:16 +04:00
}
2015-08-25 17:58:34 +03:00
return nil
2014-07-27 07:53:16 +04:00
}
ctxUser = org
2015-08-25 17:58:34 +03:00
}
ctx . Data [ "ContextUser" ] = ctxUser
2020-01-24 22:00:29 +03:00
if err := ctx . User . GetOrganizations ( & models . SearchOrganizationsOptions { All : true } ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetOrganizations" , err )
2015-08-25 17:58:34 +03:00
return nil
}
ctx . Data [ "Orgs" ] = ctx . User . Orgs
return ctxUser
}
2017-06-02 03:42:25 +03:00
// retrieveFeeds loads feeds for the specified user
2017-08-23 04:30:54 +03:00
func retrieveFeeds ( ctx * context . Context , options models . GetFeedsOptions ) {
actions , err := models . GetFeeds ( options )
2015-11-22 09:32:09 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetFeeds" , err )
2015-11-22 09:32:09 +03:00
return
}
2017-08-23 04:30:54 +03:00
userCache := map [ int64 ] * models . User { options . RequestedUser . ID : options . RequestedUser }
2017-06-08 05:11:41 +03:00
if ctx . User != nil {
userCache [ ctx . User . ID ] = ctx . User
}
2015-11-22 09:32:09 +03:00
for _ , act := range actions {
2018-02-21 13:55:34 +03:00
if act . ActUser != nil {
userCache [ act . ActUserID ] = act . ActUser
2017-05-26 04:38:18 +03:00
}
2019-12-15 23:44:23 +03:00
}
2017-05-26 04:38:18 +03:00
2019-12-15 23:44:23 +03:00
for _ , act := range actions {
2018-02-21 13:55:34 +03:00
repoOwner , ok := userCache [ act . Repo . OwnerID ]
2017-05-26 04:38:18 +03:00
if ! ok {
2018-02-21 13:55:34 +03:00
repoOwner , err = models . GetUserByID ( act . Repo . OwnerID )
2017-05-26 04:38:18 +03:00
if err != nil {
if models . IsErrUserNotExist ( err ) {
continue
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserByID" , err )
2015-11-22 09:32:09 +03:00
return
}
2018-02-21 13:55:34 +03:00
userCache [ repoOwner . ID ] = repoOwner
2015-11-22 09:32:09 +03:00
}
2018-02-21 13:55:34 +03:00
act . Repo . Owner = repoOwner
2015-11-22 09:32:09 +03:00
}
2017-06-02 03:42:25 +03:00
ctx . Data [ "Feeds" ] = actions
2015-11-22 09:32:09 +03:00
}
2016-11-18 06:03:03 +03:00
// Dashboard render the dashborad page
2016-03-11 19:56:52 +03:00
func Dashboard ( ctx * context . Context ) {
2016-03-10 07:56:03 +03:00
ctxUser := getDashboardContextUser ( ctx )
2015-08-25 17:58:34 +03:00
if ctx . Written ( ) {
return
}
2016-07-23 20:08:22 +03:00
ctx . Data [ "Title" ] = ctxUser . DisplayName ( ) + " - " + ctx . Tr ( "dashboard" )
ctx . Data [ "PageIsDashboard" ] = true
ctx . Data [ "PageIsNews" ] = true
2017-08-17 04:31:34 +03:00
ctx . Data [ "SearchLimit" ] = setting . UI . User . RepoPagingNum
2018-10-23 05:57:42 +03:00
ctx . Data [ "EnableHeatmap" ] = setting . Service . EnableUserHeatmap
ctx . Data [ "HeatmapUser" ] = ctxUser . Name
2016-07-23 20:08:22 +03:00
2016-07-24 09:32:46 +03:00
var err error
2017-08-23 04:30:54 +03:00
var mirrors [ ] * models . Repository
2016-02-02 22:29:35 +03:00
if ctxUser . IsOrganization ( ) {
2017-01-25 18:41:38 +03:00
env , err := ctxUser . AccessibleReposEnv ( ctx . User . ID )
2016-07-24 09:32:46 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "AccessibleReposEnv" , err )
2017-01-25 18:41:38 +03:00
return
}
2016-07-24 09:32:46 +03:00
2017-01-25 18:41:38 +03:00
mirrors , err = env . MirrorRepos ( )
2016-02-02 22:29:35 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "env.MirrorRepos" , err )
2016-07-24 09:32:46 +03:00
return
}
} else {
mirrors , err = ctxUser . GetMirrorRepositories ( )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetMirrorRepositories" , err )
2016-07-24 09:32:46 +03:00
return
}
2014-04-13 09:57:42 +04:00
}
2016-07-24 09:32:46 +03:00
ctx . Data [ "MaxShowRepoNum" ] = setting . UI . User . RepoPagingNum
2014-04-13 09:57:42 +04:00
2016-07-24 09:32:46 +03:00
if err := models . MirrorRepositoryList ( mirrors ) . LoadAttributes ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "MirrorRepositoryList.LoadAttributes" , err )
2016-07-24 09:32:46 +03:00
return
2014-07-26 10:28:04 +04:00
}
2014-07-27 07:53:16 +04:00
ctx . Data [ "MirrorCount" ] = len ( mirrors )
ctx . Data [ "Mirrors" ] = mirrors
2014-05-12 23:14:22 +04:00
2018-02-21 13:55:34 +03:00
retrieveFeeds ( ctx , models . GetFeedsOptions {
RequestedUser : ctxUser ,
2020-01-13 20:33:46 +03:00
Actor : ctx . User ,
2017-08-23 04:30:54 +03:00
IncludePrivate : true ,
OnlyPerformedBy : false ,
IncludeDeleted : false ,
} )
2018-12-13 18:55:43 +03:00
2015-11-22 09:32:09 +03:00
if ctx . Written ( ) {
2014-04-13 09:57:42 +04:00
return
}
2017-08-18 14:21:46 +03:00
ctx . HTML ( 200 , tplDashboard )
2014-04-13 09:57:42 +04:00
}
2019-12-15 17:20:08 +03:00
// Milestones render the user milestones page
func Milestones ( ctx * context . Context ) {
2020-01-17 10:34:37 +03:00
if models . UnitTypeIssues . UnitGlobalDisabled ( ) && models . UnitTypePullRequests . UnitGlobalDisabled ( ) {
log . Debug ( "Milestones overview page not available as both issues and pull requests are globally disabled" )
ctx . Status ( 404 )
return
}
2019-12-15 17:20:08 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "milestones" )
ctx . Data [ "PageIsMilestonesDashboard" ] = true
ctxUser := getDashboardContextUser ( ctx )
if ctx . Written ( ) {
return
}
sortType := ctx . Query ( "sort" )
page := ctx . QueryInt ( "page" )
if page <= 1 {
page = 1
}
reposQuery := ctx . Query ( "repos" )
isShowClosed := ctx . Query ( "state" ) == "closed"
// Get repositories.
var err error
var userRepoIDs [ ] int64
if ctxUser . IsOrganization ( ) {
env , err := ctxUser . AccessibleReposEnv ( ctx . User . ID )
if err != nil {
ctx . ServerError ( "AccessibleReposEnv" , err )
return
}
userRepoIDs , err = env . RepoIDs ( 1 , ctxUser . NumRepos )
if err != nil {
ctx . ServerError ( "env.RepoIDs" , err )
return
}
2020-01-05 04:23:29 +03:00
userRepoIDs , err = models . FilterOutRepoIdsWithoutUnitAccess ( ctx . User , userRepoIDs , models . UnitTypeIssues , models . UnitTypePullRequests )
if err != nil {
ctx . ServerError ( "FilterOutRepoIdsWithoutUnitAccess" , err )
return
}
2019-12-15 17:20:08 +03:00
} else {
2020-01-05 04:23:29 +03:00
userRepoIDs , err = ctxUser . GetAccessRepoIDs ( models . UnitTypeIssues , models . UnitTypePullRequests )
2019-12-15 17:20:08 +03:00
if err != nil {
ctx . ServerError ( "ctxUser.GetAccessRepoIDs" , err )
return
}
}
if len ( userRepoIDs ) == 0 {
userRepoIDs = [ ] int64 { - 1 }
}
var repoIDs [ ] int64
2020-01-05 04:23:29 +03:00
if len ( reposQuery ) != 0 {
if issueReposQueryPattern . MatchString ( reposQuery ) {
// remove "[" and "]" from string
reposQuery = reposQuery [ 1 : len ( reposQuery ) - 1 ]
//for each ID (delimiter ",") add to int to repoIDs
reposSet := false
for _ , rID := range strings . Split ( reposQuery , "," ) {
// Ensure nonempty string entries
if rID != "" && rID != "0" {
reposSet = true
rIDint64 , err := strconv . ParseInt ( rID , 10 , 64 )
// If the repo id specified by query is not parseable or not accessible by user, just ignore it.
if err == nil && com . IsSliceContainsInt64 ( userRepoIDs , rIDint64 ) {
repoIDs = append ( repoIDs , rIDint64 )
}
2019-12-15 17:20:08 +03:00
}
}
2020-01-05 04:23:29 +03:00
if reposSet && len ( repoIDs ) == 0 {
// force an empty result
repoIDs = [ ] int64 { - 1 }
}
} else {
log . Warn ( "issueReposQueryPattern not match with query" )
2019-12-15 17:20:08 +03:00
}
}
if len ( repoIDs ) == 0 {
repoIDs = userRepoIDs
}
counts , err := models . CountMilestonesByRepoIDs ( userRepoIDs , isShowClosed )
if err != nil {
ctx . ServerError ( "CountMilestonesByRepoIDs" , err )
return
}
milestones , err := models . GetMilestonesByRepoIDs ( repoIDs , page , isShowClosed , sortType )
if err != nil {
ctx . ServerError ( "GetMilestonesByRepoIDs" , err )
return
}
showReposMap := make ( map [ int64 ] * models . Repository , len ( counts ) )
for rID := range counts {
if rID == - 1 {
break
}
repo , err := models . GetRepositoryByID ( rID )
if err != nil {
if models . IsErrRepoNotExist ( err ) {
ctx . NotFound ( "GetRepositoryByID" , err )
return
} else if err != nil {
ctx . ServerError ( "GetRepositoryByID" , fmt . Errorf ( "[%d]%v" , rID , err ) )
return
}
}
showReposMap [ rID ] = repo
}
showRepos := models . RepositoryListOfMap ( showReposMap )
sort . Sort ( showRepos )
if err = showRepos . LoadAttributes ( ) ; err != nil {
ctx . ServerError ( "LoadAttributes" , err )
return
}
for _ , m := range milestones {
m . Repo = showReposMap [ m . RepoID ]
m . RenderedContent = string ( markdown . Render ( [ ] byte ( m . Content ) , m . Repo . Link ( ) , m . Repo . ComposeMetas ( ) ) )
if m . Repo . IsTimetrackerEnabled ( ) {
err := m . LoadTotalTrackedTime ( )
if err != nil {
ctx . ServerError ( "LoadTotalTrackedTime" , err )
return
}
}
}
milestoneStats , err := models . GetMilestonesStats ( repoIDs )
if err != nil {
ctx . ServerError ( "GetMilestoneStats" , err )
return
}
totalMilestoneStats , err := models . GetMilestonesStats ( userRepoIDs )
if err != nil {
ctx . ServerError ( "GetMilestoneStats" , err )
return
}
var pagerCount int
if isShowClosed {
ctx . Data [ "State" ] = "closed"
ctx . Data [ "Total" ] = totalMilestoneStats . ClosedCount
pagerCount = int ( milestoneStats . ClosedCount )
} else {
ctx . Data [ "State" ] = "open"
ctx . Data [ "Total" ] = totalMilestoneStats . OpenCount
pagerCount = int ( milestoneStats . OpenCount )
}
ctx . Data [ "Milestones" ] = milestones
ctx . Data [ "Repos" ] = showRepos
ctx . Data [ "Counts" ] = counts
ctx . Data [ "MilestoneStats" ] = milestoneStats
ctx . Data [ "SortType" ] = sortType
if len ( repoIDs ) != len ( userRepoIDs ) {
ctx . Data [ "RepoIDs" ] = repoIDs
}
ctx . Data [ "IsShowClosed" ] = isShowClosed
pager := context . NewPagination ( pagerCount , setting . UI . IssuePagingNum , page , 5 )
pager . AddParam ( ctx , "repos" , "RepoIDs" )
pager . AddParam ( ctx , "sort" , "SortType" )
pager . AddParam ( ctx , "state" , "State" )
ctx . Data [ "Page" ] = pager
ctx . HTML ( 200 , tplMilestones )
}
2019-12-02 06:50:36 +03:00
// Regexp for repos query
var issueReposQueryPattern = regexp . MustCompile ( ` ^\[\d+(,\d+)*,?\]$ ` )
2016-11-18 06:03:03 +03:00
// Issues render the user issues page
2016-03-11 19:56:52 +03:00
func Issues ( ctx * context . Context ) {
2015-09-02 23:18:09 +03:00
isPullList := ctx . Params ( ":type" ) == "pulls"
2020-01-05 04:23:29 +03:00
unitType := models . UnitTypeIssues
2015-09-02 23:18:09 +03:00
if isPullList {
2020-01-17 10:34:37 +03:00
if models . UnitTypePullRequests . UnitGlobalDisabled ( ) {
log . Debug ( "Pull request overview page not available as it is globally disabled." )
ctx . Status ( 404 )
return
}
2015-09-02 23:18:09 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "pull_requests" )
ctx . Data [ "PageIsPulls" ] = true
2020-01-05 04:23:29 +03:00
unitType = models . UnitTypePullRequests
2015-09-02 23:18:09 +03:00
} else {
2020-01-17 10:34:37 +03:00
if models . UnitTypeIssues . UnitGlobalDisabled ( ) {
log . Debug ( "Issues overview page not available as it is globally disabled." )
ctx . Status ( 404 )
return
}
2015-09-02 23:18:09 +03:00
ctx . Data [ "Title" ] = ctx . Tr ( "issues" )
ctx . Data [ "PageIsIssues" ] = true
}
2015-08-25 17:58:34 +03:00
ctxUser := getDashboardContextUser ( ctx )
if ctx . Written ( ) {
return
}
// Organization does not have view type and filter mode.
var (
viewType string
2015-11-04 20:50:02 +03:00
sortType = ctx . Query ( "sort" )
2016-11-07 19:24:59 +03:00
filterMode = models . FilterModeAll
2015-08-25 17:58:34 +03:00
)
2017-02-14 17:15:18 +03:00
2015-08-25 17:58:34 +03:00
if ctxUser . IsOrganization ( ) {
2019-12-03 09:01:29 +03:00
viewType = "your_repositories"
2015-08-25 17:58:34 +03:00
} else {
viewType = ctx . Query ( "type" )
switch viewType {
case "assigned" :
2016-11-07 19:24:59 +03:00
filterMode = models . FilterModeAssign
2015-08-25 17:58:34 +03:00
case "created_by" :
2016-11-07 19:24:59 +03:00
filterMode = models . FilterModeCreate
2019-09-18 10:24:44 +03:00
case "mentioned" :
filterMode = models . FilterModeMention
2019-12-03 09:01:29 +03:00
case "your_repositories" : // filterMode already set to All
2017-12-08 04:02:34 +03:00
default :
2019-12-03 09:01:29 +03:00
viewType = "your_repositories"
2015-08-25 17:58:34 +03:00
}
}
2017-02-14 17:15:18 +03:00
page := ctx . QueryInt ( "page" )
if page <= 1 {
page = 1
}
2019-12-02 06:50:36 +03:00
reposQuery := ctx . Query ( "repos" )
var repoIDs [ ] int64
2020-01-04 00:39:12 +03:00
if len ( reposQuery ) != 0 {
if issueReposQueryPattern . MatchString ( reposQuery ) {
// remove "[" and "]" from string
reposQuery = reposQuery [ 1 : len ( reposQuery ) - 1 ]
//for each ID (delimiter ",") add to int to repoIDs
for _ , rID := range strings . Split ( reposQuery , "," ) {
// Ensure nonempty string entries
if rID != "" && rID != "0" {
rIDint64 , err := strconv . ParseInt ( rID , 10 , 64 )
if err == nil {
repoIDs = append ( repoIDs , rIDint64 )
}
2019-12-02 06:50:36 +03:00
}
}
2020-01-04 00:39:12 +03:00
} else {
2020-01-05 04:23:29 +03:00
log . Warn ( "issueReposQueryPattern not match with query" )
2019-12-02 06:50:36 +03:00
}
}
2019-10-08 20:55:16 +03:00
isShowClosed := ctx . Query ( "state" ) == "closed"
2015-08-25 17:58:34 +03:00
// Get repositories.
2019-10-08 20:55:16 +03:00
var err error
var userRepoIDs [ ] int64
if ctxUser . IsOrganization ( ) {
env , err := ctxUser . AccessibleReposEnv ( ctx . User . ID )
if err != nil {
ctx . ServerError ( "AccessibleReposEnv" , err )
return
}
userRepoIDs , err = env . RepoIDs ( 1 , ctxUser . NumRepos )
if err != nil {
ctx . ServerError ( "env.RepoIDs" , err )
return
}
2020-01-05 04:23:29 +03:00
userRepoIDs , err = models . FilterOutRepoIdsWithoutUnitAccess ( ctx . User , userRepoIDs , unitType )
if err != nil {
ctx . ServerError ( "FilterOutRepoIdsWithoutUnitAccess" , err )
return
2018-06-21 19:00:13 +03:00
}
2020-01-05 04:23:29 +03:00
} else {
2019-10-08 20:55:16 +03:00
userRepoIDs , err = ctxUser . GetAccessRepoIDs ( unitType )
if err != nil {
ctx . ServerError ( "ctxUser.GetAccessRepoIDs" , err )
return
2016-01-31 23:12:03 +03:00
}
2017-02-17 03:58:19 +03:00
}
2019-10-08 20:55:16 +03:00
if len ( userRepoIDs ) == 0 {
userRepoIDs = [ ] int64 { - 1 }
}
opts := & models . IssuesOptions {
IsPull : util . OptionalBoolOf ( isPullList ) ,
SortType : sortType ,
}
2017-02-14 17:15:18 +03:00
switch filterMode {
2019-10-08 20:55:16 +03:00
case models . FilterModeAll :
2019-12-02 06:50:36 +03:00
opts . RepoIDs = userRepoIDs
2017-02-14 17:15:18 +03:00
case models . FilterModeAssign :
2017-08-03 08:09:16 +03:00
opts . AssigneeID = ctxUser . ID
2017-02-14 17:15:18 +03:00
case models . FilterModeCreate :
2017-08-03 08:09:16 +03:00
opts . PosterID = ctxUser . ID
2017-02-14 17:15:18 +03:00
case models . FilterModeMention :
2017-08-03 08:09:16 +03:00
opts . MentionedID = ctxUser . ID
2017-02-14 17:15:18 +03:00
}
2020-02-29 09:52:05 +03:00
var forceEmpty bool
var issueIDsFromSearch [ ] int64
var keyword = strings . Trim ( ctx . Query ( "q" ) , " " )
if len ( keyword ) > 0 {
searchRepoIDs , err := models . GetRepoIDsForIssuesOptions ( opts , ctxUser )
if err != nil {
ctx . ServerError ( "GetRepoIDsForIssuesOptions" , err )
return
}
issueIDsFromSearch , err = issue_indexer . SearchIssuesByKeyword ( searchRepoIDs , keyword )
if err != nil {
ctx . ServerError ( "SearchIssuesByKeyword" , err )
return
}
if len ( issueIDsFromSearch ) > 0 {
opts . IssueIDs = issueIDsFromSearch
} else {
forceEmpty = true
}
}
ctx . Data [ "Keyword" ] = keyword
opts . IsClosed = util . OptionalBoolOf ( isShowClosed )
var counts map [ int64 ] int64
if ! forceEmpty {
counts , err = models . CountIssuesByRepo ( opts )
if err != nil {
ctx . ServerError ( "CountIssuesByRepo" , err )
return
}
2019-10-08 20:55:16 +03:00
}
opts . Page = page
opts . PageSize = setting . UI . IssuePagingNum
2019-01-23 07:10:38 +03:00
var labelIDs [ ] int64
selectLabels := ctx . Query ( "labels" )
if len ( selectLabels ) > 0 && selectLabels != "0" {
labelIDs , err = base . StringsToInt64s ( strings . Split ( selectLabels , "," ) )
if err != nil {
ctx . ServerError ( "StringsToInt64s" , err )
return
}
}
opts . LabelIDs = labelIDs
2018-10-28 09:55:01 +03:00
2019-12-03 10:26:02 +03:00
if len ( repoIDs ) > 0 {
opts . RepoIDs = repoIDs
}
2019-12-02 06:50:36 +03:00
2020-02-29 09:52:05 +03:00
var issues [ ] * models . Issue
if ! forceEmpty {
issues , err = models . Issues ( opts )
if err != nil {
ctx . ServerError ( "Issues" , err )
return
}
} else {
issues = [ ] * models . Issue { }
2017-02-17 03:58:19 +03:00
}
2015-09-02 23:18:09 +03:00
2017-08-03 08:09:16 +03:00
showReposMap := make ( map [ int64 ] * models . Repository , len ( counts ) )
for repoID := range counts {
2019-12-03 10:26:02 +03:00
if repoID > 0 {
if _ , ok := showReposMap [ repoID ] ; ! ok {
repo , err := models . GetRepositoryByID ( repoID )
if models . IsErrRepoNotExist ( err ) {
ctx . NotFound ( "GetRepositoryByID" , err )
return
} else if err != nil {
ctx . ServerError ( "GetRepositoryByID" , fmt . Errorf ( "[%d]%v" , repoID , err ) )
return
}
showReposMap [ repoID ] = repo
}
repo := showReposMap [ repoID ]
2019-10-08 20:55:16 +03:00
2019-12-03 10:26:02 +03:00
// Check if user has access to given repository.
perm , err := models . GetUserRepoPermission ( repo , ctxUser )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , fmt . Errorf ( "[%d]%v" , repoID , err ) )
return
}
if ! perm . CanRead ( models . UnitTypeIssues ) {
log . Error ( "User created Issues in Repository which they no longer have access to: [%d]" , repoID )
}
2019-10-08 20:55:16 +03:00
}
}
2017-08-03 08:09:16 +03:00
showRepos := models . RepositoryListOfMap ( showReposMap )
2017-12-04 07:39:01 +03:00
sort . Sort ( showRepos )
2017-08-03 08:09:16 +03:00
if err = showRepos . LoadAttributes ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "LoadAttributes" , err )
2017-02-17 03:58:19 +03:00
return
2015-08-25 18:22:05 +03:00
}
2019-04-02 22:54:29 +03:00
var commitStatus = make ( map [ int64 ] * models . CommitStatus , len ( issues ) )
2017-08-03 08:09:16 +03:00
for _ , issue := range issues {
issue . Repo = showReposMap [ issue . RepoID ]
2019-04-02 22:54:29 +03:00
if isPullList {
commitStatus [ issue . PullRequest . ID ] , _ = issue . PullRequest . GetLastCommitStatus ( )
}
2017-08-03 08:09:16 +03:00
}
2020-02-29 09:52:05 +03:00
userIssueStatsOpts := models . UserIssueStatsOptions {
2019-10-08 20:55:16 +03:00
UserID : ctxUser . ID ,
UserRepoIDs : userRepoIDs ,
FilterMode : filterMode ,
IsPull : isPullList ,
IsClosed : isShowClosed ,
2020-02-03 05:21:50 +03:00
}
if len ( repoIDs ) > 0 {
2020-02-29 09:52:05 +03:00
userIssueStatsOpts . UserRepoIDs = repoIDs
2020-02-03 05:21:50 +03:00
}
2020-02-29 09:52:05 +03:00
userIssueStats , err := models . GetUserIssueStats ( userIssueStatsOpts )
2017-12-26 02:25:16 +03:00
if err != nil {
2020-02-29 09:52:05 +03:00
ctx . ServerError ( "GetUserIssueStats User" , err )
2017-12-26 02:25:16 +03:00
return
}
2017-02-14 17:15:18 +03:00
2020-02-29 09:52:05 +03:00
var shownIssueStats * models . IssueStats
if ! forceEmpty {
statsOpts := models . UserIssueStatsOptions {
UserID : ctxUser . ID ,
UserRepoIDs : userRepoIDs ,
FilterMode : filterMode ,
IsPull : isPullList ,
IsClosed : isShowClosed ,
IssueIDs : issueIDsFromSearch ,
}
if len ( repoIDs ) > 0 {
statsOpts . RepoIDs = repoIDs
}
shownIssueStats , err = models . GetUserIssueStats ( statsOpts )
if err != nil {
ctx . ServerError ( "GetUserIssueStats Shown" , err )
return
}
} else {
shownIssueStats = & models . IssueStats { }
}
var allIssueStats * models . IssueStats
if ! forceEmpty {
allIssueStats , err = models . GetUserIssueStats ( models . UserIssueStatsOptions {
UserID : ctxUser . ID ,
UserRepoIDs : userRepoIDs ,
FilterMode : filterMode ,
IsPull : isPullList ,
IsClosed : isShowClosed ,
IssueIDs : issueIDsFromSearch ,
} )
if err != nil {
ctx . ServerError ( "GetUserIssueStats All" , err )
return
}
} else {
allIssueStats = & models . IssueStats { }
2019-12-02 06:50:36 +03:00
}
var shownIssues int
var totalIssues int
2015-08-25 18:22:05 +03:00
if ! isShowClosed {
2020-02-29 09:52:05 +03:00
shownIssues = int ( shownIssueStats . OpenCount )
2019-12-02 06:50:36 +03:00
totalIssues = int ( allIssueStats . OpenCount )
2015-08-25 18:22:05 +03:00
} else {
2020-02-29 09:52:05 +03:00
shownIssues = int ( shownIssueStats . ClosedCount )
2019-12-02 06:50:36 +03:00
totalIssues = int ( allIssueStats . ClosedCount )
2015-08-25 18:22:05 +03:00
}
2015-08-25 17:58:34 +03:00
ctx . Data [ "Issues" ] = issues
2019-04-02 22:54:29 +03:00
ctx . Data [ "CommitStatus" ] = commitStatus
2017-02-14 17:15:18 +03:00
ctx . Data [ "Repos" ] = showRepos
2017-08-03 08:09:16 +03:00
ctx . Data [ "Counts" ] = counts
2020-02-29 09:52:05 +03:00
ctx . Data [ "IssueStats" ] = userIssueStats
ctx . Data [ "ShownIssueStats" ] = shownIssueStats
2015-08-25 17:58:34 +03:00
ctx . Data [ "ViewType" ] = viewType
2015-11-04 20:50:02 +03:00
ctx . Data [ "SortType" ] = sortType
2019-12-02 06:50:36 +03:00
ctx . Data [ "RepoIDs" ] = repoIDs
2015-08-25 17:58:34 +03:00
ctx . Data [ "IsShowClosed" ] = isShowClosed
2019-12-02 06:50:36 +03:00
ctx . Data [ "TotalIssueCount" ] = totalIssues
2017-02-14 17:15:18 +03:00
2015-08-25 17:58:34 +03:00
if isShowClosed {
ctx . Data [ "State" ] = "closed"
} else {
ctx . Data [ "State" ] = "open"
}
2019-12-02 06:50:36 +03:00
// Convert []int64 to string
reposParam , _ := json . Marshal ( repoIDs )
ctx . Data [ "ReposParam" ] = string ( reposParam )
pager := context . NewPagination ( shownIssues , setting . UI . IssuePagingNum , page , 5 )
2020-02-29 09:52:05 +03:00
pager . AddParam ( ctx , "q" , "Keyword" )
2019-04-20 07:15:19 +03:00
pager . AddParam ( ctx , "type" , "ViewType" )
2019-12-02 06:50:36 +03:00
pager . AddParam ( ctx , "repos" , "ReposParam" )
2019-04-20 07:15:19 +03:00
pager . AddParam ( ctx , "sort" , "SortType" )
pager . AddParam ( ctx , "state" , "State" )
pager . AddParam ( ctx , "labels" , "SelectLabels" )
pager . AddParam ( ctx , "milestone" , "MilestoneID" )
pager . AddParam ( ctx , "assignee" , "AssigneeID" )
ctx . Data [ "Page" ] = pager
2016-11-18 06:03:03 +03:00
ctx . HTML ( 200 , tplIssues )
2015-08-25 17:58:34 +03:00
}
2016-11-27 14:59:12 +03:00
// ShowSSHKeys output all the ssh keys of user by uid
2016-03-11 19:56:52 +03:00
func ShowSSHKeys ( ctx * context . Context , uid int64 ) {
2020-01-24 22:00:29 +03:00
keys , err := models . ListPublicKeys ( uid , models . ListOptions { } )
2014-11-23 10:33:47 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "ListPublicKeys" , err )
2014-11-23 10:33:47 +03:00
return
}
var buf bytes . Buffer
for i := range keys {
buf . WriteString ( keys [ i ] . OmitEmail ( ) )
2015-06-08 10:40:38 +03:00
buf . WriteString ( "\n" )
2014-11-23 10:33:47 +03:00
}
2015-10-16 04:28:12 +03:00
ctx . PlainText ( 200 , buf . Bytes ( ) )
2014-11-23 10:33:47 +03:00
}
2019-04-14 19:43:56 +03:00
// ShowGPGKeys output all the public GPG keys of user by uid
func ShowGPGKeys ( ctx * context . Context , uid int64 ) {
2020-01-24 22:00:29 +03:00
keys , err := models . ListGPGKeys ( uid , models . ListOptions { } )
2019-04-14 19:43:56 +03:00
if err != nil {
ctx . ServerError ( "ListGPGKeys" , err )
return
}
entities := make ( [ ] * openpgp . Entity , 0 )
failedEntitiesID := make ( [ ] string , 0 )
for _ , k := range keys {
e , err := models . GPGKeyToEntity ( k )
if err != nil {
if models . IsErrGPGKeyImportNotExist ( err ) {
failedEntitiesID = append ( failedEntitiesID , k . KeyID )
continue //Skip previous import without backup of imported armored key
}
ctx . ServerError ( "ShowGPGKeys" , err )
return
}
entities = append ( entities , e )
}
var buf bytes . Buffer
headers := make ( map [ string ] string )
if len ( failedEntitiesID ) > 0 { //If some key need re-import to be exported
headers [ "Note" ] = fmt . Sprintf ( "The keys with the following IDs couldn't be exported and need to be reuploaded %s" , strings . Join ( failedEntitiesID , ", " ) )
}
writer , _ := armor . Encode ( & buf , "PGP PUBLIC KEY BLOCK" , headers )
for _ , e := range entities {
err = e . Serialize ( writer ) //TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
if err != nil {
ctx . ServerError ( "ShowGPGKeys" , err )
return
}
}
writer . Close ( )
ctx . PlainText ( 200 , buf . Bytes ( ) )
}
2016-11-18 06:03:03 +03:00
// Email2User show user page via email
2016-03-11 19:56:52 +03:00
func Email2User ( ctx * context . Context ) {
2014-04-13 09:57:42 +04:00
u , err := models . GetUserByEmail ( ctx . Query ( "email" ) )
if err != nil {
2015-08-05 06:14:17 +03:00
if models . IsErrUserNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetUserByEmail" , err )
2014-04-13 09:57:42 +04:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserByEmail" , err )
2014-04-13 09:57:42 +04:00
}
return
}
2016-11-27 13:14:25 +03:00
ctx . Redirect ( setting . AppSubURL + "/user/" + u . Name )
2014-04-13 09:57:42 +04:00
}