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.

411 lines
9.6 KiB

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