You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

home.go 10 kB

11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package user
  5. import (
  6. "bytes"
  7. "fmt"
  8. "sort"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/base"
  11. "code.gitea.io/gitea/modules/context"
  12. "code.gitea.io/gitea/modules/setting"
  13. "code.gitea.io/gitea/modules/util"
  14. "github.com/Unknwon/com"
  15. "github.com/Unknwon/paginater"
  16. )
  17. const (
  18. tplDashboard base.TplName = "user/dashboard/dashboard"
  19. tplIssues base.TplName = "user/dashboard/issues"
  20. tplProfile base.TplName = "user/profile"
  21. tplOrgHome base.TplName = "org/home"
  22. )
  23. // getDashboardContextUser finds out dashboard is viewing as which context user.
  24. func getDashboardContextUser(ctx *context.Context) *models.User {
  25. ctxUser := ctx.User
  26. orgName := ctx.Params(":org")
  27. if len(orgName) > 0 {
  28. // Organization.
  29. org, err := models.GetUserByName(orgName)
  30. if err != nil {
  31. if models.IsErrUserNotExist(err) {
  32. ctx.NotFound("GetUserByName", err)
  33. } else {
  34. ctx.ServerError("GetUserByName", err)
  35. }
  36. return nil
  37. }
  38. ctxUser = org
  39. }
  40. ctx.Data["ContextUser"] = ctxUser
  41. if err := ctx.User.GetOrganizations(true); err != nil {
  42. ctx.ServerError("GetOrganizations", err)
  43. return nil
  44. }
  45. ctx.Data["Orgs"] = ctx.User.Orgs
  46. return ctxUser
  47. }
  48. // retrieveFeeds loads feeds for the specified user
  49. func retrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) {
  50. actions, err := models.GetFeeds(options)
  51. if err != nil {
  52. ctx.ServerError("GetFeeds", err)
  53. return
  54. }
  55. userCache := map[int64]*models.User{options.RequestedUser.ID: options.RequestedUser}
  56. if ctx.User != nil {
  57. userCache[ctx.User.ID] = ctx.User
  58. }
  59. for _, act := range actions {
  60. if act.ActUser != nil {
  61. userCache[act.ActUserID] = act.ActUser
  62. }
  63. repoOwner, ok := userCache[act.Repo.OwnerID]
  64. if !ok {
  65. repoOwner, err = models.GetUserByID(act.Repo.OwnerID)
  66. if err != nil {
  67. if models.IsErrUserNotExist(err) {
  68. continue
  69. }
  70. ctx.ServerError("GetUserByID", err)
  71. return
  72. }
  73. userCache[repoOwner.ID] = repoOwner
  74. }
  75. act.Repo.Owner = repoOwner
  76. }
  77. ctx.Data["Feeds"] = actions
  78. }
  79. // Dashboard render the dashborad page
  80. func Dashboard(ctx *context.Context) {
  81. ctxUser := getDashboardContextUser(ctx)
  82. if ctx.Written() {
  83. return
  84. }
  85. ctx.Data["Title"] = ctxUser.DisplayName() + " - " + ctx.Tr("dashboard")
  86. ctx.Data["PageIsDashboard"] = true
  87. ctx.Data["PageIsNews"] = true
  88. ctx.Data["SearchLimit"] = setting.UI.User.RepoPagingNum
  89. ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap
  90. ctx.Data["HeatmapUser"] = ctxUser.Name
  91. var err error
  92. var mirrors []*models.Repository
  93. if ctxUser.IsOrganization() {
  94. env, err := ctxUser.AccessibleReposEnv(ctx.User.ID)
  95. if err != nil {
  96. ctx.ServerError("AccessibleReposEnv", err)
  97. return
  98. }
  99. mirrors, err = env.MirrorRepos()
  100. if err != nil {
  101. ctx.ServerError("env.MirrorRepos", err)
  102. return
  103. }
  104. } else {
  105. mirrors, err = ctxUser.GetMirrorRepositories()
  106. if err != nil {
  107. ctx.ServerError("GetMirrorRepositories", err)
  108. return
  109. }
  110. }
  111. ctx.Data["MaxShowRepoNum"] = setting.UI.User.RepoPagingNum
  112. if err := models.MirrorRepositoryList(mirrors).LoadAttributes(); err != nil {
  113. ctx.ServerError("MirrorRepositoryList.LoadAttributes", err)
  114. return
  115. }
  116. ctx.Data["MirrorCount"] = len(mirrors)
  117. ctx.Data["Mirrors"] = mirrors
  118. retrieveFeeds(ctx, models.GetFeedsOptions{
  119. RequestedUser: ctxUser,
  120. IncludePrivate: true,
  121. OnlyPerformedBy: false,
  122. IncludeDeleted: false,
  123. })
  124. if ctx.Written() {
  125. return
  126. }
  127. ctx.HTML(200, tplDashboard)
  128. }
  129. // Issues render the user issues page
  130. func Issues(ctx *context.Context) {
  131. isPullList := ctx.Params(":type") == "pulls"
  132. if isPullList {
  133. ctx.Data["Title"] = ctx.Tr("pull_requests")
  134. ctx.Data["PageIsPulls"] = true
  135. } else {
  136. ctx.Data["Title"] = ctx.Tr("issues")
  137. ctx.Data["PageIsIssues"] = true
  138. }
  139. ctxUser := getDashboardContextUser(ctx)
  140. if ctx.Written() {
  141. return
  142. }
  143. // Organization does not have view type and filter mode.
  144. var (
  145. viewType string
  146. sortType = ctx.Query("sort")
  147. filterMode = models.FilterModeAll
  148. )
  149. if ctxUser.IsOrganization() {
  150. viewType = "all"
  151. } else {
  152. viewType = ctx.Query("type")
  153. switch viewType {
  154. case "assigned":
  155. filterMode = models.FilterModeAssign
  156. case "created_by":
  157. filterMode = models.FilterModeCreate
  158. case "all": // filterMode already set to All
  159. default:
  160. viewType = "all"
  161. }
  162. }
  163. page := ctx.QueryInt("page")
  164. if page <= 1 {
  165. page = 1
  166. }
  167. repoID := ctx.QueryInt64("repo")
  168. isShowClosed := ctx.Query("state") == "closed"
  169. // Get repositories.
  170. var err error
  171. var userRepoIDs []int64
  172. if ctxUser.IsOrganization() {
  173. env, err := ctxUser.AccessibleReposEnv(ctx.User.ID)
  174. if err != nil {
  175. ctx.ServerError("AccessibleReposEnv", err)
  176. return
  177. }
  178. userRepoIDs, err = env.RepoIDs(1, ctxUser.NumRepos)
  179. if err != nil {
  180. ctx.ServerError("env.RepoIDs", err)
  181. return
  182. }
  183. } else {
  184. unitType := models.UnitTypeIssues
  185. if isPullList {
  186. unitType = models.UnitTypePullRequests
  187. }
  188. userRepoIDs, err = ctxUser.GetAccessRepoIDs(unitType)
  189. if err != nil {
  190. ctx.ServerError("ctxUser.GetAccessRepoIDs", err)
  191. return
  192. }
  193. }
  194. if len(userRepoIDs) == 0 {
  195. userRepoIDs = []int64{-1}
  196. }
  197. opts := &models.IssuesOptions{
  198. IsClosed: util.OptionalBoolOf(isShowClosed),
  199. IsPull: util.OptionalBoolOf(isPullList),
  200. SortType: sortType,
  201. }
  202. if repoID > 0 {
  203. opts.RepoIDs = []int64{repoID}
  204. }
  205. switch filterMode {
  206. case models.FilterModeAll:
  207. if repoID > 0 {
  208. if !com.IsSliceContainsInt64(userRepoIDs, repoID) {
  209. // force an empty result
  210. opts.RepoIDs = []int64{-1}
  211. }
  212. } else {
  213. opts.RepoIDs = userRepoIDs
  214. }
  215. case models.FilterModeAssign:
  216. opts.AssigneeID = ctxUser.ID
  217. case models.FilterModeCreate:
  218. opts.PosterID = ctxUser.ID
  219. case models.FilterModeMention:
  220. opts.MentionedID = ctxUser.ID
  221. }
  222. counts, err := models.CountIssuesByRepo(opts)
  223. if err != nil {
  224. ctx.ServerError("CountIssuesByRepo", err)
  225. return
  226. }
  227. opts.Page = page
  228. opts.PageSize = setting.UI.IssuePagingNum
  229. opts.Labels = ctx.Query("labels")
  230. issues, err := models.Issues(opts)
  231. if err != nil {
  232. ctx.ServerError("Issues", err)
  233. return
  234. }
  235. showReposMap := make(map[int64]*models.Repository, len(counts))
  236. for repoID := range counts {
  237. repo, err := models.GetRepositoryByID(repoID)
  238. if err != nil {
  239. ctx.ServerError("GetRepositoryByID", err)
  240. return
  241. }
  242. showReposMap[repoID] = repo
  243. }
  244. if repoID > 0 {
  245. if _, ok := showReposMap[repoID]; !ok {
  246. repo, err := models.GetRepositoryByID(repoID)
  247. if err != nil {
  248. ctx.ServerError("GetRepositoryByID", fmt.Errorf("[%d]%v", repoID, err))
  249. return
  250. }
  251. showReposMap[repoID] = repo
  252. }
  253. repo := showReposMap[repoID]
  254. // Check if user has access to given repository.
  255. if !repo.IsOwnedBy(ctxUser.ID) && !repo.HasAccess(ctxUser) {
  256. ctx.Status(404)
  257. return
  258. }
  259. }
  260. showRepos := models.RepositoryListOfMap(showReposMap)
  261. sort.Sort(showRepos)
  262. if err = showRepos.LoadAttributes(); err != nil {
  263. ctx.ServerError("LoadAttributes", err)
  264. return
  265. }
  266. for _, issue := range issues {
  267. issue.Repo = showReposMap[issue.RepoID]
  268. }
  269. issueStats, err := models.GetUserIssueStats(models.UserIssueStatsOptions{
  270. UserID: ctxUser.ID,
  271. RepoID: repoID,
  272. UserRepoIDs: userRepoIDs,
  273. FilterMode: filterMode,
  274. IsPull: isPullList,
  275. IsClosed: isShowClosed,
  276. })
  277. if err != nil {
  278. ctx.ServerError("GetUserIssueStats", err)
  279. return
  280. }
  281. var total int
  282. if !isShowClosed {
  283. total = int(issueStats.OpenCount)
  284. } else {
  285. total = int(issueStats.ClosedCount)
  286. }
  287. ctx.Data["Issues"] = issues
  288. ctx.Data["Repos"] = showRepos
  289. ctx.Data["Counts"] = counts
  290. ctx.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5)
  291. ctx.Data["IssueStats"] = issueStats
  292. ctx.Data["ViewType"] = viewType
  293. ctx.Data["SortType"] = sortType
  294. ctx.Data["RepoID"] = repoID
  295. ctx.Data["IsShowClosed"] = isShowClosed
  296. if isShowClosed {
  297. ctx.Data["State"] = "closed"
  298. } else {
  299. ctx.Data["State"] = "open"
  300. }
  301. ctx.HTML(200, tplIssues)
  302. }
  303. // ShowSSHKeys output all the ssh keys of user by uid
  304. func ShowSSHKeys(ctx *context.Context, uid int64) {
  305. keys, err := models.ListPublicKeys(uid)
  306. if err != nil {
  307. ctx.ServerError("ListPublicKeys", err)
  308. return
  309. }
  310. var buf bytes.Buffer
  311. for i := range keys {
  312. buf.WriteString(keys[i].OmitEmail())
  313. buf.WriteString("\n")
  314. }
  315. ctx.PlainText(200, buf.Bytes())
  316. }
  317. func showOrgProfile(ctx *context.Context) {
  318. ctx.SetParams(":org", ctx.Params(":username"))
  319. context.HandleOrgAssignment(ctx)
  320. if ctx.Written() {
  321. return
  322. }
  323. org := ctx.Org.Organization
  324. ctx.Data["Title"] = org.DisplayName()
  325. page := ctx.QueryInt("page")
  326. if page <= 0 {
  327. page = 1
  328. }
  329. var (
  330. repos []*models.Repository
  331. count int64
  332. err error
  333. )
  334. if ctx.IsSigned && !ctx.User.IsAdmin {
  335. env, err := org.AccessibleReposEnv(ctx.User.ID)
  336. if err != nil {
  337. ctx.ServerError("AccessibleReposEnv", err)
  338. return
  339. }
  340. repos, err = env.Repos(page, setting.UI.User.RepoPagingNum)
  341. if err != nil {
  342. ctx.ServerError("env.Repos", err)
  343. return
  344. }
  345. count, err = env.CountRepos()
  346. if err != nil {
  347. ctx.ServerError("env.CountRepos", err)
  348. return
  349. }
  350. ctx.Data["Repos"] = repos
  351. } else {
  352. showPrivate := ctx.IsSigned && ctx.User.IsAdmin
  353. repos, err = models.GetUserRepositories(org.ID, showPrivate, page, setting.UI.User.RepoPagingNum, "")
  354. if err != nil {
  355. ctx.ServerError("GetRepositories", err)
  356. return
  357. }
  358. ctx.Data["Repos"] = repos
  359. count = models.CountUserRepositories(org.ID, showPrivate)
  360. }
  361. ctx.Data["Page"] = paginater.New(int(count), setting.UI.User.RepoPagingNum, page, 5)
  362. if err := org.GetMembers(); err != nil {
  363. ctx.ServerError("GetMembers", err)
  364. return
  365. }
  366. ctx.Data["Members"] = org.Members
  367. ctx.Data["Teams"] = org.Teams
  368. ctx.HTML(200, tplOrgHome)
  369. }
  370. // Email2User show user page via email
  371. func Email2User(ctx *context.Context) {
  372. u, err := models.GetUserByEmail(ctx.Query("email"))
  373. if err != nil {
  374. if models.IsErrUserNotExist(err) {
  375. ctx.NotFound("GetUserByEmail", err)
  376. } else {
  377. ctx.ServerError("GetUserByEmail", err)
  378. }
  379. return
  380. }
  381. ctx.Redirect(setting.AppSubURL + "/user/" + u.Name)
  382. }