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 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package user
  5. import (
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. activities_model "code.gitea.io/gitea/models/activities"
  10. "code.gitea.io/gitea/models/db"
  11. "code.gitea.io/gitea/models/organization"
  12. project_model "code.gitea.io/gitea/models/project"
  13. repo_model "code.gitea.io/gitea/models/repo"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/context"
  16. "code.gitea.io/gitea/modules/markup"
  17. "code.gitea.io/gitea/modules/markup/markdown"
  18. "code.gitea.io/gitea/modules/setting"
  19. "code.gitea.io/gitea/modules/util"
  20. "code.gitea.io/gitea/routers/web/feed"
  21. "code.gitea.io/gitea/routers/web/org"
  22. )
  23. // Profile render user's profile page
  24. func Profile(ctx *context.Context) {
  25. if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") {
  26. feed.ShowUserFeedRSS(ctx)
  27. return
  28. }
  29. if strings.Contains(ctx.Req.Header.Get("Accept"), "application/atom+xml") {
  30. feed.ShowUserFeedAtom(ctx)
  31. return
  32. }
  33. if ctx.ContextUser.IsOrganization() {
  34. org.Home(ctx)
  35. return
  36. }
  37. // check view permissions
  38. if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
  39. ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
  40. return
  41. }
  42. // advertise feed via meta tag
  43. ctx.Data["FeedURL"] = ctx.ContextUser.HTMLURL()
  44. // Show OpenID URIs
  45. openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID)
  46. if err != nil {
  47. ctx.ServerError("GetUserOpenIDs", err)
  48. return
  49. }
  50. var isFollowing bool
  51. if ctx.Doer != nil {
  52. isFollowing = user_model.IsFollowing(ctx.Doer.ID, ctx.ContextUser.ID)
  53. }
  54. ctx.Data["Title"] = ctx.ContextUser.DisplayName()
  55. ctx.Data["PageIsUserProfile"] = true
  56. ctx.Data["Owner"] = ctx.ContextUser
  57. ctx.Data["OpenIDs"] = openIDs
  58. ctx.Data["IsFollowing"] = isFollowing
  59. if setting.Service.EnableUserHeatmap {
  60. data, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
  61. if err != nil {
  62. ctx.ServerError("GetUserHeatmapDataByUser", err)
  63. return
  64. }
  65. ctx.Data["HeatmapData"] = data
  66. }
  67. if len(ctx.ContextUser.Description) != 0 {
  68. content, err := markdown.RenderString(&markup.RenderContext{
  69. URLPrefix: ctx.Repo.RepoLink,
  70. Metas: map[string]string{"mode": "document"},
  71. GitRepo: ctx.Repo.GitRepo,
  72. Ctx: ctx,
  73. }, ctx.ContextUser.Description)
  74. if err != nil {
  75. ctx.ServerError("RenderString", err)
  76. return
  77. }
  78. ctx.Data["RenderedDescription"] = content
  79. }
  80. showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
  81. orgs, err := organization.FindOrgs(organization.FindOrgOptions{
  82. UserID: ctx.ContextUser.ID,
  83. IncludePrivate: showPrivate,
  84. })
  85. if err != nil {
  86. ctx.ServerError("FindOrgs", err)
  87. return
  88. }
  89. ctx.Data["Orgs"] = orgs
  90. ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer)
  91. badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
  92. if err != nil {
  93. ctx.ServerError("GetUserBadges", err)
  94. return
  95. }
  96. ctx.Data["Badges"] = badges
  97. tab := ctx.FormString("tab")
  98. ctx.Data["TabName"] = tab
  99. page := ctx.FormInt("page")
  100. if page <= 0 {
  101. page = 1
  102. }
  103. topicOnly := ctx.FormBool("topic")
  104. var (
  105. repos []*repo_model.Repository
  106. count int64
  107. total int
  108. orderBy db.SearchOrderBy
  109. )
  110. ctx.Data["SortType"] = ctx.FormString("sort")
  111. switch ctx.FormString("sort") {
  112. case "newest":
  113. orderBy = db.SearchOrderByNewest
  114. case "oldest":
  115. orderBy = db.SearchOrderByOldest
  116. case "recentupdate":
  117. orderBy = db.SearchOrderByRecentUpdated
  118. case "leastupdate":
  119. orderBy = db.SearchOrderByLeastUpdated
  120. case "reversealphabetically":
  121. orderBy = db.SearchOrderByAlphabeticallyReverse
  122. case "alphabetically":
  123. orderBy = db.SearchOrderByAlphabetically
  124. case "moststars":
  125. orderBy = db.SearchOrderByStarsReverse
  126. case "feweststars":
  127. orderBy = db.SearchOrderByStars
  128. case "mostforks":
  129. orderBy = db.SearchOrderByForksReverse
  130. case "fewestforks":
  131. orderBy = db.SearchOrderByForks
  132. default:
  133. ctx.Data["SortType"] = "recentupdate"
  134. orderBy = db.SearchOrderByRecentUpdated
  135. }
  136. keyword := ctx.FormTrim("q")
  137. ctx.Data["Keyword"] = keyword
  138. language := ctx.FormTrim("language")
  139. ctx.Data["Language"] = language
  140. switch tab {
  141. case "followers":
  142. items, count, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
  143. PageSize: setting.UI.User.RepoPagingNum,
  144. Page: page,
  145. })
  146. if err != nil {
  147. ctx.ServerError("GetUserFollowers", err)
  148. return
  149. }
  150. ctx.Data["Cards"] = items
  151. total = int(count)
  152. case "following":
  153. items, count, err := user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{
  154. PageSize: setting.UI.User.RepoPagingNum,
  155. Page: page,
  156. })
  157. if err != nil {
  158. ctx.ServerError("GetUserFollowing", err)
  159. return
  160. }
  161. ctx.Data["Cards"] = items
  162. total = int(count)
  163. case "activity":
  164. ctx.Data["Feeds"], err = activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
  165. RequestedUser: ctx.ContextUser,
  166. Actor: ctx.Doer,
  167. IncludePrivate: showPrivate,
  168. OnlyPerformedBy: true,
  169. IncludeDeleted: false,
  170. Date: ctx.FormString("date"),
  171. ListOptions: db.ListOptions{PageSize: setting.UI.FeedPagingNum},
  172. })
  173. if err != nil {
  174. ctx.ServerError("GetFeeds", err)
  175. return
  176. }
  177. case "stars":
  178. ctx.Data["PageIsProfileStarList"] = true
  179. repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
  180. ListOptions: db.ListOptions{
  181. PageSize: setting.UI.User.RepoPagingNum,
  182. Page: page,
  183. },
  184. Actor: ctx.Doer,
  185. Keyword: keyword,
  186. OrderBy: orderBy,
  187. Private: ctx.IsSigned,
  188. StarredByID: ctx.ContextUser.ID,
  189. Collaborate: util.OptionalBoolFalse,
  190. TopicOnly: topicOnly,
  191. Language: language,
  192. IncludeDescription: setting.UI.SearchRepoDescription,
  193. })
  194. if err != nil {
  195. ctx.ServerError("SearchRepository", err)
  196. return
  197. }
  198. total = int(count)
  199. case "projects":
  200. ctx.Data["OpenProjects"], _, err = project_model.FindProjects(ctx, project_model.SearchOptions{
  201. Page: -1,
  202. IsClosed: util.OptionalBoolFalse,
  203. Type: project_model.TypeIndividual,
  204. })
  205. if err != nil {
  206. ctx.ServerError("GetProjects", err)
  207. return
  208. }
  209. case "watching":
  210. repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
  211. ListOptions: db.ListOptions{
  212. PageSize: setting.UI.User.RepoPagingNum,
  213. Page: page,
  214. },
  215. Actor: ctx.Doer,
  216. Keyword: keyword,
  217. OrderBy: orderBy,
  218. Private: ctx.IsSigned,
  219. WatchedByID: ctx.ContextUser.ID,
  220. Collaborate: util.OptionalBoolFalse,
  221. TopicOnly: topicOnly,
  222. Language: language,
  223. IncludeDescription: setting.UI.SearchRepoDescription,
  224. })
  225. if err != nil {
  226. ctx.ServerError("SearchRepository", err)
  227. return
  228. }
  229. total = int(count)
  230. default:
  231. repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
  232. ListOptions: db.ListOptions{
  233. PageSize: setting.UI.User.RepoPagingNum,
  234. Page: page,
  235. },
  236. Actor: ctx.Doer,
  237. Keyword: keyword,
  238. OwnerID: ctx.ContextUser.ID,
  239. OrderBy: orderBy,
  240. Private: ctx.IsSigned,
  241. Collaborate: util.OptionalBoolFalse,
  242. TopicOnly: topicOnly,
  243. Language: language,
  244. IncludeDescription: setting.UI.SearchRepoDescription,
  245. })
  246. if err != nil {
  247. ctx.ServerError("SearchRepository", err)
  248. return
  249. }
  250. total = int(count)
  251. }
  252. ctx.Data["Repos"] = repos
  253. ctx.Data["Total"] = total
  254. pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
  255. pager.SetDefaultParams(ctx)
  256. pager.AddParam(ctx, "tab", "TabName")
  257. if tab != "followers" && tab != "following" && tab != "activity" && tab != "projects" {
  258. pager.AddParam(ctx, "language", "Language")
  259. }
  260. ctx.Data["Page"] = pager
  261. ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
  262. ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
  263. ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.Doer.ID)
  264. ctx.HTML(http.StatusOK, tplProfile)
  265. }
  266. // Action response for follow/unfollow user request
  267. func Action(ctx *context.Context) {
  268. var err error
  269. switch ctx.FormString("action") {
  270. case "follow":
  271. err = user_model.FollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
  272. case "unfollow":
  273. err = user_model.UnfollowUser(ctx.Doer.ID, ctx.ContextUser.ID)
  274. }
  275. if err != nil {
  276. ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.FormString("action")), err)
  277. return
  278. }
  279. // FIXME: We should check this URL and make sure that it's a valid Gitea URL
  280. ctx.RedirectToFirst(ctx.FormString("redirect_to"), ctx.ContextUser.HomeLink())
  281. }