mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	feat(repo): support search repository by topic name (#4505)
* feat(repo): support search repository by topic name
This commit is contained in:
		| @@ -9,10 +9,10 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	api "code.gitea.io/sdk/gitea" | 	api "code.gitea.io/sdk/gitea" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { | func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { | ||||||
|   | |||||||
| @@ -5,13 +5,13 @@ | |||||||
| package integrations | package integrations | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	api "code.gitea.io/sdk/gitea" | 	api "code.gitea.io/sdk/gitea" | ||||||
|  |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,9 +67,9 @@ func TestAPISearchRepo(t *testing.T) { | |||||||
| 		expectedResults | 		expectedResults | ||||||
| 	}{ | 	}{ | ||||||
| 		{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ | 		{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ | ||||||
| 			nil:   {count: 17}, | 			nil:   {count: 19}, | ||||||
| 			user:  {count: 17}, | 			user:  {count: 19}, | ||||||
| 			user2: {count: 17}}, | 			user2: {count: 19}}, | ||||||
| 		}, | 		}, | ||||||
| 		{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ | 		{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ | ||||||
| 			nil:   {count: 10}, | 			nil:   {count: 10}, | ||||||
|   | |||||||
| @@ -9,3 +9,11 @@ | |||||||
| - | - | ||||||
|   repo_id: 1 |   repo_id: 1 | ||||||
|   topic_id: 3 |   topic_id: 3 | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   repo_id: 33 | ||||||
|  |   topic_id: 1 | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   repo_id: 33 | ||||||
|  |   topic_id: 4 | ||||||
|   | |||||||
| @@ -407,3 +407,25 @@ | |||||||
|   lower_name: utf8 |   lower_name: utf8 | ||||||
|   name: utf8 |   name: utf8 | ||||||
|   is_private: false |   is_private: false | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   id: 34 | ||||||
|  |   owner_id: 21 | ||||||
|  |   lower_name: golang | ||||||
|  |   name: golang | ||||||
|  |   is_private: false | ||||||
|  |   num_stars: 0 | ||||||
|  |   num_forks: 0 | ||||||
|  |   num_issues: 0 | ||||||
|  |   is_mirror: false | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   id: 35 | ||||||
|  |   owner_id: 21 | ||||||
|  |   lower_name: graphql | ||||||
|  |   name: graphql | ||||||
|  |   is_private: false | ||||||
|  |   num_stars: 0 | ||||||
|  |   num_forks: 0 | ||||||
|  |   num_issues: 0 | ||||||
|  |   is_mirror: false | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| - | - | ||||||
|   id: 1 |   id: 1 | ||||||
|   name: golang |   name: golang | ||||||
|   repo_count: 1 |   repo_count: 2 | ||||||
|  |  | ||||||
| - | - | ||||||
|   id: 2 |   id: 2 | ||||||
| @@ -11,3 +11,7 @@ | |||||||
| - id: 3 | - id: 3 | ||||||
|   name: SQL |   name: SQL | ||||||
|   repo_count: 1 |   repo_count: 1 | ||||||
|  |  | ||||||
|  | - id: 4 | ||||||
|  |   name: graphql | ||||||
|  |   repo_count: 1 | ||||||
|   | |||||||
| @@ -314,3 +314,18 @@ | |||||||
|   avatar_email: user20@example.com |   avatar_email: user20@example.com | ||||||
|   num_repos: 4 |   num_repos: 4 | ||||||
|   is_active: true |   is_active: true | ||||||
|  |  | ||||||
|  | - | ||||||
|  |   id: 21 | ||||||
|  |   lower_name: user21 | ||||||
|  |   name: user21 | ||||||
|  |   full_name: User 21 | ||||||
|  |   email: user21@example.com | ||||||
|  |   passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password | ||||||
|  |   type: 0 # individual | ||||||
|  |   salt: ZogKvWdyEx | ||||||
|  |   is_admin: false | ||||||
|  |   avatar: avatar21 | ||||||
|  |   avatar_email: user21@example.com | ||||||
|  |   num_repos: 2 | ||||||
|  |   is_active: true | ||||||
|   | |||||||
| @@ -131,6 +131,8 @@ type SearchRepoOptions struct { | |||||||
| 	// True -> include just mirrors | 	// True -> include just mirrors | ||||||
| 	// False -> include just non-mirrors | 	// False -> include just non-mirrors | ||||||
| 	Mirror util.OptionalBool | 	Mirror util.OptionalBool | ||||||
|  | 	// only search topic name | ||||||
|  | 	TopicOnly bool | ||||||
| } | } | ||||||
|  |  | ||||||
| //SearchOrderBy is used to sort the result | //SearchOrderBy is used to sort the result | ||||||
| @@ -184,7 +186,7 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | |||||||
|  |  | ||||||
| 			if opts.Collaborate != util.OptionalBoolFalse { | 			if opts.Collaborate != util.OptionalBoolFalse { | ||||||
| 				collaborateCond := builder.And( | 				collaborateCond := builder.And( | ||||||
| 					builder.Expr("id IN (SELECT repo_id FROM `access` WHERE access.user_id = ?)", opts.OwnerID), | 					builder.Expr("repository.id IN (SELECT repo_id FROM `access` WHERE access.user_id = ?)", opts.OwnerID), | ||||||
| 					builder.Neq{"owner_id": opts.OwnerID}) | 					builder.Neq{"owner_id": opts.OwnerID}) | ||||||
| 				if !opts.Private { | 				if !opts.Private { | ||||||
| 					collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false)) | 					collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false)) | ||||||
| @@ -202,7 +204,14 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if opts.Keyword != "" { | 	if opts.Keyword != "" { | ||||||
| 		cond = cond.And(builder.Like{"lower_name", strings.ToLower(opts.Keyword)}) | 		var keywordCond = builder.NewCond() | ||||||
|  | 		if opts.TopicOnly { | ||||||
|  | 			keywordCond = keywordCond.Or(builder.Like{"topic.name", strings.ToLower(opts.Keyword)}) | ||||||
|  | 		} else { | ||||||
|  | 			keywordCond = keywordCond.Or(builder.Like{"lower_name", strings.ToLower(opts.Keyword)}) | ||||||
|  | 			keywordCond = keywordCond.Or(builder.Like{"topic.name", strings.ToLower(opts.Keyword)}) | ||||||
|  | 		} | ||||||
|  | 		cond = cond.And(keywordCond) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if opts.Fork != util.OptionalBoolNone { | 	if opts.Fork != util.OptionalBoolNone { | ||||||
| @@ -224,9 +233,15 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | |||||||
| 		sess.Join("INNER", "star", "star.repo_id = repository.id") | 		sess.Join("INNER", "star", "star.repo_id = repository.id") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if opts.Keyword != "" { | ||||||
|  | 		sess.Join("LEFT", "repo_topic", "repo_topic.repo_id = repository.id") | ||||||
|  | 		sess.Join("LEFT", "topic", "repo_topic.topic_id = topic.id") | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	count, err := sess. | 	count, err := sess. | ||||||
| 		Where(cond). | 		Where(cond). | ||||||
| 		Count(new(Repository)) | 		Count(new(Repository)) | ||||||
|  |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, 0, fmt.Errorf("Count: %v", err) | 		return nil, 0, fmt.Errorf("Count: %v", err) | ||||||
| 	} | 	} | ||||||
| @@ -236,11 +251,23 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | |||||||
| 		sess.Join("INNER", "star", "star.repo_id = repository.id") | 		sess.Join("INNER", "star", "star.repo_id = repository.id") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if opts.Keyword != "" { | ||||||
|  | 		sess.Join("LEFT", "repo_topic", "repo_topic.repo_id = repository.id") | ||||||
|  | 		sess.Join("LEFT", "topic", "repo_topic.topic_id = topic.id") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opts.Keyword != "" { | ||||||
|  | 		sess.Select("repository.*") | ||||||
|  | 		sess.GroupBy("repository.id") | ||||||
|  | 		sess.OrderBy("repository." + opts.OrderBy.String()) | ||||||
|  | 	} else { | ||||||
|  | 		sess.OrderBy(opts.OrderBy.String()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	repos := make(RepositoryList, 0, opts.PageSize) | 	repos := make(RepositoryList, 0, opts.PageSize) | ||||||
| 	if err = sess. | 	if err = sess. | ||||||
| 		Where(cond). | 		Where(cond). | ||||||
| 		Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). | 		Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). | ||||||
| 		OrderBy(opts.OrderBy.String()). |  | ||||||
| 		Find(&repos); err != nil { | 		Find(&repos); err != nil { | ||||||
| 		return nil, 0, fmt.Errorf("Repo: %v", err) | 		return nil, 0, fmt.Errorf("Repo: %v", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -147,10 +147,10 @@ func TestSearchRepositoryByName(t *testing.T) { | |||||||
| 			count: 14}, | 			count: 14}, | ||||||
| 		{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", | 		{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", | ||||||
| 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, | 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, | ||||||
| 			count: 17}, | 			count: 19}, | ||||||
| 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", | 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", | ||||||
| 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | ||||||
| 			count: 21}, | 			count: 23}, | ||||||
| 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", | 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", | ||||||
| 			opts:  &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | 			opts:  &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | ||||||
| 			count: 13}, | 			count: 13}, | ||||||
| @@ -159,7 +159,7 @@ func TestSearchRepositoryByName(t *testing.T) { | |||||||
| 			count: 11}, | 			count: 11}, | ||||||
| 		{name: "AllPublic/PublicRepositoriesOfOrganization", | 		{name: "AllPublic/PublicRepositoriesOfOrganization", | ||||||
| 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse}, | 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse}, | ||||||
| 			count: 17}, | 			count: 19}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, testCase := range testCases { | 	for _, testCase := range testCases { | ||||||
| @@ -222,3 +222,28 @@ func TestSearchRepositoryByName(t *testing.T) { | |||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestSearchRepositoryByTopicName(t *testing.T) { | ||||||
|  | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		name  string | ||||||
|  | 		opts  *SearchRepoOptions | ||||||
|  | 		count int | ||||||
|  | 	}{ | ||||||
|  | 		{name: "AllPublic/SearchPublicRepositoriesFromTopicAndName", | ||||||
|  | 			opts:  &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql"}, | ||||||
|  | 			count: 2}, | ||||||
|  | 		{name: "AllPublic/OnlySearchPublicRepositoriesFromTopic", | ||||||
|  | 			opts:  &SearchRepoOptions{OwnerID: 21, AllPublic: true, Keyword: "graphql", TopicOnly: true}, | ||||||
|  | 			count: 1}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for _, testCase := range testCases { | ||||||
|  | 		t.Run(testCase.name, func(t *testing.T) { | ||||||
|  | 			_, count, err := SearchRepositoryByName(testCase.opts) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			assert.Equal(t, int64(testCase.count), count) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -18,14 +18,14 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/Unknwon/com" |  | ||||||
| 	"github.com/go-xorm/xorm" |  | ||||||
| 	"golang.org/x/crypto/ssh" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/process" | 	"code.gitea.io/gitea/modules/process" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
|  |  | ||||||
|  | 	"github.com/Unknwon/com" | ||||||
|  | 	"github.com/go-xorm/xorm" | ||||||
|  | 	"golang.org/x/crypto/ssh" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
|   | |||||||
| @@ -15,7 +15,7 @@ func TestAddTopic(t *testing.T) { | |||||||
|  |  | ||||||
| 	topics, err := FindTopics(&FindTopicOptions{}) | 	topics, err := FindTopics(&FindTopicOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 3, len(topics)) | 	assert.EqualValues(t, 4, len(topics)) | ||||||
|  |  | ||||||
| 	topics, err = FindTopics(&FindTopicOptions{ | 	topics, err = FindTopics(&FindTopicOptions{ | ||||||
| 		Limit: 2, | 		Limit: 2, | ||||||
| @@ -32,7 +32,7 @@ func TestAddTopic(t *testing.T) { | |||||||
| 	assert.NoError(t, SaveTopics(2, "golang")) | 	assert.NoError(t, SaveTopics(2, "golang")) | ||||||
| 	topics, err = FindTopics(&FindTopicOptions{}) | 	topics, err = FindTopics(&FindTopicOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 3, len(topics)) | 	assert.EqualValues(t, 4, len(topics)) | ||||||
|  |  | ||||||
| 	topics, err = FindTopics(&FindTopicOptions{ | 	topics, err = FindTopics(&FindTopicOptions{ | ||||||
| 		RepoID: 2, | 		RepoID: 2, | ||||||
| @@ -47,7 +47,7 @@ func TestAddTopic(t *testing.T) { | |||||||
|  |  | ||||||
| 	topics, err = FindTopics(&FindTopicOptions{}) | 	topics, err = FindTopics(&FindTopicOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 4, len(topics)) | 	assert.EqualValues(t, 5, len(topics)) | ||||||
|  |  | ||||||
| 	topics, err = FindTopics(&FindTopicOptions{ | 	topics, err = FindTopics(&FindTopicOptions{ | ||||||
| 		RepoID: 2, | 		RepoID: 2, | ||||||
|   | |||||||
| @@ -77,13 +77,13 @@ func TestSearchUsers(t *testing.T) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1}, | 	testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1}, | ||||||
| 		[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20}) | 		[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21}) | ||||||
|  |  | ||||||
| 	testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse}, | 	testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse}, | ||||||
| 		[]int64{9}) | 		[]int64{9}) | ||||||
|  |  | ||||||
| 	testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, | 	testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, | ||||||
| 		[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20}) | 		[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21}) | ||||||
|  |  | ||||||
| 	testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, | 	testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, | ||||||
| 		[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) | 		[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) | ||||||
|   | |||||||
| @@ -91,6 +91,7 @@ func Search(ctx *context.APIContext) { | |||||||
| 		OwnerID:     ctx.QueryInt64("uid"), | 		OwnerID:     ctx.QueryInt64("uid"), | ||||||
| 		Page:        ctx.QueryInt("page"), | 		Page:        ctx.QueryInt("page"), | ||||||
| 		PageSize:    convert.ToCorrectPageSize(ctx.QueryInt("limit")), | 		PageSize:    convert.ToCorrectPageSize(ctx.QueryInt("limit")), | ||||||
|  | 		TopicOnly:   ctx.QueryBool("topic"), | ||||||
| 		Collaborate: util.OptionalBoolNone, | 		Collaborate: util.OptionalBoolNone, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -122,6 +122,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	keyword := strings.Trim(ctx.Query("q"), " ") | 	keyword := strings.Trim(ctx.Query("q"), " ") | ||||||
|  | 	topicOnly := ctx.QueryBool("topic") | ||||||
|  |  | ||||||
| 	repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | 	repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | ||||||
| 		Page:      page, | 		Page:      page, | ||||||
| @@ -131,6 +132,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { | |||||||
| 		Keyword:   keyword, | 		Keyword:   keyword, | ||||||
| 		OwnerID:   opts.OwnerID, | 		OwnerID:   opts.OwnerID, | ||||||
| 		AllPublic: true, | 		AllPublic: true, | ||||||
|  | 		TopicOnly: topicOnly, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("SearchRepositoryByName", err) | 		ctx.ServerError("SearchRepositoryByName", err) | ||||||
|   | |||||||
| @@ -105,6 +105,8 @@ func Profile(ctx *context.Context) { | |||||||
| 		page = 1 | 		page = 1 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	topicOnly := ctx.QueryBool("topic") | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		repos   []*models.Repository | 		repos   []*models.Repository | ||||||
| 		count   int64 | 		count   int64 | ||||||
| @@ -174,6 +176,7 @@ func Profile(ctx *context.Context) { | |||||||
| 				PageSize:    setting.UI.User.RepoPagingNum, | 				PageSize:    setting.UI.User.RepoPagingNum, | ||||||
| 				Starred:     true, | 				Starred:     true, | ||||||
| 				Collaborate: util.OptionalBoolFalse, | 				Collaborate: util.OptionalBoolFalse, | ||||||
|  | 				TopicOnly:   topicOnly, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				ctx.ServerError("SearchRepositoryByName", err) | 				ctx.ServerError("SearchRepositoryByName", err) | ||||||
| @@ -217,6 +220,7 @@ func Profile(ctx *context.Context) { | |||||||
| 				IsProfile:   true, | 				IsProfile:   true, | ||||||
| 				PageSize:    setting.UI.User.RepoPagingNum, | 				PageSize:    setting.UI.User.RepoPagingNum, | ||||||
| 				Collaborate: util.OptionalBoolFalse, | 				Collaborate: util.OptionalBoolFalse, | ||||||
|  | 				TopicOnly:   topicOnly, | ||||||
| 			}) | 			}) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				ctx.ServerError("SearchRepositoryByName", err) | 				ctx.ServerError("SearchRepositoryByName", err) | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ | |||||||
| 			{{if .Topics }} | 			{{if .Topics }} | ||||||
| 				<div> | 				<div> | ||||||
| 				{{range .Topics}} | 				{{range .Topics}} | ||||||
| 					{{if ne . "" }}<div class="ui green basic label topic">{{.}}</div>{{end}} | 					{{if ne . "" }}<a href="/explore/repos?q={{.}}&topic=1"><div class="ui green basic label topic">{{.}}</div></a>{{end}} | ||||||
| 				{{end}} | 				{{end}} | ||||||
| 				</div> | 				</div> | ||||||
| 			{{end}} | 			{{end}} | ||||||
|   | |||||||
| @@ -24,8 +24,8 @@ | |||||||
| 			{{end}} | 			{{end}} | ||||||
| 		</div> | 		</div> | ||||||
| 		<div class="ui repo-topic" id="repo-topic"> | 		<div class="ui repo-topic" id="repo-topic"> | ||||||
| 		{{range .Topics}}<div class="ui green basic label topic" style="cursor:pointer;">{{.Name}}</div>{{end}} | 		{{range .Topics}}<a class="ui green basic label topic" style="cursor:pointer;" href="/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}} | ||||||
| 		{{if .IsRepositoryAdmin}}<a id="manage_topic" style="cursor:pointer;margin-left:10px;">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}} | 		{{if .IsRepositoryAdmin}}<a id="manage_topic" style="cursor:pointer;margin-left:10px;" href="/explore/repos?q={{.Name}}&topic=1">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}} | ||||||
| 		</div> | 		</div> | ||||||
| 		{{if .IsRepositoryAdmin}} | 		{{if .IsRepositoryAdmin}} | ||||||
| 		<div class="ui repo-topic-edit grid form segment error" id="topic_edit" > | 		<div class="ui repo-topic-edit grid form segment error" id="topic_edit" > | ||||||
| @@ -34,7 +34,7 @@ | |||||||
| 					<div class="ui fluid multiple search selection dropdown"> | 					<div class="ui fluid multiple search selection dropdown"> | ||||||
| 						<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}"> | 						<input type="hidden" name="topics" value="{{range $i, $v := .Topics}}{{.Name}}{{if lt (Add $i 1) (len $.Topics)}},{{end}}{{end}}"> | ||||||
| 						{{range .Topics}} | 						{{range .Topics}} | ||||||
| 						<a class="ui green basic label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important;">{{.Name}}<i class="delete icon"></i></a> | 						<a class="ui green basic label topic transition visible" data-value="{{.Name}}" style="display: inline-block !important;" href="/explore/repos?q={{.Name}}&topic=1">{{.Name}}<i class="delete icon"></i></a> | ||||||
| 						{{end}} | 						{{end}} | ||||||
| 						<div class="text"></div> | 						<div class="text"></div> | ||||||
| 					</div> | 					</div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Bo-Yi Wu
					Bo-Yi Wu