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.

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