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.

403 lines
9.3 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 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. "strings"
  9. "github.com/Unknwon/com"
  10. "github.com/Unknwon/paginater"
  11. "github.com/gogits/gogs/models"
  12. "github.com/gogits/gogs/modules/base"
  13. "github.com/gogits/gogs/modules/middleware"
  14. "github.com/gogits/gogs/modules/setting"
  15. )
  16. const (
  17. DASHBOARD base.TplName = "user/dashboard/dashboard"
  18. ISSUES base.TplName = "user/dashboard/issues"
  19. STARS base.TplName = "user/stars"
  20. PROFILE base.TplName = "user/profile"
  21. )
  22. func getDashboardContextUser(ctx *middleware.Context) *models.User {
  23. ctxUser := ctx.User
  24. orgName := ctx.Params(":org")
  25. if len(orgName) > 0 {
  26. // Organization.
  27. org, err := models.GetUserByName(orgName)
  28. if err != nil {
  29. if models.IsErrUserNotExist(err) {
  30. ctx.Handle(404, "GetUserByName", err)
  31. } else {
  32. ctx.Handle(500, "GetUserByName", err)
  33. }
  34. return nil
  35. }
  36. ctxUser = org
  37. }
  38. ctx.Data["ContextUser"] = ctxUser
  39. if err := ctx.User.GetOrganizations(); err != nil {
  40. ctx.Handle(500, "GetOrganizations", err)
  41. return nil
  42. }
  43. ctx.Data["Orgs"] = ctx.User.Orgs
  44. return ctxUser
  45. }
  46. func Dashboard(ctx *middleware.Context) {
  47. ctx.Data["Title"] = ctx.Tr("dashboard")
  48. ctx.Data["PageIsDashboard"] = true
  49. ctx.Data["PageIsNews"] = true
  50. ctxUser := getDashboardContextUser(ctx)
  51. if ctx.Written() {
  52. return
  53. }
  54. // Check context type.
  55. if !ctxUser.IsOrganization() {
  56. // Normal user.
  57. ctxUser = ctx.User
  58. collaborates, err := ctx.User.GetAccessibleRepositories()
  59. if err != nil {
  60. ctx.Handle(500, "GetAccessibleRepositories", err)
  61. return
  62. }
  63. repositories := make([]*models.Repository, 0, len(collaborates))
  64. for repo := range collaborates {
  65. repositories = append(repositories, repo)
  66. }
  67. ctx.Data["CollaborateCount"] = len(repositories)
  68. ctx.Data["CollaborativeRepos"] = repositories
  69. }
  70. repos, err := models.GetRepositories(ctxUser.Id, true)
  71. if err != nil {
  72. ctx.Handle(500, "GetRepositories", err)
  73. return
  74. }
  75. ctx.Data["Repos"] = repos
  76. // Get mirror repositories.
  77. mirrors := make([]*models.Repository, 0, len(repos)/2)
  78. for _, repo := range repos {
  79. if repo.IsMirror {
  80. if err = repo.GetMirror(); err != nil {
  81. ctx.Handle(500, "GetMirror: "+repo.Name, err)
  82. return
  83. }
  84. mirrors = append(mirrors, repo)
  85. }
  86. }
  87. ctx.Data["MirrorCount"] = len(mirrors)
  88. ctx.Data["Mirrors"] = mirrors
  89. // Get feeds.
  90. actions, err := models.GetFeeds(ctxUser.Id, 0, false)
  91. if err != nil {
  92. ctx.Handle(500, "GetFeeds", err)
  93. return
  94. }
  95. // Check access of private repositories.
  96. feeds := make([]*models.Action, 0, len(actions))
  97. for _, act := range actions {
  98. if act.IsPrivate {
  99. // This prevents having to retrieve the repository for each action
  100. repo := &models.Repository{ID: act.RepoID, IsPrivate: true}
  101. if act.RepoUserName != ctx.User.LowerName {
  102. if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
  103. continue
  104. }
  105. }
  106. }
  107. // FIXME: cache results?
  108. u, err := models.GetUserByName(act.ActUserName)
  109. if err != nil {
  110. if models.IsErrUserNotExist(err) {
  111. continue
  112. }
  113. ctx.Handle(500, "GetUserByName", err)
  114. return
  115. }
  116. act.ActAvatar = u.AvatarLink()
  117. feeds = append(feeds, act)
  118. }
  119. ctx.Data["Feeds"] = feeds
  120. ctx.HTML(200, DASHBOARD)
  121. }
  122. func Issues(ctx *middleware.Context) {
  123. isPullList := ctx.Params(":type") == "pulls"
  124. if isPullList {
  125. ctx.Data["Title"] = ctx.Tr("pull_requests")
  126. ctx.Data["PageIsPulls"] = true
  127. } else {
  128. ctx.Data["Title"] = ctx.Tr("issues")
  129. ctx.Data["PageIsIssues"] = true
  130. }
  131. ctxUser := getDashboardContextUser(ctx)
  132. if ctx.Written() {
  133. return
  134. }
  135. // Organization does not have view type and filter mode.
  136. var (
  137. viewType string
  138. filterMode = models.FM_ALL
  139. assigneeID int64
  140. posterID int64
  141. )
  142. if ctxUser.IsOrganization() {
  143. viewType = "all"
  144. } else {
  145. viewType = ctx.Query("type")
  146. types := []string{"assigned", "created_by"}
  147. if !com.IsSliceContainsStr(types, viewType) {
  148. viewType = "all"
  149. }
  150. switch viewType {
  151. case "assigned":
  152. filterMode = models.FM_ASSIGN
  153. assigneeID = ctxUser.Id
  154. case "created_by":
  155. filterMode = models.FM_CREATE
  156. posterID = ctxUser.Id
  157. }
  158. }
  159. repoID := ctx.QueryInt64("repo")
  160. isShowClosed := ctx.Query("state") == "closed"
  161. // Get repositories.
  162. repos, err := models.GetRepositories(ctxUser.Id, true)
  163. if err != nil {
  164. ctx.Handle(500, "GetRepositories", err)
  165. return
  166. }
  167. allCount := 0
  168. repoIDs := make([]int64, 0, len(repos))
  169. showRepos := make([]*models.Repository, 0, len(repos))
  170. for _, repo := range repos {
  171. if (isPullList && repo.NumPulls == 0) ||
  172. (!isPullList && repo.NumIssues == 0) {
  173. continue
  174. }
  175. repoIDs = append(repoIDs, repo.ID)
  176. if isPullList {
  177. allCount += repo.NumOpenPulls
  178. repo.NumOpenIssues = repo.NumOpenPulls
  179. repo.NumClosedIssues = repo.NumClosedPulls
  180. } else {
  181. allCount += repo.NumOpenIssues
  182. }
  183. if filterMode != models.FM_ALL {
  184. // Calculate repository issue count with filter mode.
  185. numOpen, numClosed := repo.IssueStats(ctxUser.Id, filterMode, isPullList)
  186. repo.NumOpenIssues, repo.NumClosedIssues = int(numOpen), int(numClosed)
  187. }
  188. if repo.ID == repoID ||
  189. (isShowClosed && repo.NumClosedIssues > 0) ||
  190. (!isShowClosed && repo.NumOpenIssues > 0) {
  191. showRepos = append(showRepos, repo)
  192. }
  193. }
  194. ctx.Data["Repos"] = showRepos
  195. issueStats := models.GetUserIssueStats(repoID, ctxUser.Id, repoIDs, filterMode, isPullList)
  196. issueStats.AllCount = int64(allCount)
  197. page := ctx.QueryInt("page")
  198. if page <= 1 {
  199. page = 1
  200. }
  201. var total int
  202. if !isShowClosed {
  203. total = int(issueStats.OpenCount)
  204. } else {
  205. total = int(issueStats.ClosedCount)
  206. }
  207. ctx.Data["Page"] = paginater.New(total, setting.IssuePagingNum, page, 5)
  208. // Get issues.
  209. issues, err := models.Issues(&models.IssuesOptions{
  210. UserID: ctxUser.Id,
  211. AssigneeID: assigneeID,
  212. RepoID: repoID,
  213. PosterID: posterID,
  214. RepoIDs: repoIDs,
  215. Page: page,
  216. IsClosed: isShowClosed,
  217. IsPull: isPullList,
  218. })
  219. if err != nil {
  220. ctx.Handle(500, "Issues: %v", err)
  221. return
  222. }
  223. // Get posters and repository.
  224. for i := range issues {
  225. issues[i].Repo, err = models.GetRepositoryByID(issues[i].RepoID)
  226. if err != nil {
  227. ctx.Handle(500, "GetRepositoryByID", fmt.Errorf("[#%d]%v", issues[i].ID, err))
  228. return
  229. }
  230. if err = issues[i].Repo.GetOwner(); err != nil {
  231. ctx.Handle(500, "GetOwner", fmt.Errorf("[#%d]%v", issues[i].ID, err))
  232. return
  233. }
  234. if err = issues[i].GetPoster(); err != nil {
  235. ctx.Handle(500, "GetPoster", fmt.Errorf("[#%d]%v", issues[i].ID, err))
  236. return
  237. }
  238. }
  239. ctx.Data["Issues"] = issues
  240. ctx.Data["IssueStats"] = issueStats
  241. ctx.Data["ViewType"] = viewType
  242. ctx.Data["RepoID"] = repoID
  243. ctx.Data["IsShowClosed"] = isShowClosed
  244. if isShowClosed {
  245. ctx.Data["State"] = "closed"
  246. } else {
  247. ctx.Data["State"] = "open"
  248. }
  249. ctx.HTML(200, ISSUES)
  250. }
  251. func ShowSSHKeys(ctx *middleware.Context, uid int64) {
  252. keys, err := models.ListPublicKeys(uid)
  253. if err != nil {
  254. ctx.Handle(500, "ListPublicKeys", err)
  255. return
  256. }
  257. var buf bytes.Buffer
  258. for i := range keys {
  259. buf.WriteString(keys[i].OmitEmail())
  260. buf.WriteString("\n")
  261. }
  262. ctx.PlainText(200, buf.Bytes())
  263. }
  264. func Profile(ctx *middleware.Context) {
  265. ctx.Data["Title"] = "Profile"
  266. ctx.Data["PageIsUserProfile"] = true
  267. uname := ctx.Params(":username")
  268. // Special handle for FireFox requests favicon.ico.
  269. if uname == "favicon.ico" {
  270. ctx.Redirect(setting.AppSubUrl + "/img/favicon.png")
  271. return
  272. }
  273. isShowKeys := false
  274. if strings.HasSuffix(uname, ".keys") {
  275. isShowKeys = true
  276. uname = strings.TrimSuffix(uname, ".keys")
  277. }
  278. u, err := models.GetUserByName(uname)
  279. if err != nil {
  280. if models.IsErrUserNotExist(err) {
  281. ctx.Handle(404, "GetUserByName", err)
  282. } else {
  283. ctx.Handle(500, "GetUserByName", err)
  284. }
  285. return
  286. }
  287. // Show SSH keys.
  288. if isShowKeys {
  289. ShowSSHKeys(ctx, u.Id)
  290. return
  291. }
  292. if u.IsOrganization() {
  293. ctx.Redirect(setting.AppSubUrl + "/org/" + u.Name)
  294. return
  295. }
  296. ctx.Data["Owner"] = u
  297. tab := ctx.Query("tab")
  298. ctx.Data["TabName"] = tab
  299. switch tab {
  300. case "activity":
  301. actions, err := models.GetFeeds(u.Id, 0, false)
  302. if err != nil {
  303. ctx.Handle(500, "GetFeeds", err)
  304. return
  305. }
  306. feeds := make([]*models.Action, 0, len(actions))
  307. for _, act := range actions {
  308. if act.IsPrivate {
  309. if !ctx.IsSigned {
  310. continue
  311. }
  312. // This prevents having to retrieve the repository for each action
  313. repo := &models.Repository{ID: act.RepoID, IsPrivate: true}
  314. if act.RepoUserName != ctx.User.LowerName {
  315. if has, _ := models.HasAccess(ctx.User, repo, models.ACCESS_MODE_READ); !has {
  316. continue
  317. }
  318. }
  319. }
  320. // FIXME: cache results?
  321. u, err := models.GetUserByName(act.ActUserName)
  322. if err != nil {
  323. if models.IsErrUserNotExist(err) {
  324. continue
  325. }
  326. ctx.Handle(500, "GetUserByName", err)
  327. return
  328. }
  329. act.ActAvatar = u.AvatarLink()
  330. feeds = append(feeds, act)
  331. }
  332. ctx.Data["Feeds"] = feeds
  333. default:
  334. ctx.Data["Repos"], err = models.GetRepositories(u.Id, ctx.IsSigned && ctx.User.Id == u.Id)
  335. if err != nil {
  336. ctx.Handle(500, "GetRepositories", err)
  337. return
  338. }
  339. }
  340. ctx.HTML(200, PROFILE)
  341. }
  342. func Email2User(ctx *middleware.Context) {
  343. u, err := models.GetUserByEmail(ctx.Query("email"))
  344. if err != nil {
  345. if models.IsErrUserNotExist(err) {
  346. ctx.Handle(404, "GetUserByEmail", err)
  347. } else {
  348. ctx.Handle(500, "GetUserByEmail", err)
  349. }
  350. return
  351. }
  352. ctx.Redirect(setting.AppSubUrl + "/user/" + u.Name)
  353. }