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

461 lines
11 KiB

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