Skip to content

Commit c000ab4

Browse files
committed
Add user/organization code search
1 parent 083ac16 commit c000ab4

File tree

17 files changed

+253
-78
lines changed

17 files changed

+253
-78
lines changed

models/repo/repo_list.go

+20-10
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,16 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
593593
return sess, count, nil
594594
}
595595

596+
// SearchRepositoryIDsByCondition search repository IDs by given condition.
597+
func SearchRepositoryIDsByCondition(ctx context.Context, cond builder.Cond) ([]int64, error) {
598+
repoIDs := make([]int64, 0, 10)
599+
return repoIDs, db.GetEngine(ctx).
600+
Table("repository").
601+
Cols("id").
602+
Where(cond).
603+
Find(&repoIDs)
604+
}
605+
596606
// AccessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
597607
func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) builder.Cond {
598608
cond := builder.NewCond()
@@ -680,16 +690,16 @@ func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
680690
}
681691

682692
// FindUserCodeAccessibleRepoIDs finds all at Code level accessible repositories' ID by the user's id
683-
func FindUserCodeAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
684-
repoIDs := make([]int64, 0, 10)
685-
if err := db.GetEngine(db.DefaultContext).
686-
Table("repository").
687-
Cols("id").
688-
Where(AccessibleRepositoryCondition(user, unit.TypeCode)).
689-
Find(&repoIDs); err != nil {
690-
return nil, fmt.Errorf("FindUserCodeAccesibleRepoIDs: %v", err)
691-
}
692-
return repoIDs, nil
693+
func FindUserCodeAccessibleRepoIDs(ctx context.Context, user *user_model.User) ([]int64, error) {
694+
return SearchRepositoryIDsByCondition(ctx, AccessibleRepositoryCondition(user, unit.TypeCode))
695+
}
696+
697+
// FindUserCodeAccessibleOwnerRepoIDs finds all repository IDs for the given owner whose code the user can see.
698+
func FindUserCodeAccessibleOwnerRepoIDs(ctx context.Context, ownerID int64, user *user_model.User) ([]int64, error) {
699+
return SearchRepositoryIDsByCondition(ctx, builder.NewCond().And(
700+
builder.Eq{"owner_id": ownerID},
701+
AccessibleRepositoryCondition(user, unit.TypeCode),
702+
))
693703
}
694704

695705
// GetUserRepositories returns a list of repositories of given user.

modules/context/org.go

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
130130
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
131131
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
132132
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
133+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
133134
ctx.Data["IsPublicMember"] = func(uid int64) bool {
134135
is, _ := organization.IsPublicMembership(ctx.Org.Organization.ID, uid)
135136
return is

options/locale/locale_en-US.ini

+2
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ activity = Public Activity
507507
followers = Followers
508508
starred = Starred Repositories
509509
watched = Watched Repositories
510+
code = Code
510511
projects = Projects
511512
following = Following
512513
follow = Follow
@@ -2310,6 +2311,7 @@ create_org = Create Organization
23102311
repo_updated = Updated
23112312
people = People
23122313
teams = Teams
2314+
code = Code
23132315
lower_members = members
23142316
lower_repositories = repositories
23152317
create_new_team = New Team

routers/web/explore/code.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ func Code(ctx *context.Context) {
5454

5555
// guest user or non-admin user
5656
if ctx.Doer == nil || !isAdmin {
57-
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx.Doer)
57+
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer)
5858
if err != nil {
59-
ctx.ServerError("SearchResults", err)
59+
ctx.ServerError("FindUserCodeAccessibleRepoIDs", err)
6060
return
6161
}
6262
}

routers/web/user/code.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package user
6+
7+
import (
8+
"net/http"
9+
10+
repo_model "code.gitea.io/gitea/models/repo"
11+
"code.gitea.io/gitea/modules/base"
12+
"code.gitea.io/gitea/modules/context"
13+
code_indexer "code.gitea.io/gitea/modules/indexer/code"
14+
"code.gitea.io/gitea/modules/setting"
15+
)
16+
17+
const (
18+
tplUserCode base.TplName = "user/code"
19+
)
20+
21+
// CodeSearch render user/organization code search page
22+
func CodeSearch(ctx *context.Context) {
23+
if !setting.Indexer.RepoIndexerEnabled {
24+
ctx.Redirect(ctx.ContextUser.HomeLink())
25+
return
26+
}
27+
28+
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
29+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
30+
ctx.Data["Title"] = ctx.Tr("code.title")
31+
ctx.Data["ContextUser"] = ctx.ContextUser
32+
33+
language := ctx.FormTrim("l")
34+
keyword := ctx.FormTrim("q")
35+
36+
queryType := ctx.FormTrim("t")
37+
isMatch := queryType == "match"
38+
39+
ctx.Data["Keyword"] = keyword
40+
ctx.Data["Language"] = language
41+
ctx.Data["queryType"] = queryType
42+
ctx.Data["IsCodePage"] = true
43+
44+
if keyword == "" {
45+
ctx.HTML(http.StatusOK, tplUserCode)
46+
return
47+
}
48+
49+
var (
50+
repoIDs []int64
51+
err error
52+
)
53+
54+
page := ctx.FormInt("page")
55+
if page <= 0 {
56+
page = 1
57+
}
58+
59+
repoIDs, err = repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer)
60+
if err != nil {
61+
ctx.ServerError("FindUserCodeAccessibleOwnerRepoIDs", err)
62+
return
63+
}
64+
65+
var (
66+
total int
67+
searchResults []*code_indexer.Result
68+
searchResultLanguages []*code_indexer.SearchResultLanguages
69+
)
70+
71+
if len(repoIDs) > 0 {
72+
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
73+
if err != nil {
74+
if code_indexer.IsAvailable() {
75+
ctx.ServerError("SearchResults", err)
76+
return
77+
}
78+
ctx.Data["CodeIndexerUnavailable"] = true
79+
} else {
80+
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
81+
}
82+
83+
loadRepoIDs := make([]int64, 0, len(searchResults))
84+
for _, result := range searchResults {
85+
var find bool
86+
for _, id := range loadRepoIDs {
87+
if id == result.RepoID {
88+
find = true
89+
break
90+
}
91+
}
92+
if !find {
93+
loadRepoIDs = append(loadRepoIDs, result.RepoID)
94+
}
95+
}
96+
97+
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
98+
if err != nil {
99+
ctx.ServerError("GetRepositoriesMapByIDs", err)
100+
return
101+
}
102+
103+
ctx.Data["RepoMaps"] = repoMaps
104+
}
105+
ctx.Data["SearchResults"] = searchResults
106+
ctx.Data["SearchResultLanguages"] = searchResultLanguages
107+
108+
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
109+
pager.SetDefaultParams(ctx)
110+
pager.AddParam(ctx, "l", "Language")
111+
ctx.Data["Page"] = pager
112+
113+
ctx.HTML(http.StatusOK, tplUserCode)
114+
}

routers/web/user/package.go

+4
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func ListPackages(ctx *context.Context) {
8686

8787
ctx.Data["Title"] = ctx.Tr("packages.title")
8888
ctx.Data["IsPackagesPage"] = true
89+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
8990
ctx.Data["ContextUser"] = ctx.ContextUser
9091
ctx.Data["Query"] = query
9192
ctx.Data["PackageType"] = packageType
@@ -157,6 +158,7 @@ func ViewPackageVersion(ctx *context.Context) {
157158

158159
ctx.Data["Title"] = pd.Package.Name
159160
ctx.Data["IsPackagesPage"] = true
161+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
160162
ctx.Data["ContextUser"] = ctx.ContextUser
161163
ctx.Data["PackageDescriptor"] = pd
162164

@@ -234,6 +236,7 @@ func ListPackageVersions(ctx *context.Context) {
234236

235237
ctx.Data["Title"] = ctx.Tr("packages.title")
236238
ctx.Data["IsPackagesPage"] = true
239+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
237240
ctx.Data["ContextUser"] = ctx.ContextUser
238241
ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
239242
Package: p,
@@ -305,6 +308,7 @@ func PackageSettings(ctx *context.Context) {
305308

306309
ctx.Data["Title"] = pd.Package.Name
307310
ctx.Data["IsPackagesPage"] = true
311+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
308312
ctx.Data["ContextUser"] = ctx.ContextUser
309313
ctx.Data["PackageDescriptor"] = pd
310314

routers/web/user/profile.go

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ func Profile(ctx *context.Context) {
290290
}
291291
ctx.Data["Page"] = pager
292292
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
293+
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
293294

294295
ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.Doer.ID)
295296

routers/web/web.go

+1
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ func RegisterRoutes(m *web.Route) {
754754
})
755755
}, ignSignIn, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
756756
}
757+
m.Get("/code", user.CodeSearch)
757758
}, context_service.UserAssignmentWeb())
758759

759760
// ***** Release Attachment Download without Signin

templates/code/searchform.tmpl

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<form class="ui form ignore-dirty" style="max-width: 100%">
2+
<div class="ui fluid action input">
3+
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable }} disabled{{end}} placeholder="{{.locale.Tr "explore.search"}}…" autofocus>
4+
<div class="ui dropdown selection{{if .CodeIndexerUnavailable }} disabled{{end}}">
5+
<input name="t" type="hidden" value="{{.queryType}}"{{if .CodeIndexerUnavailable }} disabled{{end}}>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
6+
<div class="text">{{.locale.Tr (printf "explore.search.%s" (or .queryType "fuzzy"))}}</div>
7+
<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
8+
<div class="item" data-value="">{{.locale.Tr "explore.search.fuzzy"}}</div>
9+
<div class="item" data-value="match">{{.locale.Tr "explore.search.match"}}</div>
10+
</div>
11+
</div>
12+
<button class="ui primary button"{{if .CodeIndexerUnavailable }} disabled{{end}}>{{.locale.Tr "explore.search"}}</button>
13+
</div>
14+
</form>

templates/code/searchresults.tmpl

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<div class="df ac fw">
2+
{{range $term := .SearchResultLanguages}}
3+
<a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}{{if $.ContextUser}}/{{$.ContextUser.Name}}/-/code{{else}}/explore/code{{end}}?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}">
4+
<i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i>
5+
{{$term.Language}}
6+
<div class="detail">{{$term.Count}}</div>
7+
</a>
8+
{{end}}
9+
</div>
10+
<div class="repository search">
11+
{{range $result := .SearchResults}}
12+
{{$repo := (index $.RepoMaps .RepoID)}}
13+
<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
14+
<h4 class="ui top attached normal header">
15+
<span class="file">
16+
<a rel="nofollow" href="{{$repo.HTMLURL}}">{{$repo.FullName}}</a>
17+
{{if $repo.IsArchived}}
18+
<span class="ui basic label">{{$.locale.Tr "repo.desc.archived"}}</span>
19+
{{end}}
20+
- {{.Filename}}
21+
</span>
22+
<a class="ui basic tiny button" rel="nofollow" href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{.Filename | PathEscapeSegments}}">{{$.locale.Tr "repo.diff.view_file"}}</a>
23+
</h4>
24+
<div class="ui attached table segment">
25+
<div class="file-body file-code code-view">
26+
<table>
27+
<tbody>
28+
<tr>
29+
<td class="lines-num">
30+
{{range .LineNumbers}}
31+
<a href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a>
32+
{{end}}
33+
</td>
34+
<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td>
35+
</tr>
36+
</tbody>
37+
</table>
38+
</div>
39+
</div>
40+
{{template "shared/searchbottom" dict "root" $ "result" .}}
41+
</div>
42+
{{end}}
43+
</div>

templates/explore/code.tmpl

+3-58
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,7 @@
22
<div class="page-content explore users">
33
{{template "explore/navbar" .}}
44
<div class="ui container">
5-
<form class="ui form ignore-dirty" style="max-width: 100%">
6-
<div class="ui fluid action input">
7-
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "explore.search"}}..." autofocus>
8-
<div class="ui dropdown selection{{if .CodeIndexerUnavailable}} disabled{{end}}">
9-
<input name="t" type="hidden" value="{{.queryType}}"{{if .CodeIndexerUnavailable}} disabled{{end}}>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
10-
<div class="text">{{.locale.Tr (printf "explore.search.%s" (or .queryType "fuzzy"))}}</div>
11-
<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
12-
<div class="item" data-value="">{{.locale.Tr "explore.search.fuzzy"}}</div>
13-
<div class="item" data-value="match">{{.locale.Tr "explore.search.match"}}</div>
14-
</div>
15-
</div>
16-
<button class="ui primary button"{{if .CodeIndexerUnavailable}} disabled{{end}}>{{.locale.Tr "explore.search"}}</button>
17-
</div>
18-
</form>
5+
{{template "code/searchform" .}}
196
<div class="ui divider"></div>
207
<div class="ui user list">
218
{{if .CodeIndexerUnavailable}}
@@ -26,50 +13,8 @@
2613
<h3>
2714
{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}}
2815
</h3>
29-
<div class="df ac fw">
30-
{{range $term := .SearchResultLanguages}}
31-
<a class="ui text-label df ac mr-1 my-1 {{if eq $.Language $term.Language}}primary {{end}}basic label" href="{{AppSubUrl}}/explore/code?q={{$.Keyword}}{{if ne $.Language $term.Language}}&l={{$term.Language}}{{end}}{{if ne $.queryType ""}}&t={{$.queryType}}{{end}}">
32-
<i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i>
33-
{{$term.Language}}
34-
<div class="detail">{{$term.Count}}</div>
35-
</a>
36-
{{end}}
37-
</div>
38-
<div class="repository search">
39-
{{range $result := .SearchResults}}
40-
{{$repo := (index $.RepoMaps .RepoID)}}
41-
<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
42-
<h4 class="ui top attached normal header">
43-
<span class="file">
44-
<a rel="nofollow" href="{{$repo.HTMLURL}}">{{$repo.FullName}}</a>
45-
{{if $repo.IsArchived}}
46-
<span class="ui basic label">{{$.locale.Tr "repo.desc.archived"}}</span>
47-
{{end}}
48-
- {{.Filename}}
49-
</span>
50-
<a class="ui basic tiny button" rel="nofollow" href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{.Filename | PathEscapeSegments}}">{{$.locale.Tr "repo.diff.view_file"}}</a>
51-
</h4>
52-
<div class="ui attached table segment">
53-
<div class="file-body file-code code-view">
54-
<table>
55-
<tbody>
56-
<tr>
57-
<td class="lines-num">
58-
{{range .LineNumbers}}
59-
<a href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a>
60-
{{end}}
61-
</td>
62-
<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td>
63-
</tr>
64-
</tbody>
65-
</table>
66-
</div>
67-
</div>
68-
{{template "shared/searchbottom" dict "root" $ "result" .}}
69-
</div>
70-
{{end}}
71-
</div>
72-
{{else}}
16+
{{template "code/searchresults" .}}
17+
{{else if .Keyword}}
7318
<div>{{$.locale.Tr "explore.code_no_results"}}</div>
7419
{{end}}
7520
</div>

templates/explore/repo_search.tmpl

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<input type="hidden" name="sort" value="{{$.SortType}}">
2626
<input type="hidden" name="language" value="{{$.Language}}">
2727
<div class="ui fluid action input">
28-
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..." autofocus>
28+
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}" autofocus>
2929
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
3030
</div>
3131
</form>

templates/explore/search.tmpl

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
</div>
1818
<form class="ui form ignore-dirty" style="max-width: 90%">
1919
<div class="ui fluid action input">
20-
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..." autofocus>
20+
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}" autofocus>
2121
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
2222
</div>
2323
</form>

0 commit comments

Comments
 (0)