Files
gitea/routers/web/shared/user/header.go
wxiaoguang 9dc04289aa refactor: make git package handle all git operations (#38543)
before: gitrepo vs git packages
after: git package fully handle all git operations

by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide
path details.

benefits:

1. remove all unnecessary wrappers, developers no need to struggle with
"which package should be used"
2. simplify code, RepositoryFacade can (will) be used everywhere, all
"path" details are (will be) hidden
2026-07-20 16:07:38 +00:00

196 lines
6.7 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package user
import (
"errors"
"net/url"
"gitea.dev/models/db"
"gitea.dev/models/organization"
access_model "gitea.dev/models/perm/access"
project_model "gitea.dev/models/project"
repo_model "gitea.dev/models/repo"
"gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/git"
"gitea.dev/modules/log"
"gitea.dev/modules/markup"
"gitea.dev/modules/markup/markdown"
"gitea.dev/modules/optional"
"gitea.dev/modules/setting"
"gitea.dev/modules/util"
"gitea.dev/services/context"
)
// prepareContextForProfileBigAvatar set the context for big avatar view on the profile page
func prepareContextForProfileBigAvatar(ctx *context.Context) {
ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
if setting.Service.UserLocationMapURL != "" {
ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location)
}
// Show OpenID URIs
openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID)
if err != nil {
ctx.ServerError("GetUserOpenIDs", err)
return
}
ctx.Data["OpenIDs"] = openIDs
if len(ctx.ContextUser.Description) != 0 {
content, err := markdown.RenderString(markup.NewRenderContext(ctx).WithMetas(markup.ComposeSimpleDocumentMetas()), ctx.ContextUser.Description)
if err != nil {
ctx.ServerError("RenderString", err)
return
}
ctx.Data["RenderedDescription"] = content
}
orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{
UserID: ctx.ContextUser.ID,
IncludeVisibility: organization.DoerViewOtherVisibility(ctx.Doer, ctx.ContextUser),
ListOptions: db.ListOptions{
Page: 1,
// query one more result (without a separate counting) to see whether we need to add the "show more orgs" link
PageSize: setting.UI.User.OrgPagingNum + 1,
},
})
if err != nil {
ctx.ServerError("FindOrgs", err)
return
}
if len(orgs) > setting.UI.User.OrgPagingNum {
orgs = orgs[:setting.UI.User.OrgPagingNum]
ctx.Data["ShowMoreOrgs"] = true
}
ctx.Data["Orgs"] = orgs
ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(ctx, orgs, ctx.Doer)
badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
if err != nil {
ctx.ServerError("GetUserBadges", err)
return
}
ctx.Data["Badges"] = badges
// in case the numbers are already provided by other functions, no need to query again (which is slow)
if _, ok := ctx.Data["NumFollowers"]; !ok {
_, ctx.Data["NumFollowers"], _ = user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1})
}
if _, ok := ctx.Data["NumFollowing"]; !ok {
_, ctx.Data["NumFollowing"], _ = user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1})
}
if ctx.Doer != nil {
ctx.Data["UserBlocking"], err = user_model.GetBlocking(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
if err != nil && !errors.Is(err, util.ErrNotExist) {
ctx.ServerError("GetBlocking", err)
}
}
}
func FindOwnerProfileReadme(ctx *context.Context, doer *user_model.User, optProfileRepoName ...string) (profileDbRepo *repo_model.Repository, profileReadmeBlob *git.Blob) {
profileRepoName := util.OptionalArg(optProfileRepoName, RepoNameProfile)
profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, profileRepoName)
if err != nil {
if !repo_model.IsErrRepoNotExist(err) {
log.Error("FindOwnerProfileReadme failed to GetRepositoryByName: %v", err)
}
return nil, nil
}
perm, err := access_model.GetDoerRepoPermission(ctx, profileDbRepo, doer)
if err != nil {
log.Error("FindOwnerProfileReadme failed to GetRepositoryByName: %v", err)
return nil, nil
}
if profileDbRepo.IsEmpty || !perm.CanRead(unit.TypeCode) {
return nil, nil
}
profileGitRepo, err := git.RepositoryFromRequestContextOrOpen(ctx, profileDbRepo)
if err != nil {
log.Error("FindOwnerProfileReadme failed to OpenRepository: %v", err)
return nil, nil
}
commit, err := profileGitRepo.GetBranchCommit(ctx, profileDbRepo.DefaultBranch)
if err != nil {
log.Error("FindOwnerProfileReadme failed to GetBranchCommit: %v", err)
return nil, nil
}
profileReadmeBlob, _ = commit.GetBlobByPath(ctx, profileGitRepo, "README.md") // no need to handle this error
return profileDbRepo, profileReadmeBlob
}
type PrepareOwnerHeaderResult struct {
ProfilePublicRepo *repo_model.Repository
ProfilePublicReadmeBlob *git.Blob
ProfilePrivateRepo *repo_model.Repository
ProfilePrivateReadmeBlob *git.Blob
HasOrgProfileReadme bool
}
const (
RepoNameProfilePrivate = ".profile-private"
RepoNameProfile = ".profile"
)
func RenderUserOrgHeader(ctx *context.Context) (result *PrepareOwnerHeaderResult, err error) {
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink()
if err := loadHeaderCount(ctx); err != nil {
return nil, err
}
result = &PrepareOwnerHeaderResult{}
if ctx.ContextUser.IsOrganization() {
result.ProfilePublicRepo, result.ProfilePublicReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer)
result.ProfilePrivateRepo, result.ProfilePrivateReadmeBlob = FindOwnerProfileReadme(ctx, ctx.Doer, RepoNameProfilePrivate)
result.HasOrgProfileReadme = result.ProfilePublicReadmeBlob != nil || result.ProfilePrivateReadmeBlob != nil
ctx.Data["HasOrgProfileReadme"] = result.HasOrgProfileReadme // many pages need it to show the "overview" tab
} else {
_, profileReadmeBlob := FindOwnerProfileReadme(ctx, ctx.Doer)
ctx.Data["HasUserProfileReadme"] = profileReadmeBlob != nil
prepareContextForProfileBigAvatar(ctx)
}
return result, nil
}
func loadHeaderCount(ctx *context.Context) error {
repoCount, err := repo_model.CountRepository(ctx, repo_model.SearchRepoOptions{
Actor: ctx.Doer,
OwnerID: ctx.ContextUser.ID,
Private: ctx.IsSigned,
Collaborate: optional.Some(false),
IncludeDescription: setting.UI.SearchRepoDescription,
})
if err != nil {
return err
}
ctx.Data["RepoCount"] = repoCount
var projectType project_model.Type
if ctx.ContextUser.IsOrganization() {
projectType = project_model.TypeOrganization
} else {
projectType = project_model.TypeIndividual
}
projectCount, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{
OwnerID: ctx.ContextUser.ID,
IsClosed: optional.Some(false),
Type: projectType,
})
if err != nil {
return err
}
ctx.Data["ProjectCount"] = projectCount
return nil
}