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.

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