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.

profile.go 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package user
  6. import (
  7. "fmt"
  8. "net/http"
  9. "path"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/context"
  13. "code.gitea.io/gitea/modules/markup/markdown"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/util"
  16. "code.gitea.io/gitea/routers/org"
  17. )
  18. // GetUserByName get user by name
  19. func GetUserByName(ctx *context.Context, name string) *models.User {
  20. user, err := models.GetUserByName(name)
  21. if err != nil {
  22. if models.IsErrUserNotExist(err) {
  23. if redirectUserID, err := models.LookupUserRedirect(name); err == nil {
  24. context.RedirectToUser(ctx, name, redirectUserID)
  25. } else {
  26. ctx.NotFound("GetUserByName", err)
  27. }
  28. } else {
  29. ctx.ServerError("GetUserByName", err)
  30. }
  31. return nil
  32. }
  33. return user
  34. }
  35. // GetUserByParams returns user whose name is presented in URL paramenter.
  36. func GetUserByParams(ctx *context.Context) *models.User {
  37. return GetUserByName(ctx, ctx.Params(":username"))
  38. }
  39. // Profile render user's profile page
  40. func Profile(ctx *context.Context) {
  41. uname := ctx.Params(":username")
  42. // Special handle for FireFox requests favicon.ico.
  43. if uname == "favicon.ico" {
  44. ctx.ServeFile(path.Join(setting.StaticRootPath, "public/img/favicon.png"))
  45. return
  46. } else if strings.HasSuffix(uname, ".png") {
  47. ctx.Error(http.StatusNotFound)
  48. return
  49. }
  50. isShowKeys := false
  51. if strings.HasSuffix(uname, ".keys") {
  52. isShowKeys = true
  53. uname = strings.TrimSuffix(uname, ".keys")
  54. }
  55. isShowGPG := false
  56. if strings.HasSuffix(uname, ".gpg") {
  57. isShowGPG = true
  58. uname = strings.TrimSuffix(uname, ".gpg")
  59. }
  60. ctxUser := GetUserByName(ctx, uname)
  61. if ctx.Written() {
  62. return
  63. }
  64. // Show SSH keys.
  65. if isShowKeys {
  66. ShowSSHKeys(ctx, ctxUser.ID)
  67. return
  68. }
  69. // Show GPG keys.
  70. if isShowGPG {
  71. ShowGPGKeys(ctx, ctxUser.ID)
  72. return
  73. }
  74. if ctxUser.IsOrganization() {
  75. org.Home(ctx)
  76. return
  77. }
  78. // Show OpenID URIs
  79. openIDs, err := models.GetUserOpenIDs(ctxUser.ID)
  80. if err != nil {
  81. ctx.ServerError("GetUserOpenIDs", err)
  82. return
  83. }
  84. ctx.Data["Title"] = ctxUser.DisplayName()
  85. ctx.Data["PageIsUserProfile"] = true
  86. ctx.Data["Owner"] = ctxUser
  87. ctx.Data["OpenIDs"] = openIDs
  88. if setting.Service.EnableUserHeatmap {
  89. data, err := models.GetUserHeatmapDataByUser(ctxUser, ctx.User)
  90. if err != nil {
  91. ctx.ServerError("GetUserHeatmapDataByUser", err)
  92. return
  93. }
  94. ctx.Data["HeatmapData"] = data
  95. }
  96. if len(ctxUser.Description) != 0 {
  97. ctx.Data["RenderedDescription"] = string(markdown.Render([]byte(ctxUser.Description), ctx.Repo.RepoLink, map[string]string{"mode": "document"}))
  98. }
  99. showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID)
  100. orgs, err := models.GetOrgsByUserID(ctxUser.ID, showPrivate)
  101. if err != nil {
  102. ctx.ServerError("GetOrgsByUserIDDesc", err)
  103. return
  104. }
  105. ctx.Data["Orgs"] = orgs
  106. ctx.Data["HasOrgsVisible"] = models.HasOrgsVisible(orgs, ctx.User)
  107. tab := ctx.Query("tab")
  108. ctx.Data["TabName"] = tab
  109. page := ctx.QueryInt("page")
  110. if page <= 0 {
  111. page = 1
  112. }
  113. topicOnly := ctx.QueryBool("topic")
  114. var (
  115. repos []*models.Repository
  116. count int64
  117. total int
  118. orderBy models.SearchOrderBy
  119. )
  120. ctx.Data["SortType"] = ctx.Query("sort")
  121. switch ctx.Query("sort") {
  122. case "newest":
  123. orderBy = models.SearchOrderByNewest
  124. case "oldest":
  125. orderBy = models.SearchOrderByOldest
  126. case "recentupdate":
  127. orderBy = models.SearchOrderByRecentUpdated
  128. case "leastupdate":
  129. orderBy = models.SearchOrderByLeastUpdated
  130. case "reversealphabetically":
  131. orderBy = models.SearchOrderByAlphabeticallyReverse
  132. case "alphabetically":
  133. orderBy = models.SearchOrderByAlphabetically
  134. case "moststars":
  135. orderBy = models.SearchOrderByStarsReverse
  136. case "feweststars":
  137. orderBy = models.SearchOrderByStars
  138. case "mostforks":
  139. orderBy = models.SearchOrderByForksReverse
  140. case "fewestforks":
  141. orderBy = models.SearchOrderByForks
  142. default:
  143. ctx.Data["SortType"] = "recentupdate"
  144. orderBy = models.SearchOrderByRecentUpdated
  145. }
  146. keyword := strings.Trim(ctx.Query("q"), " ")
  147. ctx.Data["Keyword"] = keyword
  148. switch tab {
  149. case "followers":
  150. items, err := ctxUser.GetFollowers(models.ListOptions{
  151. PageSize: setting.UI.User.RepoPagingNum,
  152. Page: page,
  153. })
  154. if err != nil {
  155. ctx.ServerError("GetFollowers", err)
  156. return
  157. }
  158. ctx.Data["Cards"] = items
  159. total = ctxUser.NumFollowers
  160. case "following":
  161. items, err := ctxUser.GetFollowing(models.ListOptions{
  162. PageSize: setting.UI.User.RepoPagingNum,
  163. Page: page,
  164. })
  165. if err != nil {
  166. ctx.ServerError("GetFollowing", err)
  167. return
  168. }
  169. ctx.Data["Cards"] = items
  170. total = ctxUser.NumFollowing
  171. case "activity":
  172. retrieveFeeds(ctx, models.GetFeedsOptions{RequestedUser: ctxUser,
  173. Actor: ctx.User,
  174. IncludePrivate: showPrivate,
  175. OnlyPerformedBy: true,
  176. IncludeDeleted: false,
  177. Date: ctx.Query("date"),
  178. })
  179. if ctx.Written() {
  180. return
  181. }
  182. case "stars":
  183. ctx.Data["PageIsProfileStarList"] = true
  184. repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
  185. ListOptions: models.ListOptions{
  186. PageSize: setting.UI.User.RepoPagingNum,
  187. Page: page,
  188. },
  189. Actor: ctx.User,
  190. Keyword: keyword,
  191. OrderBy: orderBy,
  192. Private: ctx.IsSigned,
  193. StarredByID: ctxUser.ID,
  194. Collaborate: util.OptionalBoolFalse,
  195. TopicOnly: topicOnly,
  196. IncludeDescription: setting.UI.SearchRepoDescription,
  197. })
  198. if err != nil {
  199. ctx.ServerError("SearchRepository", err)
  200. return
  201. }
  202. total = int(count)
  203. case "projects":
  204. ctx.Data["OpenProjects"], _, err = models.GetProjects(models.ProjectSearchOptions{
  205. Page: -1,
  206. IsClosed: util.OptionalBoolFalse,
  207. Type: models.ProjectTypeIndividual,
  208. })
  209. if err != nil {
  210. ctx.ServerError("GetProjects", err)
  211. return
  212. }
  213. default:
  214. repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
  215. ListOptions: models.ListOptions{
  216. PageSize: setting.UI.User.RepoPagingNum,
  217. Page: page,
  218. },
  219. Actor: ctx.User,
  220. Keyword: keyword,
  221. OwnerID: ctxUser.ID,
  222. OrderBy: orderBy,
  223. Private: ctx.IsSigned,
  224. Collaborate: util.OptionalBoolFalse,
  225. TopicOnly: topicOnly,
  226. IncludeDescription: setting.UI.SearchRepoDescription,
  227. })
  228. if err != nil {
  229. ctx.ServerError("SearchRepository", err)
  230. return
  231. }
  232. total = int(count)
  233. }
  234. ctx.Data["Repos"] = repos
  235. ctx.Data["Total"] = total
  236. pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
  237. pager.SetDefaultParams(ctx)
  238. ctx.Data["Page"] = pager
  239. ctx.Data["ShowUserEmail"] = len(ctxUser.Email) > 0 && ctx.IsSigned && (!ctxUser.KeepEmailPrivate || ctxUser.ID == ctx.User.ID)
  240. ctx.HTML(http.StatusOK, tplProfile)
  241. }
  242. // Action response for follow/unfollow user request
  243. func Action(ctx *context.Context) {
  244. u := GetUserByParams(ctx)
  245. if ctx.Written() {
  246. return
  247. }
  248. var err error
  249. switch ctx.Params(":action") {
  250. case "follow":
  251. err = models.FollowUser(ctx.User.ID, u.ID)
  252. case "unfollow":
  253. err = models.UnfollowUser(ctx.User.ID, u.ID)
  254. }
  255. if err != nil {
  256. ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
  257. return
  258. }
  259. ctx.RedirectToFirst(ctx.Query("redirect_to"), u.HomeLink())
  260. }