Count only visible repos on profile (#25928)

Fixes #25914
This commit is contained in:
JakobDev 2023-08-11 19:08:05 +02:00 committed by GitHub
parent 7e382a5555
commit f3fbb7c67d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 211 additions and 106 deletions

View File

@ -522,6 +522,11 @@ func SearchRepository(ctx context.Context, opts *SearchRepoOptions) (RepositoryL
return SearchRepositoryByCondition(ctx, opts, cond, true) return SearchRepositoryByCondition(ctx, opts, cond, true)
} }
// CountRepository counts repositories based on search options,
func CountRepository(ctx context.Context, opts *SearchRepoOptions) (int64, error) {
return db.GetEngine(ctx).Where(SearchRepositoryCondition(opts)).Count(new(Repository))
}
// SearchRepositoryByCondition search repositories by condition // SearchRepositoryByCondition search repositories by condition
func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) { func SearchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
sess, count, err := searchRepositoryByCondition(ctx, opts, cond) sess, count, err := searchRepositoryByCondition(ctx, opts, cond)

View File

@ -15,108 +15,11 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestSearchRepository(t *testing.T) { func getTestCases() []struct {
assert.NoError(t, unittest.PrepareTestDatabase()) name string
opts *repo_model.SearchRepoOptions
// test search public repository on explore page count int
repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{ } {
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "repo_12",
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
if assert.Len(t, repos, 1) {
assert.Equal(t, "test_repo_12", repos[0].Name)
}
assert.Equal(t, int64(1), count)
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "test_repo",
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, repos, 2)
// test search private repository on explore page
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "repo_13",
Private: true,
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
if assert.Len(t, repos, 1) {
assert.Equal(t, "test_repo_13", repos[0].Name)
}
assert.Equal(t, int64(1), count)
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "test_repo",
Private: true,
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(3), count)
assert.Len(t, repos, 3)
// Test non existing owner
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
assert.NoError(t, err)
assert.Empty(t, repos)
assert.Equal(t, int64(0), count)
// Test search within description
repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "description_14",
Collaborate: util.OptionalBoolFalse,
IncludeDescription: true,
})
assert.NoError(t, err)
if assert.Len(t, repos, 1) {
assert.Equal(t, "test_repo_14", repos[0].Name)
}
assert.Equal(t, int64(1), count)
// Test NOT search within description
repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "description_14",
Collaborate: util.OptionalBoolFalse,
IncludeDescription: false,
})
assert.NoError(t, err)
assert.Empty(t, repos)
assert.Equal(t, int64(0), count)
testCases := []struct { testCases := []struct {
name string name string
opts *repo_model.SearchRepoOptions opts *repo_model.SearchRepoOptions
@ -274,6 +177,113 @@ func TestSearchRepository(t *testing.T) {
}, },
} }
return testCases
}
func TestSearchRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// test search public repository on explore page
repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "repo_12",
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
if assert.Len(t, repos, 1) {
assert.Equal(t, "test_repo_12", repos[0].Name)
}
assert.Equal(t, int64(1), count)
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "test_repo",
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, repos, 2)
// test search private repository on explore page
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "repo_13",
Private: true,
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
if assert.Len(t, repos, 1) {
assert.Equal(t, "test_repo_13", repos[0].Name)
}
assert.Equal(t, int64(1), count)
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "test_repo",
Private: true,
Collaborate: util.OptionalBoolFalse,
})
assert.NoError(t, err)
assert.Equal(t, int64(3), count)
assert.Len(t, repos, 3)
// Test non existing owner
repos, count, err = repo_model.SearchRepositoryByName(db.DefaultContext, &repo_model.SearchRepoOptions{OwnerID: unittest.NonexistentID})
assert.NoError(t, err)
assert.Empty(t, repos)
assert.Equal(t, int64(0), count)
// Test search within description
repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "description_14",
Collaborate: util.OptionalBoolFalse,
IncludeDescription: true,
})
assert.NoError(t, err)
if assert.Len(t, repos, 1) {
assert.Equal(t, "test_repo_14", repos[0].Name)
}
assert.Equal(t, int64(1), count)
// Test NOT search within description
repos, count, err = repo_model.SearchRepository(db.DefaultContext, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{
Page: 1,
PageSize: 10,
},
Keyword: "description_14",
Collaborate: util.OptionalBoolFalse,
IncludeDescription: false,
})
assert.NoError(t, err)
assert.Empty(t, repos)
assert.Equal(t, int64(0), count)
testCases := getTestCases()
for _, testCase := range testCases { for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) { t.Run(testCase.name, func(t *testing.T) {
repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts) repos, count, err := repo_model.SearchRepositoryByName(db.DefaultContext, testCase.opts)
@ -349,6 +359,21 @@ func TestSearchRepository(t *testing.T) {
} }
} }
func TestCountRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testCases := getTestCases()
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
count, err := repo_model.CountRepository(db.DefaultContext, testCase.opts)
assert.NoError(t, err)
assert.Equal(t, int64(testCase.count), count)
})
}
}
func TestSearchRepositoryByTopicName(t *testing.T) { func TestSearchRepositoryByTopicName(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
) )
const ( const (
@ -158,6 +159,12 @@ func Home(ctx *context.Context) {
ctx.Data["PageIsViewRepositories"] = true ctx.Data["PageIsViewRepositories"] = true
ctx.Data["IsFollowing"] = isFollowing ctx.Data["IsFollowing"] = isFollowing
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5) pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "language", "Language") pager.AddParam(ctx, "language", "Language")

View File

@ -101,6 +101,12 @@ func Projects(ctx *context.Context) {
project.RenderedContent = project.Description project.RenderedContent = project.Description
} }
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
numPages := 0 numPages := 0
if total > 0 { if total > 0 {
numPages = (int(total) - 1/setting.UI.IssuePagingNum) numPages = (int(total) - 1/setting.UI.IssuePagingNum)
@ -135,6 +141,13 @@ func RenderNewProject(ctx *context.Context) {
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink() ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
ctx.Data["CancelLink"] = ctx.ContextUser.HomeLink() + "/-/projects" ctx.Data["CancelLink"] = ctx.ContextUser.HomeLink() + "/-/projects"
shared_user.RenderUserHeader(ctx) shared_user.RenderUserHeader(ctx)
err := shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
ctx.HTML(http.StatusOK, tplProjectsNew) ctx.HTML(http.StatusOK, tplProjectsNew)
} }
@ -270,6 +283,12 @@ func EditProjectPost(ctx *context.Context) {
shared_user.RenderUserHeader(ctx) shared_user.RenderUserHeader(ctx)
err := shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(http.StatusOK, tplProjectsNew) ctx.HTML(http.StatusOK, tplProjectsNew)
return return
@ -379,6 +398,12 @@ func ViewProject(ctx *context.Context) {
ctx.Data["Boards"] = boards ctx.Data["Boards"] = boards
shared_user.RenderUserHeader(ctx) shared_user.RenderUserHeader(ctx)
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
ctx.HTML(http.StatusOK, tplProjectsView) ctx.HTML(http.StatusOK, tplProjectsView)
} }

View File

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
) )
// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu) // prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu)
@ -110,3 +111,21 @@ func RenderUserHeader(ctx *context.Context) {
defer profileClose() defer profileClose()
ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
} }
func LoadHeaderCount(ctx *context.Context) error {
prepareContextForCommonProfile(ctx)
repoCount, err := repo_model.CountRepository(ctx, &repo_model.SearchRepoOptions{
Actor: ctx.Doer,
OwnerID: ctx.ContextUser.ID,
Private: ctx.IsSigned,
Collaborate: util.OptionalBoolFalse,
IncludeDescription: setting.UI.SearchRepoDescription,
})
if err != nil {
return err
}
ctx.Data["RepoCount"] = repoCount
return nil
}

View File

@ -101,6 +101,12 @@ func ListPackages(ctx *context.Context) {
ctx.Data["Total"] = total ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = repositoryAccessMap ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
// TODO: context/org -> HandleOrgAssignment() can not be used // TODO: context/org -> HandleOrgAssignment() can not be used
if ctx.ContextUser.IsOrganization() { if ctx.ContextUser.IsOrganization() {
org := org_model.OrgFromUser(ctx.ContextUser) org := org_model.OrgFromUser(ctx.ContextUser)
@ -255,6 +261,12 @@ func ViewPackageVersion(ctx *context.Context) {
} }
ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess ctx.Data["HasRepositoryAccess"] = hasRepositoryAccess
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
ctx.HTML(http.StatusOK, tplPackagesView) ctx.HTML(http.StatusOK, tplPackagesView)
} }
@ -346,6 +358,12 @@ func ListPackageVersions(ctx *context.Context) {
ctx.Data["Total"] = total ctx.Data["Total"] = total
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
for k, v := range pagerParams { for k, v := range pagerParams {
pager.AddParamString(k, v) pager.AddParamString(k, v)

View File

@ -269,6 +269,12 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGi
ctx.Data["Repos"] = repos ctx.Data["Repos"] = repos
ctx.Data["Total"] = total ctx.Data["Total"] = total
err = shared_user.LoadHeaderCount(ctx)
if err != nil {
ctx.ServerError("LoadHeaderCount", err)
return
}
pager := context.NewPagination(total, pagingNum, page, 5) pager := context.NewPagination(total, pagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "tab", "TabName") pager.AddParam(ctx, "tab", "TabName")

View File

@ -2,8 +2,8 @@
<div class="ui secondary stackable pointing menu"> <div class="ui secondary stackable pointing menu">
<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}"> <a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}">
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
{{if .ContextUser.NumRepos}} {{if .RepoCount}}
<div class="ui small label">{{.ContextUser.NumRepos}}</div> <div class="ui small label">{{.RepoCount}}</div>
{{end}} {{end}}
</a> </a>
{{if .CanReadProjects}} {{if .CanReadProjects}}

View File

@ -6,8 +6,8 @@
{{end}} {{end}}
<a class="{{if eq .TabName "repositories"}}active {{end}} item" href="{{.ContextUser.HomeLink}}?tab=repositories"> <a class="{{if eq .TabName "repositories"}}active {{end}} item" href="{{.ContextUser.HomeLink}}?tab=repositories">
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
{{if .ContextUser.NumRepos}} {{if .RepoCount}}
<div class="ui small label">{{.ContextUser.NumRepos}}</div> <div class="ui small label">{{.RepoCount}}</div>
{{end}} {{end}}
</a> </a>
{{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}} {{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}}