1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-03 09:07:19 +00:00

[Feature] Private README.md for organization (#32872)

Implemented #29503

---------

Co-authored-by: Ben Chang <ben_chang@htc.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Chai-Shi
2024-12-31 12:22:09 +08:00
committed by GitHub
parent c09656e0e0
commit 0387195abb
27 changed files with 484 additions and 149 deletions

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
@ -21,9 +22,7 @@ import (
"code.gitea.io/gitea/services/context"
)
const (
tplOrgHome templates.TplName = "org/home"
)
const tplOrgHome templates.TplName = "org/home"
// Home show organization home page
func Home(ctx *context.Context) {
@ -110,15 +109,19 @@ func home(ctx *context.Context, viewRepositories bool) {
ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull
ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0
if !prepareOrgProfileReadme(ctx, viewRepositories) {
ctx.Data["PageIsViewRepositories"] = true
prepareResult, err := shared_user.PrepareOrgHeader(ctx)
if err != nil {
ctx.ServerError("PrepareOrgHeader", err)
return
}
var (
repos []*repo_model.Repository
count int64
)
repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
// if no profile readme, it still means "view repositories"
isViewOverview := !viewRepositories && prepareOrgProfileReadme(ctx, prepareResult)
ctx.Data["PageIsViewRepositories"] = !isViewOverview
ctx.Data["PageIsViewOverview"] = isViewOverview
ctx.Data["ShowOrgProfileReadmeSelector"] = isViewOverview && prepareResult.ProfilePublicReadmeBlob != nil && prepareResult.ProfilePrivateReadmeBlob != nil
repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
@ -151,28 +154,45 @@ func home(ctx *context.Context, viewRepositories bool) {
ctx.HTML(http.StatusOK, tplOrgHome)
}
func prepareOrgProfileReadme(ctx *context.Context, viewRepositories bool) bool {
profileDbRepo, profileGitRepo, profileReadme, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
ctx.Data["HasProfileReadme"] = profileReadme != nil
func prepareOrgProfileReadme(ctx *context.Context, prepareResult *shared_user.PrepareOrgHeaderResult) bool {
viewAs := ctx.FormString("view_as", util.Iif(ctx.Org.IsMember, "member", "public"))
viewAsMember := viewAs == "member"
if profileGitRepo == nil || profileReadme == nil || viewRepositories {
var profileRepo *repo_model.Repository
var readmeBlob *git.Blob
if viewAsMember {
if prepareResult.ProfilePrivateReadmeBlob != nil {
profileRepo, readmeBlob = prepareResult.ProfilePrivateRepo, prepareResult.ProfilePrivateReadmeBlob
} else {
profileRepo, readmeBlob = prepareResult.ProfilePublicRepo, prepareResult.ProfilePublicReadmeBlob
viewAsMember = false
}
} else {
if prepareResult.ProfilePublicReadmeBlob != nil {
profileRepo, readmeBlob = prepareResult.ProfilePublicRepo, prepareResult.ProfilePublicReadmeBlob
} else {
profileRepo, readmeBlob = prepareResult.ProfilePrivateRepo, prepareResult.ProfilePrivateReadmeBlob
viewAsMember = true
}
}
if readmeBlob == nil {
return false
}
if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
log.Error("failed to GetBlobContent: %v", err)
} else {
rctx := renderhelper.NewRenderContextRepoFile(ctx, profileDbRepo, renderhelper.RepoFileOptions{
CurrentRefPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
})
if profileContent, err := markdown.RenderString(rctx, bytes); err != nil {
log.Error("failed to RenderString: %v", err)
} else {
ctx.Data["ProfileReadme"] = profileContent
}
readmeBytes, err := readmeBlob.GetBlobContent(setting.UI.MaxDisplayFileSize)
if err != nil {
log.Error("failed to GetBlobContent for profile %q (view as %q) readme: %v", profileRepo.FullName(), viewAs, err)
return false
}
ctx.Data["PageIsViewOverview"] = true
rctx := renderhelper.NewRenderContextRepoFile(ctx, profileRepo, renderhelper.RepoFileOptions{
CurrentRefPath: path.Join("branch", util.PathEscapeSegments(profileRepo.DefaultBranch)),
})
ctx.Data["ProfileReadmeContent"], err = markdown.RenderString(rctx, readmeBytes)
if err != nil {
log.Error("failed to GetBlobContent for profile %q (view as %q) readme: %v", profileRepo.FullName(), viewAs, err)
return false
}
ctx.Data["IsViewingOrgAsMember"] = viewAsMember
return true
}

View File

@ -54,9 +54,9 @@ func Members(ctx *context.Context) {
return
}
err = shared_user.RenderOrgHeader(ctx)
_, err = shared_user.PrepareOrgHeader(ctx)
if err != nil {
ctx.ServerError("RenderOrgHeader", err)
ctx.ServerError("PrepareOrgHeader", err)
return
}

View File

@ -58,9 +58,9 @@ func Teams(ctx *context.Context) {
}
ctx.Data["Teams"] = ctx.Org.Teams
err := shared_user.RenderOrgHeader(ctx)
_, err := shared_user.PrepareOrgHeader(ctx)
if err != nil {
ctx.ServerError("RenderOrgHeader", err)
ctx.ServerError("PrepareOrgHeader", err)
return
}

View File

@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context"
)
@ -102,37 +103,46 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
}
}
func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ".profile")
if err == nil {
perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer)
if err == nil && !profileDbRepo.IsEmpty && perm.CanRead(unit.TypeCode) {
if profileGitRepo, err = gitrepo.OpenRepository(ctx, profileDbRepo); err != nil {
log.Error("FindUserProfileReadme failed to OpenRepository: %v", err)
} else {
if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil {
log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err)
} else {
profileReadmeBlob, _ = commit.GetBlobByPath("README.md")
}
}
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)
}
} else if !repo_model.IsErrRepoNotExist(err) {
log.Error("FindUserProfileReadme failed to GetRepositoryByName: %v", err)
return nil, nil
}
return profileDbRepo, profileGitRepo, profileReadmeBlob, func() {
if profileGitRepo != nil {
_ = profileGitRepo.Close()
}
perm, err := access_model.GetUserRepoPermission(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 := gitrepo.RepositoryFromRequestContextOrOpen(ctx, profileDbRepo)
if err != nil {
log.Error("FindOwnerProfileReadme failed to OpenRepository: %v", err)
return nil, nil
}
commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch)
if err != nil {
log.Error("FindOwnerProfileReadme failed to GetBranchCommit: %v", err)
return nil, nil
}
profileReadmeBlob, _ = commit.GetBlobByPath("README.md") // no need to handle this error
return profileDbRepo, profileReadmeBlob
}
func RenderUserHeader(ctx *context.Context) {
prepareContextForCommonProfile(ctx)
_, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
_, profileReadmeBlob := FindOwnerProfileReadme(ctx, ctx.Doer)
ctx.Data["HasUserProfileReadme"] = profileReadmeBlob != nil
}
func LoadHeaderCount(ctx *context.Context) error {
@ -169,14 +179,28 @@ func LoadHeaderCount(ctx *context.Context) error {
return nil
}
func RenderOrgHeader(ctx *context.Context) error {
if err := LoadHeaderCount(ctx); err != nil {
return err
const (
RepoNameProfilePrivate = ".profile-private"
RepoNameProfile = ".profile"
)
type PrepareOrgHeaderResult struct {
ProfilePublicRepo *repo_model.Repository
ProfilePublicReadmeBlob *git.Blob
ProfilePrivateRepo *repo_model.Repository
ProfilePrivateReadmeBlob *git.Blob
HasOrgProfileReadme bool
}
func PrepareOrgHeader(ctx *context.Context) (result *PrepareOrgHeaderResult, err error) {
if err = LoadHeaderCount(ctx); err != nil {
return nil, err
}
_, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
return nil
result = &PrepareOrgHeaderResult{}
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
return result, nil
}

View File

@ -74,8 +74,7 @@ func userProfile(ctx *context.Context) {
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
}
profileDbRepo, _ /*profileGitRepo*/, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
defer profileClose()
profileDbRepo, profileReadmeBlob := shared_user.FindOwnerProfileReadme(ctx, ctx.Doer)
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileReadmeBlob)
@ -96,7 +95,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
}
}
ctx.Data["TabName"] = tab
ctx.Data["HasProfileReadme"] = profileReadme != nil
ctx.Data["HasUserProfileReadme"] = profileReadme != nil
page := ctx.FormInt("page")
if page <= 0 {
@ -254,7 +253,7 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
if profileContent, err := markdown.RenderString(rctx, bytes); err != nil {
log.Error("failed to RenderString: %v", err)
} else {
ctx.Data["ProfileReadme"] = profileContent
ctx.Data["ProfileReadmeContent"] = profileContent
}
}
case "organizations":