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.

user.go 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package explore
  4. import (
  5. "bytes"
  6. "net/http"
  7. "code.gitea.io/gitea/models/db"
  8. user_model "code.gitea.io/gitea/models/user"
  9. "code.gitea.io/gitea/modules/base"
  10. "code.gitea.io/gitea/modules/container"
  11. "code.gitea.io/gitea/modules/context"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "code.gitea.io/gitea/modules/sitemap"
  15. "code.gitea.io/gitea/modules/structs"
  16. "code.gitea.io/gitea/modules/util"
  17. )
  18. const (
  19. // tplExploreUsers explore users page template
  20. tplExploreUsers base.TplName = "explore/users"
  21. )
  22. // UserSearchDefaultSortType is the default sort type for user search
  23. const (
  24. UserSearchDefaultSortType = "recentupdate"
  25. UserSearchDefaultAdminSort = "alphabetically"
  26. )
  27. var nullByte = []byte{0x00}
  28. func isKeywordValid(keyword string) bool {
  29. return !bytes.Contains([]byte(keyword), nullByte)
  30. }
  31. // RenderUserSearch render user search page
  32. func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName base.TplName) {
  33. // Sitemap index for sitemap paths
  34. opts.Page = int(ctx.ParamsInt64("idx"))
  35. isSitemap := ctx.Params("idx") != ""
  36. if opts.Page <= 1 {
  37. opts.Page = ctx.FormInt("page")
  38. }
  39. if opts.Page <= 1 {
  40. opts.Page = 1
  41. }
  42. if isSitemap {
  43. opts.PageSize = setting.UI.SitemapPagingNum
  44. }
  45. var (
  46. users []*user_model.User
  47. count int64
  48. err error
  49. orderBy db.SearchOrderBy
  50. )
  51. // we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns
  52. sortOrder := ctx.FormString("sort")
  53. switch sortOrder {
  54. case "newest":
  55. orderBy = "`user`.id DESC"
  56. case "oldest":
  57. orderBy = "`user`.id ASC"
  58. case "leastupdate":
  59. orderBy = "`user`.updated_unix ASC"
  60. case "reversealphabetically":
  61. orderBy = "`user`.name DESC"
  62. case "lastlogin":
  63. orderBy = "`user`.last_login_unix ASC"
  64. case "reverselastlogin":
  65. orderBy = "`user`.last_login_unix DESC"
  66. case "alphabetically":
  67. orderBy = "`user`.name ASC"
  68. case "recentupdate":
  69. fallthrough
  70. default:
  71. // in case the sortType is not valid, we set it to recentupdate
  72. sortOrder = "recentupdate"
  73. orderBy = "`user`.updated_unix DESC"
  74. }
  75. ctx.Data["SortType"] = sortOrder
  76. if opts.SupportedSortOrders != nil && !opts.SupportedSortOrders.Contains(sortOrder) {
  77. ctx.NotFound("unsupported sort order", nil)
  78. return
  79. }
  80. opts.Keyword = ctx.FormTrim("q")
  81. opts.OrderBy = orderBy
  82. if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
  83. users, count, err = user_model.SearchUsers(ctx, opts)
  84. if err != nil {
  85. ctx.ServerError("SearchUsers", err)
  86. return
  87. }
  88. }
  89. if isSitemap {
  90. m := sitemap.NewSitemap()
  91. for _, item := range users {
  92. m.Add(sitemap.URL{URL: item.HTMLURL(), LastMod: item.UpdatedUnix.AsTimePtr()})
  93. }
  94. ctx.Resp.Header().Set("Content-Type", "text/xml")
  95. if _, err := m.WriteTo(ctx.Resp); err != nil {
  96. log.Error("Failed writing sitemap: %v", err)
  97. }
  98. return
  99. }
  100. ctx.Data["Keyword"] = opts.Keyword
  101. ctx.Data["Total"] = count
  102. ctx.Data["Users"] = users
  103. ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx)
  104. ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail
  105. ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
  106. pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
  107. pager.SetDefaultParams(ctx)
  108. for paramKey, paramVal := range opts.ExtraParamStrings {
  109. pager.AddParamString(paramKey, paramVal)
  110. }
  111. ctx.Data["Page"] = pager
  112. ctx.HTML(http.StatusOK, tplName)
  113. }
  114. // Users render explore users page
  115. func Users(ctx *context.Context) {
  116. if setting.Service.Explore.DisableUsersPage {
  117. ctx.Redirect(setting.AppSubURL + "/explore/repos")
  118. return
  119. }
  120. ctx.Data["Title"] = ctx.Tr("explore")
  121. ctx.Data["PageIsExplore"] = true
  122. ctx.Data["PageIsExploreUsers"] = true
  123. ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
  124. supportedSortOrders := container.SetOf(
  125. "newest",
  126. "oldest",
  127. "alphabetically",
  128. "reversealphabetically",
  129. )
  130. sortOrder := ctx.FormString("sort")
  131. if sortOrder == "" {
  132. sortOrder = "newest"
  133. ctx.SetFormString("sort", sortOrder)
  134. }
  135. RenderUserSearch(ctx, &user_model.SearchUserOptions{
  136. Actor: ctx.Doer,
  137. Type: user_model.UserTypeIndividual,
  138. ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
  139. IsActive: util.OptionalBoolTrue,
  140. Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
  141. SupportedSortOrders: supportedSortOrders,
  142. }, tplExploreUsers)
  143. }