| @@ -69,40 +69,41 @@ func TestAPISearchRepo(t *testing.T) { | |||||
| name, requestURL string | name, requestURL string | ||||
| expectedResults | expectedResults | ||||
| }{ | }{ | ||||
| {name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ | |||||
| {name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50&private=false", expectedResults: expectedResults{ | |||||
| nil: {count: 21}, | nil: {count: 21}, | ||||
| user: {count: 21}, | user: {count: 21}, | ||||
| user2: {count: 21}}, | user2: {count: 21}}, | ||||
| }, | }, | ||||
| {name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ | |||||
| {name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10&private=false", expectedResults: expectedResults{ | |||||
| nil: {count: 10}, | nil: {count: 10}, | ||||
| user: {count: 10}, | user: {count: 10}, | ||||
| user2: {count: 10}}, | user2: {count: 10}}, | ||||
| }, | }, | ||||
| {name: "RepositoriesDefaultMax10", requestURL: "/api/v1/repos/search?default", expectedResults: expectedResults{ | |||||
| {name: "RepositoriesDefaultMax10", requestURL: "/api/v1/repos/search?default&private=false", expectedResults: expectedResults{ | |||||
| nil: {count: 10}, | nil: {count: 10}, | ||||
| user: {count: 10}, | user: {count: 10}, | ||||
| user2: {count: 10}}, | user2: {count: 10}}, | ||||
| }, | }, | ||||
| {name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s", "big_test_"), expectedResults: expectedResults{ | |||||
| {name: "RepositoriesByName", requestURL: fmt.Sprintf("/api/v1/repos/search?q=%s&private=false", "big_test_"), expectedResults: expectedResults{ | |||||
| nil: {count: 7, repoName: "big_test_"}, | nil: {count: 7, repoName: "big_test_"}, | ||||
| user: {count: 7, repoName: "big_test_"}, | user: {count: 7, repoName: "big_test_"}, | ||||
| user2: {count: 7, repoName: "big_test_"}}, | user2: {count: 7, repoName: "big_test_"}}, | ||||
| }, | }, | ||||
| {name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user.ID), expectedResults: expectedResults{ | ||||
| nil: {count: 4}, | |||||
| user: {count: 8, includesPrivate: true}, | |||||
| user2: {count: 4}}, | |||||
| nil: {count: 5}, | |||||
| user: {count: 9, includesPrivate: true}, | |||||
| user2: {count: 5, includesPrivate: true}}, | |||||
| }, | }, | ||||
| {name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser2", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user2.ID), expectedResults: expectedResults{ | ||||
| nil: {count: 1}, | nil: {count: 1}, | ||||
| user: {count: 1}, | |||||
| user2: {count: 2, includesPrivate: true}}, | |||||
| user: {count: 2, includesPrivate: true}, | |||||
| user2: {count: 2, includesPrivate: true}, | |||||
| user4: {count: 1}}, | |||||
| }, | }, | ||||
| {name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user3.ID), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser3", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user3.ID), expectedResults: expectedResults{ | ||||
| nil: {count: 1}, | nil: {count: 1}, | ||||
| user: {count: 1}, | |||||
| user2: {count: 1}, | |||||
| user: {count: 4, includesPrivate: true}, | |||||
| user2: {count: 2, includesPrivate: true}, | |||||
| user3: {count: 4, includesPrivate: true}}, | user3: {count: 4, includesPrivate: true}}, | ||||
| }, | }, | ||||
| {name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{ | {name: "RepositoriesOwnedByOrganization", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", orgUser.ID), expectedResults: expectedResults{ | ||||
| @@ -112,12 +113,12 @@ func TestAPISearchRepo(t *testing.T) { | |||||
| }, | }, | ||||
| {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser4", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d", user4.ID), expectedResults: expectedResults{ | ||||
| nil: {count: 3}, | nil: {count: 3}, | ||||
| user: {count: 3}, | |||||
| user4: {count: 6, includesPrivate: true}}}, | |||||
| user: {count: 4, includesPrivate: true}, | |||||
| user4: {count: 7, includesPrivate: true}}}, | |||||
| {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeSource", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "source"), expectedResults: expectedResults{ | ||||
| nil: {count: 0}, | nil: {count: 0}, | ||||
| user: {count: 0}, | |||||
| user4: {count: 0, includesPrivate: true}}}, | |||||
| user: {count: 1, includesPrivate: true}, | |||||
| user4: {count: 1, includesPrivate: true}}}, | |||||
| {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeFork", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "fork"), expectedResults: expectedResults{ | ||||
| nil: {count: 1}, | nil: {count: 1}, | ||||
| user: {count: 1}, | user: {count: 1}, | ||||
| @@ -136,8 +137,8 @@ func TestAPISearchRepo(t *testing.T) { | |||||
| user4: {count: 2, includesPrivate: true}}}, | user4: {count: 2, includesPrivate: true}}}, | ||||
| {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{ | {name: "RepositoriesAccessibleAndRelatedToUser4/SearchModeCollaborative", requestURL: fmt.Sprintf("/api/v1/repos/search?uid=%d&mode=%s", user4.ID, "collaborative"), expectedResults: expectedResults{ | ||||
| nil: {count: 0}, | nil: {count: 0}, | ||||
| user: {count: 0}, | |||||
| user4: {count: 0, includesPrivate: true}}}, | |||||
| user: {count: 1, includesPrivate: true}, | |||||
| user4: {count: 1, includesPrivate: true}}}, | |||||
| } | } | ||||
| for _, testCase := range testCases { | for _, testCase := range testCases { | ||||
| @@ -164,14 +165,19 @@ func TestAPISearchRepo(t *testing.T) { | |||||
| var body api.SearchResults | var body api.SearchResults | ||||
| DecodeJSON(t, response, &body) | DecodeJSON(t, response, &body) | ||||
| assert.Len(t, body.Data, expected.count) | |||||
| repoNames := make([]string, 0, len(body.Data)) | |||||
| for _, repo := range body.Data { | |||||
| repoNames = append(repoNames, fmt.Sprintf("%d:%s:%t", repo.ID, repo.FullName, repo.Private)) | |||||
| } | |||||
| assert.Len(t, repoNames, expected.count) | |||||
| for _, repo := range body.Data { | for _, repo := range body.Data { | ||||
| r := getRepo(t, repo.ID) | r := getRepo(t, repo.ID) | ||||
| hasAccess, err := models.HasAccess(userID, r) | hasAccess, err := models.HasAccess(userID, r) | ||||
| assert.NoError(t, err) | |||||
| assert.True(t, hasAccess) | |||||
| assert.NoError(t, err, "Error when checking if User: %d has access to %s: %v", userID, repo.FullName, err) | |||||
| assert.True(t, hasAccess, "User: %d does not have access to %s", userID, repo.FullName) | |||||
| assert.NotEmpty(t, repo.Name) | assert.NotEmpty(t, repo.Name) | ||||
| assert.Equal(t, repo.Name, r.Name) | |||||
| if len(expected.repoName) > 0 { | if len(expected.repoName) > 0 { | ||||
| assert.Contains(t, repo.Name, expected.repoName) | assert.Contains(t, repo.Name, expected.repoName) | ||||
| @@ -182,7 +188,7 @@ func TestAPISearchRepo(t *testing.T) { | |||||
| } | } | ||||
| if !expected.includesPrivate { | if !expected.includesPrivate { | ||||
| assert.False(t, repo.Private) | |||||
| assert.False(t, repo.Private, "User: %d not expecting private repository: %s", userID, repo.FullName) | |||||
| } | } | ||||
| } | } | ||||
| }) | }) | ||||
| @@ -12,7 +12,6 @@ import ( | |||||
| "code.gitea.io/gitea/modules/util" | "code.gitea.io/gitea/modules/util" | ||||
| "github.com/go-xorm/builder" | "github.com/go-xorm/builder" | ||||
| "github.com/go-xorm/core" | |||||
| ) | ) | ||||
| // RepositoryListDefaultPageSize is the default number of repositories | // RepositoryListDefaultPageSize is the default number of repositories | ||||
| @@ -112,15 +111,17 @@ func (repos MirrorRepositoryList) LoadAttributes() error { | |||||
| // SearchRepoOptions holds the search options | // SearchRepoOptions holds the search options | ||||
| type SearchRepoOptions struct { | type SearchRepoOptions struct { | ||||
| Keyword string | |||||
| OwnerID int64 | |||||
| OrderBy SearchOrderBy | |||||
| Private bool // Include private repositories in results | |||||
| Starred bool | |||||
| Page int | |||||
| IsProfile bool | |||||
| AllPublic bool // Include also all public repositories | |||||
| PageSize int // Can be smaller than or equal to setting.ExplorePagingNum | |||||
| UserID int64 | |||||
| UserIsAdmin bool | |||||
| Keyword string | |||||
| OwnerID int64 | |||||
| OrderBy SearchOrderBy | |||||
| Private bool // Include private repositories in results | |||||
| StarredByID int64 | |||||
| Page int | |||||
| IsProfile bool | |||||
| AllPublic bool // Include also all public repositories | |||||
| PageSize int // Can be smaller than or equal to setting.ExplorePagingNum | |||||
| // None -> include collaborative AND non-collaborative | // None -> include collaborative AND non-collaborative | ||||
| // True -> include just collaborative | // True -> include just collaborative | ||||
| // False -> incude just non-collaborative | // False -> incude just non-collaborative | ||||
| @@ -168,72 +169,79 @@ func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, err | |||||
| if opts.Page <= 0 { | if opts.Page <= 0 { | ||||
| opts.Page = 1 | opts.Page = 1 | ||||
| } | } | ||||
| var cond = builder.NewCond() | var cond = builder.NewCond() | ||||
| if !opts.Private { | |||||
| if opts.Private { | |||||
| if !opts.UserIsAdmin && opts.UserID != 0 && opts.UserID != opts.OwnerID { | |||||
| // OK we're in the context of a User | |||||
| // We should be Either | |||||
| cond = cond.And(builder.Or( | |||||
| // 1. Be able to see all non-private repositories that either: | |||||
| cond.And( | |||||
| builder.Eq{"is_private": false}, | |||||
| builder.Or( | |||||
| // A. Aren't in organisations __OR__ | |||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})), | |||||
| // B. Isn't a private organisation. (Limited is OK because we're logged in) | |||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"visibility": structs.VisibleTypePrivate}))), | |||||
| ), | |||||
| // 2. Be able to see all repositories that we have access to | |||||
| builder.In("id", builder.Select("repo_id"). | |||||
| From("`access`"). | |||||
| Where(builder.And( | |||||
| builder.Eq{"user_id": opts.UserID}, | |||||
| builder.Gt{"mode": int(AccessModeNone)}))), | |||||
| // 3. Be able to see all repositories that we are in a team | |||||
| builder.In("id", builder.Select("`team_repo`.repo_id"). | |||||
| From("team_repo"). | |||||
| Where(builder.Eq{"`team_user`.uid": opts.UserID}). | |||||
| Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id")))) | |||||
| } | |||||
| } else { | |||||
| // Not looking at private organisations | |||||
| // We should be able to see all non-private repositories that either: | |||||
| cond = cond.And(builder.Eq{"is_private": false}) | cond = cond.And(builder.Eq{"is_private": false}) | ||||
| accessCond := builder.Or( | accessCond := builder.Or( | ||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}))), | |||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization}))) | |||||
| // A. Aren't in organisations __OR__ | |||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})), | |||||
| // B. Isn't a private or limited organisation. | |||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate})))) | |||||
| cond = cond.And(accessCond) | cond = cond.And(accessCond) | ||||
| } | } | ||||
| // Restrict to starred repositories | |||||
| if opts.StarredByID > 0 { | |||||
| cond = cond.And(builder.In("id", builder.Select("repo_id").From("star").Where(builder.Eq{"uid": opts.StarredByID}))) | |||||
| } | |||||
| // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate | |||||
| if opts.OwnerID > 0 { | if opts.OwnerID > 0 { | ||||
| if opts.Starred { | |||||
| cond = cond.And(builder.In("id", builder.Select("repo_id").From("star").Where(builder.Eq{"uid": opts.OwnerID}))) | |||||
| } else { | |||||
| var accessCond = builder.NewCond() | |||||
| if opts.Collaborate != util.OptionalBoolTrue { | |||||
| accessCond = builder.Eq{"owner_id": opts.OwnerID} | |||||
| } | |||||
| var accessCond = builder.NewCond() | |||||
| if opts.Collaborate != util.OptionalBoolTrue { | |||||
| accessCond = builder.Eq{"owner_id": opts.OwnerID} | |||||
| } | |||||
| if opts.Collaborate != util.OptionalBoolFalse { | |||||
| collaborateCond := builder.And( | |||||
| if opts.Collaborate != util.OptionalBoolFalse { | |||||
| collaborateCond := builder.And( | |||||
| builder.Or( | |||||
| builder.Expr("repository.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}) | |||||
| 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)) | |||||
| } | |||||
| accessCond = accessCond.Or(collaborateCond) | |||||
| builder.In("id", builder.Select("`team_repo`.repo_id"). | |||||
| From("team_repo"). | |||||
| Where(builder.Eq{"`team_user`.uid": opts.OwnerID}). | |||||
| Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id"))), | |||||
| builder.Neq{"owner_id": opts.OwnerID}) | |||||
| 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)) | |||||
| } | } | ||||
| var exprCond builder.Cond | |||||
| if DbCfg.Type == core.POSTGRES { | |||||
| exprCond = builder.Expr("org_user.org_id = \"user\".id") | |||||
| } else if DbCfg.Type == core.MSSQL { | |||||
| exprCond = builder.Expr("org_user.org_id = [user].id") | |||||
| } else { | |||||
| exprCond = builder.Eq{"org_user.org_id": "user.id"} | |||||
| } | |||||
| visibilityCond := builder.Or( | |||||
| builder.In("owner_id", | |||||
| builder.Select("org_id").From("org_user"). | |||||
| LeftJoin("`user`", exprCond). | |||||
| Where( | |||||
| builder.And( | |||||
| builder.Eq{"uid": opts.OwnerID}, | |||||
| builder.Eq{"visibility": structs.VisibleTypePrivate})), | |||||
| ), | |||||
| builder.In("owner_id", | |||||
| builder.Select("id").From("`user`"). | |||||
| Where( | |||||
| builder.Or( | |||||
| builder.Eq{"visibility": structs.VisibleTypePublic}, | |||||
| builder.Eq{"visibility": structs.VisibleTypeLimited})), | |||||
| ), | |||||
| builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(builder.Eq{"type": UserTypeOrganization})), | |||||
| ) | |||||
| cond = cond.And(visibilityCond) | |||||
| if opts.AllPublic { | |||||
| accessCond = accessCond.Or(builder.Eq{"is_private": false}) | |||||
| } | |||||
| accessCond = accessCond.Or(collaborateCond) | |||||
| } | |||||
| cond = cond.And(accessCond) | |||||
| if opts.AllPublic { | |||||
| accessCond = accessCond.Or(builder.Eq{"is_private": false}) | |||||
| } | } | ||||
| cond = cond.And(accessCond) | |||||
| } | } | ||||
| if opts.Keyword != "" { | if opts.Keyword != "" { | ||||
| @@ -117,7 +117,7 @@ func TestSearchRepositoryByName(t *testing.T) { | |||||
| count: 4}, | count: 4}, | ||||
| {name: "PublicRepositoriesOfUserIncludingCollaborative", | {name: "PublicRepositoriesOfUserIncludingCollaborative", | ||||
| opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15}, | opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15}, | ||||
| count: 4}, | |||||
| count: 5}, | |||||
| {name: "PublicRepositoriesOfUser2IncludingCollaborative", | {name: "PublicRepositoriesOfUser2IncludingCollaborative", | ||||
| opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18}, | opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18}, | ||||
| count: 1}, | count: 1}, | ||||
| @@ -126,13 +126,13 @@ func TestSearchRepositoryByName(t *testing.T) { | |||||
| count: 3}, | count: 3}, | ||||
| {name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative", | {name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative", | ||||
| opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true}, | opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true}, | ||||
| count: 8}, | |||||
| count: 9}, | |||||
| {name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative", | {name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative", | ||||
| opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true}, | opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true}, | ||||
| count: 4}, | count: 4}, | ||||
| {name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative", | {name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative", | ||||
| opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true}, | opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true}, | ||||
| count: 6}, | |||||
| count: 7}, | |||||
| {name: "PublicRepositoriesOfOrganization", | {name: "PublicRepositoriesOfOrganization", | ||||
| opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, | opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, | ||||
| count: 1}, | count: 1}, | ||||
| @@ -150,7 +150,7 @@ func TestSearchRepositoryByName(t *testing.T) { | |||||
| count: 21}, | count: 21}, | ||||
| {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: 26}, | |||||
| count: 27}, | |||||
| {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: 15}, | count: 15}, | ||||
| @@ -56,6 +56,15 @@ func Search(ctx *context.APIContext) { | |||||
| // description: search only for repos that the user with the given id owns or contributes to | // description: search only for repos that the user with the given id owns or contributes to | ||||
| // type: integer | // type: integer | ||||
| // format: int64 | // format: int64 | ||||
| // - name: starredBy | |||||
| // in: query | |||||
| // description: search only for repos that the user with the given id has starred | |||||
| // type: integer | |||||
| // format: int64 | |||||
| // - name: private | |||||
| // in: query | |||||
| // description: include private repositories this user has access to (defaults to true) | |||||
| // type: boolean | |||||
| // - name: page | // - name: page | ||||
| // in: query | // in: query | ||||
| // description: page number of results to return (1-based) | // description: page number of results to return (1-based) | ||||
| @@ -96,6 +105,10 @@ func Search(ctx *context.APIContext) { | |||||
| PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), | PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")), | ||||
| TopicOnly: ctx.QueryBool("topic"), | TopicOnly: ctx.QueryBool("topic"), | ||||
| Collaborate: util.OptionalBoolNone, | Collaborate: util.OptionalBoolNone, | ||||
| Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")), | |||||
| UserIsAdmin: ctx.IsUserSiteAdmin(), | |||||
| UserID: ctx.Data["SignedUserID"].(int64), | |||||
| StarredByID: ctx.QueryInt64("starredBy"), | |||||
| } | } | ||||
| if ctx.QueryBool("exclusive") { | if ctx.QueryBool("exclusive") { | ||||
| @@ -140,42 +153,6 @@ func Search(ctx *context.APIContext) { | |||||
| } | } | ||||
| var err error | var err error | ||||
| if opts.OwnerID > 0 { | |||||
| var repoOwner *models.User | |||||
| if ctx.User != nil && ctx.User.ID == opts.OwnerID { | |||||
| repoOwner = ctx.User | |||||
| } else { | |||||
| repoOwner, err = models.GetUserByID(opts.OwnerID) | |||||
| if err != nil { | |||||
| ctx.JSON(500, api.SearchError{ | |||||
| OK: false, | |||||
| Error: err.Error(), | |||||
| }) | |||||
| return | |||||
| } | |||||
| } | |||||
| if repoOwner.IsOrganization() { | |||||
| opts.Collaborate = util.OptionalBoolFalse | |||||
| } | |||||
| // Check visibility. | |||||
| if ctx.IsSigned { | |||||
| if ctx.User.ID == repoOwner.ID { | |||||
| opts.Private = true | |||||
| } else if repoOwner.IsOrganization() { | |||||
| opts.Private, err = repoOwner.IsOwnedBy(ctx.User.ID) | |||||
| if err != nil { | |||||
| ctx.JSON(500, api.SearchError{ | |||||
| OK: false, | |||||
| Error: err.Error(), | |||||
| }) | |||||
| return | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| repos, count, err := models.SearchRepositoryByName(opts) | repos, count, err := models.SearchRepositoryByName(opts) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.JSON(500, api.SearchError{ | ctx.JSON(500, api.SearchError{ | ||||
| @@ -499,50 +499,20 @@ func showOrgProfile(ctx *context.Context) { | |||||
| count int64 | count int64 | ||||
| err error | err error | ||||
| ) | ) | ||||
| if ctx.IsSigned && !ctx.User.IsAdmin { | |||||
| env, err := org.AccessibleReposEnv(ctx.User.ID) | |||||
| if err != nil { | |||||
| ctx.ServerError("AccessibleReposEnv", err) | |||||
| return | |||||
| } | |||||
| env.SetSort(orderBy) | |||||
| if len(keyword) != 0 { | |||||
| env.AddKeyword(keyword) | |||||
| } | |||||
| repos, err = env.Repos(page, setting.UI.User.RepoPagingNum) | |||||
| if err != nil { | |||||
| ctx.ServerError("env.Repos", err) | |||||
| return | |||||
| } | |||||
| count, err = env.CountRepos() | |||||
| if err != nil { | |||||
| ctx.ServerError("env.CountRepos", err) | |||||
| return | |||||
| } | |||||
| } else { | |||||
| showPrivate := ctx.IsSigned && ctx.User.IsAdmin | |||||
| if len(keyword) == 0 { | |||||
| repos, err = models.GetUserRepositories(org.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String()) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetRepositories", err) | |||||
| return | |||||
| } | |||||
| count = models.CountUserRepositories(org.ID, showPrivate) | |||||
| } else { | |||||
| repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | |||||
| Keyword: keyword, | |||||
| OwnerID: org.ID, | |||||
| OrderBy: orderBy, | |||||
| Private: showPrivate, | |||||
| Page: page, | |||||
| IsProfile: true, | |||||
| PageSize: setting.UI.User.RepoPagingNum, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchRepositoryByName", err) | |||||
| return | |||||
| } | |||||
| } | |||||
| repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | |||||
| Keyword: keyword, | |||||
| OwnerID: org.ID, | |||||
| OrderBy: orderBy, | |||||
| Private: ctx.IsSigned, | |||||
| UserIsAdmin: ctx.IsUserSiteAdmin(), | |||||
| UserID: ctx.Data["SignedUserID"].(int64), | |||||
| Page: page, | |||||
| IsProfile: true, | |||||
| PageSize: setting.UI.User.RepoPagingNum, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchRepositoryByName", err) | |||||
| return | |||||
| } | } | ||||
| if err := org.GetMembers(); err != nil { | if err := org.GetMembers(); err != nil { | ||||
| @@ -170,74 +170,44 @@ func Profile(ctx *context.Context) { | |||||
| } | } | ||||
| case "stars": | case "stars": | ||||
| ctx.Data["PageIsProfileStarList"] = true | ctx.Data["PageIsProfileStarList"] = true | ||||
| if len(keyword) == 0 { | |||||
| repos, err = ctxUser.GetStarredRepos(showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String()) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetStarredRepos", err) | |||||
| return | |||||
| } | |||||
| count, err = ctxUser.GetStarredRepoCount(showPrivate) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetStarredRepoCount", err) | |||||
| return | |||||
| } | |||||
| } else { | |||||
| repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | |||||
| Keyword: keyword, | |||||
| OwnerID: ctxUser.ID, | |||||
| OrderBy: orderBy, | |||||
| Private: showPrivate, | |||||
| Page: page, | |||||
| PageSize: setting.UI.User.RepoPagingNum, | |||||
| Starred: true, | |||||
| Collaborate: util.OptionalBoolFalse, | |||||
| TopicOnly: topicOnly, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchRepositoryByName", err) | |||||
| return | |||||
| } | |||||
| repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | |||||
| Keyword: keyword, | |||||
| OrderBy: orderBy, | |||||
| Private: ctx.IsSigned, | |||||
| UserIsAdmin: ctx.IsUserSiteAdmin(), | |||||
| UserID: ctx.Data["SignedUserID"].(int64), | |||||
| Page: page, | |||||
| PageSize: setting.UI.User.RepoPagingNum, | |||||
| StarredByID: ctxUser.ID, | |||||
| Collaborate: util.OptionalBoolFalse, | |||||
| TopicOnly: topicOnly, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchRepositoryByName", err) | |||||
| return | |||||
| } | } | ||||
| total = int(count) | total = int(count) | ||||
| default: | default: | ||||
| if len(keyword) == 0 { | |||||
| repos, err = models.GetUserRepositories(ctxUser.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String()) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetRepositories", err) | |||||
| return | |||||
| } | |||||
| if showPrivate { | |||||
| total = ctxUser.NumRepos | |||||
| } else { | |||||
| count, err := models.GetPublicRepositoryCount(ctxUser) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetPublicRepositoryCount", err) | |||||
| return | |||||
| } | |||||
| total = int(count) | |||||
| } | |||||
| } else { | |||||
| repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | |||||
| Keyword: keyword, | |||||
| OwnerID: ctxUser.ID, | |||||
| OrderBy: orderBy, | |||||
| Private: showPrivate, | |||||
| Page: page, | |||||
| IsProfile: true, | |||||
| PageSize: setting.UI.User.RepoPagingNum, | |||||
| Collaborate: util.OptionalBoolFalse, | |||||
| TopicOnly: topicOnly, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchRepositoryByName", err) | |||||
| return | |||||
| } | |||||
| total = int(count) | |||||
| repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{ | |||||
| Keyword: keyword, | |||||
| OwnerID: ctxUser.ID, | |||||
| OrderBy: orderBy, | |||||
| Private: ctx.IsSigned, | |||||
| UserIsAdmin: ctx.IsUserSiteAdmin(), | |||||
| UserID: ctx.Data["SignedUserID"].(int64), | |||||
| Page: page, | |||||
| IsProfile: true, | |||||
| PageSize: setting.UI.User.RepoPagingNum, | |||||
| Collaborate: util.OptionalBoolFalse, | |||||
| TopicOnly: topicOnly, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("SearchRepositoryByName", err) | |||||
| return | |||||
| } | } | ||||
| total = int(count) | |||||
| } | } | ||||
| ctx.Data["Repos"] = repos | ctx.Data["Repos"] = repos | ||||
| ctx.Data["Total"] = total | ctx.Data["Total"] = total | ||||
| @@ -1085,6 +1085,19 @@ | |||||
| "name": "uid", | "name": "uid", | ||||
| "in": "query" | "in": "query" | ||||
| }, | }, | ||||
| { | |||||
| "type": "integer", | |||||
| "format": "int64", | |||||
| "description": "search only for repos that the user with the given id has starred", | |||||
| "name": "starredBy", | |||||
| "in": "query" | |||||
| }, | |||||
| { | |||||
| "type": "boolean", | |||||
| "description": "include private repositories this user has access to (defaults to true)", | |||||
| "name": "private", | |||||
| "in": "query" | |||||
| }, | |||||
| { | { | ||||
| "type": "integer", | "type": "integer", | ||||
| "description": "page number of results to return (1-based)", | "description": "page number of results to return (1-based)", | ||||