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