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.

header.go 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package user
  4. import (
  5. "net/url"
  6. "code.gitea.io/gitea/models/db"
  7. "code.gitea.io/gitea/models/organization"
  8. access_model "code.gitea.io/gitea/models/perm/access"
  9. project_model "code.gitea.io/gitea/models/project"
  10. repo_model "code.gitea.io/gitea/models/repo"
  11. "code.gitea.io/gitea/models/unit"
  12. user_model "code.gitea.io/gitea/models/user"
  13. "code.gitea.io/gitea/modules/git"
  14. "code.gitea.io/gitea/modules/gitrepo"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/markup"
  17. "code.gitea.io/gitea/modules/markup/markdown"
  18. "code.gitea.io/gitea/modules/optional"
  19. "code.gitea.io/gitea/modules/setting"
  20. "code.gitea.io/gitea/services/context"
  21. )
  22. // prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu)
  23. // It is designed to be fast and safe to be called multiple times in one request
  24. func prepareContextForCommonProfile(ctx *context.Context) {
  25. ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
  26. ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
  27. ctx.Data["EnableFeed"] = setting.Other.EnableFeed
  28. ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink()
  29. }
  30. // PrepareContextForProfileBigAvatar set the context for big avatar view on the profile page
  31. func PrepareContextForProfileBigAvatar(ctx *context.Context) {
  32. prepareContextForCommonProfile(ctx)
  33. ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
  34. ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
  35. if setting.Service.UserLocationMapURL != "" {
  36. ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location)
  37. }
  38. // Show OpenID URIs
  39. openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID)
  40. if err != nil {
  41. ctx.ServerError("GetUserOpenIDs", err)
  42. return
  43. }
  44. ctx.Data["OpenIDs"] = openIDs
  45. if len(ctx.ContextUser.Description) != 0 {
  46. content, err := markdown.RenderString(&markup.RenderContext{
  47. Metas: map[string]string{"mode": "document"},
  48. Ctx: ctx,
  49. }, ctx.ContextUser.Description)
  50. if err != nil {
  51. ctx.ServerError("RenderString", err)
  52. return
  53. }
  54. ctx.Data["RenderedDescription"] = content
  55. }
  56. showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
  57. orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{
  58. UserID: ctx.ContextUser.ID,
  59. IncludePrivate: showPrivate,
  60. })
  61. if err != nil {
  62. ctx.ServerError("FindOrgs", err)
  63. return
  64. }
  65. ctx.Data["Orgs"] = orgs
  66. ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(ctx, orgs, ctx.Doer)
  67. badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
  68. if err != nil {
  69. ctx.ServerError("GetUserBadges", err)
  70. return
  71. }
  72. ctx.Data["Badges"] = badges
  73. // in case the numbers are already provided by other functions, no need to query again (which is slow)
  74. if _, ok := ctx.Data["NumFollowers"]; !ok {
  75. _, ctx.Data["NumFollowers"], _ = user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1})
  76. }
  77. if _, ok := ctx.Data["NumFollowing"]; !ok {
  78. _, ctx.Data["NumFollowing"], _ = user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1})
  79. }
  80. if ctx.Doer != nil {
  81. if block, err := user_model.GetBlocking(ctx, ctx.Doer.ID, ctx.ContextUser.ID); err != nil {
  82. ctx.ServerError("GetBlocking", err)
  83. } else {
  84. ctx.Data["UserBlocking"] = block
  85. }
  86. }
  87. }
  88. func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
  89. profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ".profile")
  90. if err == nil {
  91. perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer)
  92. if err == nil && !profileDbRepo.IsEmpty && perm.CanRead(unit.TypeCode) {
  93. if profileGitRepo, err = gitrepo.OpenRepository(ctx, profileDbRepo); err != nil {
  94. log.Error("FindUserProfileReadme failed to OpenRepository: %v", err)
  95. } else {
  96. if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil {
  97. log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err)
  98. } else {
  99. profileReadmeBlob, _ = commit.GetBlobByPath("README.md")
  100. }
  101. }
  102. }
  103. } else if !repo_model.IsErrRepoNotExist(err) {
  104. log.Error("FindUserProfileReadme failed to GetRepositoryByName: %v", err)
  105. }
  106. return profileDbRepo, profileGitRepo, profileReadmeBlob, func() {
  107. if profileGitRepo != nil {
  108. _ = profileGitRepo.Close()
  109. }
  110. }
  111. }
  112. func RenderUserHeader(ctx *context.Context) {
  113. prepareContextForCommonProfile(ctx)
  114. _, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer)
  115. defer profileClose()
  116. ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
  117. }
  118. func LoadHeaderCount(ctx *context.Context) error {
  119. prepareContextForCommonProfile(ctx)
  120. repoCount, err := repo_model.CountRepository(ctx, &repo_model.SearchRepoOptions{
  121. Actor: ctx.Doer,
  122. OwnerID: ctx.ContextUser.ID,
  123. Private: ctx.IsSigned,
  124. Collaborate: optional.Some(false),
  125. IncludeDescription: setting.UI.SearchRepoDescription,
  126. })
  127. if err != nil {
  128. return err
  129. }
  130. ctx.Data["RepoCount"] = repoCount
  131. var projectType project_model.Type
  132. if ctx.ContextUser.IsOrganization() {
  133. projectType = project_model.TypeOrganization
  134. } else {
  135. projectType = project_model.TypeIndividual
  136. }
  137. projectCount, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{
  138. OwnerID: ctx.ContextUser.ID,
  139. IsClosed: optional.Some(false),
  140. Type: projectType,
  141. })
  142. if err != nil {
  143. return err
  144. }
  145. ctx.Data["ProjectCount"] = projectCount
  146. return nil
  147. }