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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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. Collaborate: true,
  142. IncludeDeleted: false,
  143. })
  144. if ctx.Written() {
  145. return
  146. }
  147. ctx.HTML(200, tplDashboard)
  148. }
  149. // Issues render the user issues page
  150. func Issues(ctx *context.Context) {
  151. isPullList := ctx.Params(":type") == "pulls"
  152. if isPullList {
  153. ctx.Data["Title"] = ctx.Tr("pull_requests")
  154. ctx.Data["PageIsPulls"] = true
  155. } else {
  156. ctx.Data["Title"] = ctx.Tr("issues")
  157. ctx.Data["PageIsIssues"] = true
  158. }
  159. ctxUser := getDashboardContextUser(ctx)
  160. if ctx.Written() {
  161. return
  162. }
  163. // Organization does not have view type and filter mode.
  164. var (
  165. viewType string
  166. sortType = ctx.Query("sort")
  167. filterMode = models.FilterModeAll
  168. )
  169. if ctxUser.IsOrganization() {
  170. viewType = "all"
  171. } else {
  172. viewType = ctx.Query("type")
  173. types := []string{"all", "assigned", "created_by"}
  174. if !com.IsSliceContainsStr(types, viewType) {
  175. viewType = "all"
  176. }
  177. switch viewType {
  178. case "all":
  179. filterMode = models.FilterModeAll
  180. case "assigned":
  181. filterMode = models.FilterModeAssign
  182. case "created_by":
  183. filterMode = models.FilterModeCreate
  184. }
  185. }
  186. page := ctx.QueryInt("page")
  187. if page <= 1 {
  188. page = 1
  189. }
  190. repoID := ctx.QueryInt64("repo")
  191. isShowClosed := ctx.Query("state") == "closed"
  192. // Get repositories.
  193. var err error
  194. var userRepoIDs []int64
  195. if ctxUser.IsOrganization() {
  196. env, err := ctxUser.AccessibleReposEnv(ctx.User.ID)
  197. if err != nil {
  198. ctx.Handle(500, "AccessibleReposEnv", err)
  199. return
  200. }
  201. userRepoIDs, err = env.RepoIDs(1, ctxUser.NumRepos)
  202. if err != nil {
  203. ctx.Handle(500, "env.RepoIDs", err)
  204. return
  205. }
  206. } else {
  207. userRepoIDs, err = ctxUser.GetAccessRepoIDs()
  208. if err != nil {
  209. ctx.Handle(500, "ctxUser.GetAccessRepoIDs", err)
  210. return
  211. }
  212. }
  213. if len(userRepoIDs) <= 0 {
  214. userRepoIDs = []int64{-1}
  215. }
  216. opts := &models.IssuesOptions{
  217. RepoID: repoID,
  218. IsClosed: util.OptionalBoolOf(isShowClosed),
  219. IsPull: util.OptionalBoolOf(isPullList),
  220. SortType: sortType,
  221. }
  222. switch filterMode {
  223. case models.FilterModeAll:
  224. opts.RepoIDs = userRepoIDs
  225. case models.FilterModeAssign:
  226. opts.AssigneeID = ctxUser.ID
  227. case models.FilterModeCreate:
  228. opts.PosterID = ctxUser.ID
  229. case models.FilterModeMention:
  230. opts.MentionedID = ctxUser.ID
  231. }
  232. counts, err := models.CountIssuesByRepo(opts)
  233. if err != nil {
  234. ctx.Handle(500, "CountIssuesByRepo", err)
  235. return
  236. }
  237. opts.Page = page
  238. opts.PageSize = setting.UI.IssuePagingNum
  239. issues, err := models.Issues(opts)
  240. if err != nil {
  241. ctx.Handle(500, "Issues", err)
  242. return
  243. }
  244. showReposMap := make(map[int64]*models.Repository, len(counts))
  245. for repoID := range counts {
  246. repo, err := models.GetRepositoryByID(repoID)
  247. if err != nil {
  248. ctx.Handle(500, "GetRepositoryByID", err)
  249. return
  250. }
  251. showReposMap[repoID] = repo
  252. }
  253. if repoID > 0 {
  254. if _, ok := showReposMap[repoID]; !ok {
  255. repo, err := models.GetRepositoryByID(repoID)
  256. if err != nil {
  257. ctx.Handle(500, "GetRepositoryByID", fmt.Errorf("[%d]%v", repoID, err))
  258. return
  259. }
  260. showReposMap[repoID] = repo
  261. }
  262. repo := showReposMap[repoID]
  263. // Check if user has access to given repository.
  264. if !repo.IsOwnedBy(ctxUser.ID) && !repo.HasAccess(ctxUser) {
  265. ctx.Status(404)
  266. return
  267. }
  268. }
  269. showRepos := models.RepositoryListOfMap(showReposMap)
  270. if err = showRepos.LoadAttributes(); err != nil {
  271. ctx.Handle(500, "LoadAttributes", fmt.Errorf("%v", err))
  272. return
  273. }
  274. for _, issue := range issues {
  275. issue.Repo = showReposMap[issue.RepoID]
  276. }
  277. issueStats := models.GetUserIssueStats(repoID, ctxUser.ID, userRepoIDs, filterMode, isPullList)
  278. var total int
  279. if !isShowClosed {
  280. total = int(issueStats.OpenCount)
  281. } else {
  282. total = int(issueStats.ClosedCount)
  283. }
  284. ctx.Data["Issues"] = issues
  285. ctx.Data["Repos"] = showRepos
  286. ctx.Data["Counts"] = counts
  287. ctx.Data["Page"] = paginater.New(total, setting.UI.IssuePagingNum, page, 5)
  288. ctx.Data["IssueStats"] = issueStats
  289. ctx.Data["ViewType"] = viewType
  290. ctx.Data["SortType"] = sortType
  291. ctx.Data["RepoID"] = repoID
  292. ctx.Data["IsShowClosed"] = isShowClosed
  293. if isShowClosed {
  294. ctx.Data["State"] = "closed"
  295. } else {
  296. ctx.Data["State"] = "open"
  297. }
  298. ctx.HTML(200, tplIssues)
  299. }
  300. // ShowSSHKeys output all the ssh keys of user by uid
  301. func ShowSSHKeys(ctx *context.Context, uid int64) {
  302. keys, err := models.ListPublicKeys(uid)
  303. if err != nil {
  304. ctx.Handle(500, "ListPublicKeys", err)
  305. return
  306. }
  307. var buf bytes.Buffer
  308. for i := range keys {
  309. buf.WriteString(keys[i].OmitEmail())
  310. buf.WriteString("\n")
  311. }
  312. ctx.PlainText(200, buf.Bytes())
  313. }
  314. func showOrgProfile(ctx *context.Context) {
  315. ctx.SetParams(":org", ctx.Params(":username"))
  316. context.HandleOrgAssignment(ctx)
  317. if ctx.Written() {
  318. return
  319. }
  320. org := ctx.Org.Organization
  321. ctx.Data["Title"] = org.DisplayName()
  322. page := ctx.QueryInt("page")
  323. if page <= 0 {
  324. page = 1
  325. }
  326. var (
  327. repos []*models.Repository
  328. count int64
  329. err error
  330. )
  331. if ctx.IsSigned && !ctx.User.IsAdmin {
  332. env, err := org.AccessibleReposEnv(ctx.User.ID)
  333. if err != nil {
  334. ctx.Handle(500, "AccessibleReposEnv", err)
  335. return
  336. }
  337. repos, err = env.Repos(page, setting.UI.User.RepoPagingNum)
  338. if err != nil {
  339. ctx.Handle(500, "env.Repos", err)
  340. return
  341. }
  342. count, err = env.CountRepos()
  343. if err != nil {
  344. ctx.Handle(500, "env.CountRepos", err)
  345. return
  346. }
  347. ctx.Data["Repos"] = repos
  348. } else {
  349. showPrivate := ctx.IsSigned && ctx.User.IsAdmin
  350. repos, err = models.GetUserRepositories(org.ID, showPrivate, page, setting.UI.User.RepoPagingNum, "")
  351. if err != nil {
  352. ctx.Handle(500, "GetRepositories", err)
  353. return
  354. }
  355. ctx.Data["Repos"] = repos
  356. count = models.CountUserRepositories(org.ID, showPrivate)
  357. }
  358. ctx.Data["Page"] = paginater.New(int(count), setting.UI.User.RepoPagingNum, page, 5)
  359. if err := org.GetMembers(); err != nil {
  360. ctx.Handle(500, "GetMembers", err)
  361. return
  362. }
  363. ctx.Data["Members"] = org.Members
  364. ctx.Data["Teams"] = org.Teams
  365. ctx.HTML(200, tplOrgHome)
  366. }
  367. // Email2User show user page via email
  368. func Email2User(ctx *context.Context) {
  369. u, err := models.GetUserByEmail(ctx.Query("email"))
  370. if err != nil {
  371. if models.IsErrUserNotExist(err) {
  372. ctx.Handle(404, "GetUserByEmail", err)
  373. } else {
  374. ctx.Handle(500, "GetUserByEmail", err)
  375. }
  376. return
  377. }
  378. ctx.Redirect(setting.AppSubURL + "/user/" + u.Name)
  379. }