Add user/organization code search (#19977)

Fixes #19925 

Screenshots:

![attels](https://user-images.githubusercontent.com/165205/173864718-fe789429-55bc-4cad-808c-9f02f335cddf.png)
This commit is contained in:
Lauris BH 2022-10-11 02:12:03 +03:00 committed by GitHub
parent 3ccebf7f40
commit b59b0cad0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 355 additions and 159 deletions

View File

@ -593,6 +593,16 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
return sess, count, nil return sess, count, nil
} }
// SearchRepositoryIDsByCondition search repository IDs by given condition.
func SearchRepositoryIDsByCondition(ctx context.Context, cond builder.Cond) ([]int64, error) {
repoIDs := make([]int64, 0, 10)
return repoIDs, db.GetEngine(ctx).
Table("repository").
Cols("id").
Where(cond).
Find(&repoIDs)
}
// AccessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible // AccessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) builder.Cond { func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
@ -680,16 +690,16 @@ func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
} }
// FindUserCodeAccessibleRepoIDs finds all at Code level accessible repositories' ID by the user's id // FindUserCodeAccessibleRepoIDs finds all at Code level accessible repositories' ID by the user's id
func FindUserCodeAccessibleRepoIDs(user *user_model.User) ([]int64, error) { func FindUserCodeAccessibleRepoIDs(ctx context.Context, user *user_model.User) ([]int64, error) {
repoIDs := make([]int64, 0, 10) return SearchRepositoryIDsByCondition(ctx, AccessibleRepositoryCondition(user, unit.TypeCode))
if err := db.GetEngine(db.DefaultContext). }
Table("repository").
Cols("id"). // FindUserCodeAccessibleOwnerRepoIDs finds all repository IDs for the given owner whose code the user can see.
Where(AccessibleRepositoryCondition(user, unit.TypeCode)). func FindUserCodeAccessibleOwnerRepoIDs(ctx context.Context, ownerID int64, user *user_model.User) ([]int64, error) {
Find(&repoIDs); err != nil { return SearchRepositoryIDsByCondition(ctx, builder.NewCond().And(
return nil, fmt.Errorf("FindUserCodeAccesibleRepoIDs: %v", err) builder.Eq{"owner_id": ownerID},
} AccessibleRepositoryCondition(user, unit.TypeCode),
return repoIDs, nil ))
} }
// GetUserRepositories returns a list of repositories of given user. // GetUserRepositories returns a list of repositories of given user.

View File

@ -130,6 +130,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["IsPublicMember"] = func(uid int64) bool { ctx.Data["IsPublicMember"] = func(uid int64) bool {
is, _ := organization.IsPublicMembership(ctx.Org.Organization.ID, uid) is, _ := organization.IsPublicMembership(ctx.Org.Organization.ID, uid)
return is return is

View File

@ -268,8 +268,11 @@ users = Users
organizations = Organizations organizations = Organizations
search = Search search = Search
code = Code code = Code
search.type.tooltip = Search type
search.fuzzy = Fuzzy search.fuzzy = Fuzzy
search.fuzzy.tooltip = Include results that also matches the search term closely
search.match = Match search.match = Match
search.match.tooltip = Include only results that matches the exact search term
code_search_unavailable = Currently code search is not available. Please contact your site administrator. code_search_unavailable = Currently code search is not available. Please contact your site administrator.
repo_no_results = No matching repositories found. repo_no_results = No matching repositories found.
user_no_results = No matching users found. user_no_results = No matching users found.
@ -507,6 +510,7 @@ activity = Public Activity
followers = Followers followers = Followers
starred = Starred Repositories starred = Starred Repositories
watched = Watched Repositories watched = Watched Repositories
code = Code
projects = Projects projects = Projects
following = Following following = Following
follow = Follow follow = Follow
@ -1763,8 +1767,11 @@ activity.git_stats_deletion_n = %d deletions
search = Search search = Search
search.search_repo = Search repository search.search_repo = Search repository
search.type.tooltip = Search type
search.fuzzy = Fuzzy search.fuzzy = Fuzzy
search.fuzzy.tooltip = Include results that also matches the search term closely
search.match = Match search.match = Match
search.match.tooltip = Include only results that matches the exact search term
search.results = Search results for "%s" in <a href="%s">%s</a> search.results = Search results for "%s" in <a href="%s">%s</a>
search.code_no_results = No source code matching your search term found. search.code_no_results = No source code matching your search term found.
search.code_search_unavailable = Currently code search is not available. Please contact your site administrator. search.code_search_unavailable = Currently code search is not available. Please contact your site administrator.
@ -2310,6 +2317,7 @@ create_org = Create Organization
repo_updated = Updated repo_updated = Updated
people = People people = People
teams = Teams teams = Teams
code = Code
lower_members = members lower_members = members
lower_repositories = repositories lower_repositories = repositories
create_new_team = New Team create_new_team = New Team

View File

@ -34,86 +34,91 @@ func Code(ctx *context.Context) {
language := ctx.FormTrim("l") language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q") keyword := ctx.FormTrim("q")
queryType := ctx.FormTrim("t")
isMatch := queryType == "match"
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["PageIsViewCode"] = true
if keyword == "" {
ctx.HTML(http.StatusOK, tplExploreCode)
return
}
page := ctx.FormInt("page") page := ctx.FormInt("page")
if page <= 0 { if page <= 0 {
page = 1 page = 1
} }
queryType := ctx.FormTrim("t") var (
isMatch := queryType == "match" repoIDs []int64
err error
if keyword != "" { isAdmin bool
var ( )
repoIDs []int64 if ctx.Doer != nil {
err error isAdmin = ctx.Doer.IsAdmin
isAdmin bool
)
if ctx.Doer != nil {
isAdmin = ctx.Doer.IsAdmin
}
// guest user or non-admin user
if ctx.Doer == nil || !isAdmin {
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx.Doer)
if err != nil {
ctx.ServerError("SearchResults", err)
return
}
}
var (
total int
searchResults []*code_indexer.Result
searchResultLanguages []*code_indexer.SearchResultLanguages
)
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
if err != nil {
if code_indexer.IsAvailable() {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["CodeIndexerUnavailable"] = true
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
if err != nil {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["RepoMaps"] = repoMaps
}
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages
ctx.Data["PageIsViewCode"] = true
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "l", "Language")
ctx.Data["Page"] = pager
} }
// guest user or non-admin user
if ctx.Doer == nil || !isAdmin {
repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer)
if err != nil {
ctx.ServerError("FindUserCodeAccessibleRepoIDs", err)
return
}
}
var (
total int
searchResults []*code_indexer.Result
searchResultLanguages []*code_indexer.SearchResultLanguages
)
if (len(repoIDs) > 0) || isAdmin {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
if err != nil {
if code_indexer.IsAvailable() {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["CodeIndexerUnavailable"] = true
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
if err != nil {
ctx.ServerError("GetRepositoriesMapByIDs", err)
return
}
ctx.Data["RepoMaps"] = repoMaps
}
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "l", "Language")
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplExploreCode) ctx.HTML(http.StatusOK, tplExploreCode)
} }

View File

@ -21,14 +21,27 @@ func Search(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink) ctx.Redirect(ctx.Repo.RepoLink)
return return
} }
language := ctx.FormTrim("l") language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q") keyword := ctx.FormTrim("q")
queryType := ctx.FormTrim("t")
isMatch := queryType == "match"
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["PageIsViewCode"] = true
if keyword == "" {
ctx.HTML(http.StatusOK, tplSearch)
return
}
page := ctx.FormInt("page") page := ctx.FormInt("page")
if page <= 0 { if page <= 0 {
page = 1 page = 1
} }
queryType := ctx.FormTrim("t")
isMatch := queryType == "match"
total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID}, total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID},
language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
@ -41,13 +54,10 @@ func Search(ctx *context.Context) {
} else { } else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable() ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
} }
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["SourcePath"] = ctx.Repo.Repository.HTMLURL() ctx.Data["SourcePath"] = ctx.Repo.Repository.HTMLURL()
ctx.Data["SearchResults"] = searchResults ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages ctx.Data["SearchResultLanguages"] = searchResultLanguages
ctx.Data["PageIsViewCode"] = true
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)

114
routers/web/user/code.go Normal file
View File

@ -0,0 +1,114 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package user
import (
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/setting"
)
const (
tplUserCode base.TplName = "user/code"
)
// CodeSearch render user/organization code search page
func CodeSearch(ctx *context.Context) {
if !setting.Indexer.RepoIndexerEnabled {
ctx.Redirect(ctx.ContextUser.HomeLink())
return
}
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["Title"] = ctx.Tr("code.title")
ctx.Data["ContextUser"] = ctx.ContextUser
language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q")
queryType := ctx.FormTrim("t")
isMatch := queryType == "match"
ctx.Data["Keyword"] = keyword
ctx.Data["Language"] = language
ctx.Data["queryType"] = queryType
ctx.Data["IsCodePage"] = true
if keyword == "" {
ctx.HTML(http.StatusOK, tplUserCode)
return
}
var (
repoIDs []int64
err error
)
page := ctx.FormInt("page")
if page <= 0 {
page = 1
}
repoIDs, err = repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer)
if err != nil {
ctx.ServerError("FindUserCodeAccessibleOwnerRepoIDs", err)
return
}
var (
total int
searchResults []*code_indexer.Result
searchResultLanguages []*code_indexer.SearchResultLanguages
)
if len(repoIDs) > 0 {
total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
if err != nil {
if code_indexer.IsAvailable() {
ctx.ServerError("SearchResults", err)
return
}
ctx.Data["CodeIndexerUnavailable"] = true
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
loadRepoIDs := make([]int64, 0, len(searchResults))
for _, result := range searchResults {
var find bool
for _, id := range loadRepoIDs {
if id == result.RepoID {
find = true
break
}
}
if !find {
loadRepoIDs = append(loadRepoIDs, result.RepoID)
}
}
repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
if err != nil {
ctx.ServerError("GetRepositoriesMapByIDs", err)
return
}
ctx.Data["RepoMaps"] = repoMaps
}
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "l", "Language")
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplUserCode)
}

View File

@ -86,6 +86,7 @@ func ListPackages(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("packages.title") ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["IsPackagesPage"] = true ctx.Data["IsPackagesPage"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["Query"] = query ctx.Data["Query"] = query
ctx.Data["PackageType"] = packageType ctx.Data["PackageType"] = packageType
@ -157,6 +158,7 @@ func ViewPackageVersion(ctx *context.Context) {
ctx.Data["Title"] = pd.Package.Name ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true ctx.Data["IsPackagesPage"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = pd ctx.Data["PackageDescriptor"] = pd
@ -234,6 +236,7 @@ func ListPackageVersions(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("packages.title") ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["IsPackagesPage"] = true ctx.Data["IsPackagesPage"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{ ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
Package: p, Package: p,
@ -305,6 +308,7 @@ func PackageSettings(ctx *context.Context) {
ctx.Data["Title"] = pd.Package.Name ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true ctx.Data["IsPackagesPage"] = true
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = pd ctx.Data["PackageDescriptor"] = pd

View File

@ -290,6 +290,7 @@ func Profile(ctx *context.Context) {
} }
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.Doer.ID) ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.Doer.ID)

View File

@ -754,6 +754,7 @@ func RegisterRoutes(m *web.Route) {
}) })
}, ignSignIn, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) }, ignSignIn, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
} }
m.Get("/code", user.CodeSearch)
}, context_service.UserAssignmentWeb()) }, context_service.UserAssignmentWeb())
// ***** Release Attachment Download without Signin // ***** Release Attachment Download without Signin

View File

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

View File

@ -0,0 +1,43 @@
<div class="df ac fw">
{{range $term := .SearchResultLanguages}}
<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}}">
<i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i>
{{$term.Language}}
<div class="detail">{{$term.Count}}</div>
</a>
{{end}}
</div>
<div class="repository search">
{{range $result := .SearchResults}}
{{$repo := (index $.RepoMaps .RepoID)}}
<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
<h4 class="ui top attached normal header">
<span class="file">
<a rel="nofollow" href="{{$repo.HTMLURL}}">{{$repo.FullName}}</a>
{{if $repo.IsArchived}}
<span class="ui basic label">{{$.locale.Tr "repo.desc.archived"}}</span>
{{end}}
- {{.Filename}}
</span>
<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>
</h4>
<div class="ui attached table segment">
<div class="file-body file-code code-view">
<table>
<tbody>
<tr>
<td class="lines-num">
{{range .LineNumbers}}
<a href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a>
{{end}}
</td>
<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td>
</tr>
</tbody>
</table>
</div>
</div>
{{template "shared/searchbottom" dict "root" $ "result" .}}
</div>
{{end}}
</div>

View File

@ -2,20 +2,7 @@
<div class="page-content explore users"> <div class="page-content explore users">
{{template "explore/navbar" .}} {{template "explore/navbar" .}}
<div class="ui container"> <div class="ui container">
<form class="ui form ignore-dirty" style="max-width: 100%"> {{template "code/searchform" .}}
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "explore.search"}}..." autofocus>
<div class="ui dropdown selection{{if .CodeIndexerUnavailable}} disabled{{end}}">
<input name="t" type="hidden" value="{{.queryType}}"{{if .CodeIndexerUnavailable}} disabled{{end}}>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="text">{{.locale.Tr (printf "explore.search.%s" (or .queryType "fuzzy"))}}</div>
<div class="menu transition hidden" tabindex="-1" style="display: block !important;">
<div class="item" data-value="">{{.locale.Tr "explore.search.fuzzy"}}</div>
<div class="item" data-value="match">{{.locale.Tr "explore.search.match"}}</div>
</div>
</div>
<button class="ui primary button"{{if .CodeIndexerUnavailable}} disabled{{end}}>{{.locale.Tr "explore.search"}}</button>
</div>
</form>
<div class="ui divider"></div> <div class="ui divider"></div>
<div class="ui user list"> <div class="ui user list">
{{if .CodeIndexerUnavailable}} {{if .CodeIndexerUnavailable}}
@ -26,50 +13,8 @@
<h3> <h3>
{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}} {{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}}
</h3> </h3>
<div class="df ac fw"> {{template "code/searchresults" .}}
{{range $term := .SearchResultLanguages}} {{else if .Keyword}}
<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}}">
<i class="color-icon mr-3" style="background-color: {{$term.Color}}"></i>
{{$term.Language}}
<div class="detail">{{$term.Count}}</div>
</a>
{{end}}
</div>
<div class="repository search">
{{range $result := .SearchResults}}
{{$repo := (index $.RepoMaps .RepoID)}}
<div class="diff-file-box diff-box file-content non-diff-file-content repo-search-result">
<h4 class="ui top attached normal header">
<span class="file">
<a rel="nofollow" href="{{$repo.HTMLURL}}">{{$repo.FullName}}</a>
{{if $repo.IsArchived}}
<span class="ui basic label">{{$.locale.Tr "repo.desc.archived"}}</span>
{{end}}
- {{.Filename}}
</span>
<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>
</h4>
<div class="ui attached table segment">
<div class="file-body file-code code-view">
<table>
<tbody>
<tr>
<td class="lines-num">
{{range .LineNumbers}}
<a href="{{$repo.HTMLURL}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a>
{{end}}
</td>
<td class="lines-code chroma"><code class="code-inner">{{.FormattedLines | Safe}}</code></td>
</tr>
</tbody>
</table>
</div>
</div>
{{template "shared/searchbottom" dict "root" $ "result" .}}
</div>
{{end}}
</div>
{{else}}
<div>{{$.locale.Tr "explore.code_no_results"}}</div> <div>{{$.locale.Tr "explore.code_no_results"}}</div>
{{end}} {{end}}
</div> </div>

View File

@ -25,7 +25,7 @@
<input type="hidden" name="sort" value="{{$.SortType}}"> <input type="hidden" name="sort" value="{{$.SortType}}">
<input type="hidden" name="language" value="{{$.Language}}"> <input type="hidden" name="language" value="{{$.Language}}">
<div class="ui fluid action input"> <div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..." autofocus> <input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}" autofocus>
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button> <button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
</div> </div>
</form> </form>

View File

@ -17,7 +17,7 @@
</div> </div>
<form class="ui form ignore-dirty" style="max-width: 90%"> <form class="ui form ignore-dirty" style="max-width: 90%">
<div class="ui fluid action input"> <div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}..." autofocus> <input name="q" value="{{.Keyword}}" placeholder="{{.locale.Tr "explore.search"}}" autofocus>
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button> <button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
</div> </div>
</form> </form>

View File

@ -1,6 +1,6 @@
<div class="ui tabs container"> <div class="ui tabs container">
<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"}}
</a> </a>
{{if .IsPackageEnabled}} {{if .IsPackageEnabled}}
@ -8,14 +8,19 @@
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
</a> </a>
{{end}} {{end}}
{{if .IsRepoIndexerEnabled}}
<a class="{{if $.PageIsOrgCode}}active {{end}}item" href="{{$.Org.HomeLink}}/-/code">
{{svg "octicon-code"}}&nbsp;{{$.locale.Tr "org.code"}}
</a>
{{end}}
{{if .IsOrganizationMember}} {{if .IsOrganizationMember}}
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> <a class="{{if $.PageIsOrgMembers}}active {{end}}item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization"}}&nbsp;{{$.locale.Tr "org.people"}} {{svg "octicon-organization"}}&nbsp;{{$.locale.Tr "org.people"}}
{{if .NumMembers}} {{if .NumMembers}}
<div class="ui primary label">{{.NumMembers}}</div> <div class="ui primary label">{{.NumMembers}}</div>
{{end}} {{end}}
</a> </a>
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams"> <a class="{{if $.PageIsOrgTeams}}active {{end}}item" href="{{$.OrgLink}}/teams">
{{svg "octicon-people"}}&nbsp;{{$.locale.Tr "org.teams"}} {{svg "octicon-people"}}&nbsp;{{$.locale.Tr "org.teams"}}
{{if .NumTeams}} {{if .NumTeams}}
<div class="ui primary label">{{.NumTeams}}</div> <div class="ui primary label">{{.NumTeams}}</div>
@ -25,7 +30,7 @@
{{if .IsOrganizationOwner}} {{if .IsOrganizationOwner}}
<div class="right menu"> <div class="right menu">
<a class="{{if .PageIsOrgSettings}}active{{end}} item" href="{{.OrgLink}}/settings"> <a class="{{if .PageIsOrgSettings}}active {{end}}item" href="{{.OrgLink}}/settings">
{{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}} {{svg "octicon-tools"}} {{.locale.Tr "repo.settings"}}
</a> </a>
</div> </div>

View File

@ -6,12 +6,12 @@
<form class="ui form ignore-dirty" method="get"> <form class="ui form ignore-dirty" method="get">
<div class="ui fluid action input"> <div class="ui fluid action input">
<input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "repo.search.search_repo"}}"> <input name="q" value="{{.Keyword}}"{{if .CodeIndexerUnavailable}} disabled{{end}} placeholder="{{.locale.Tr "repo.search.search_repo"}}">
<div class="ui dropdown selection{{if .CodeIndexerUnavailable}} disabled{{end}}"> <div class="ui dropdown selection tooltip{{if .CodeIndexerUnavailable}} disabled{{end}}" data-content="{{.locale.Tr "repo.search.type.tooltip"}}">
<input name="t" type="hidden"{{if .CodeIndexerUnavailable}} disabled{{end}} value="{{.queryType}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}} <input name="t" type="hidden"{{if .CodeIndexerUnavailable}} disabled{{end}} value="{{.queryType}}">{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="text">{{.locale.Tr (printf "repo.search.%s" (or .queryType "fuzzy"))}}</div> <div class="text">{{.locale.Tr (printf "repo.search.%s" (or .queryType "fuzzy"))}}</div>
<div class="menu transition hidden" tabindex="-1" style="display: block !important;"> <div class="menu transition hidden" tabindex="-1" style="display: block !important;">
<div class="item" data-value="">{{.locale.Tr "repo.search.fuzzy"}}</div> <div class="item tooltip" data-value="" data-content="{{.locale.Tr "repo.search.fuzzy.tooltip"}}">{{.locale.Tr "repo.search.fuzzy"}}</div>
<div class="item" data-value="match">{{.locale.Tr "repo.search.match"}}</div> <div class="item tooltip" data-value="match" data-content="{{.locale.Tr "repo.search.match.tooltip"}}">{{.locale.Tr "repo.search.match"}}</div>
</div> </div>
</div> </div>
<button class="ui icon button"{{if .CodeIndexerUnavailable}} disabled{{end}} type="submit">{{svg "octicon-search" 16}}</button> <button class="ui icon button"{{if .CodeIndexerUnavailable}} disabled{{end}} type="submit">{{svg "octicon-search" 16}}</button>

25
templates/user/code.tmpl Normal file
View File

@ -0,0 +1,25 @@
{{template "base/head" .}}
<div class="page-content repository code-search">
{{template "user/overview/header" .}}
<div class="ui container">
{{template "code/searchform" .}}
<div class="ui divider"></div>
<div class="ui user list">
{{if .CodeIndexerUnavailable }}
<div class="ui error message">
<p>{{$.locale.Tr "explore.code_search_unavailable"}}</p>
</div>
{{else if .SearchResults}}
<h3>
{{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html }}
</h3>
{{template "code/searchresults" .}}
{{else if .Keyword}}
<div>{{$.locale.Tr "explore.code_no_results"}}</div>
{{end}}
</div>
{{template "base/paginate" .}}
</div>
</div>
{{template "base/footer" .}}

View File

@ -23,10 +23,15 @@
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
</a> </a>
{{if (not .UnitPackagesGlobalDisabled)}} {{if (not .UnitPackagesGlobalDisabled)}}
<a href="{{.ContextUser.HTMLURL}}/-/packages" class="{{if .IsPackagesPage}}active{{end}} item"> <a href="{{.ContextUser.HomeLink}}/-/packages" class="{{if .IsPackagesPage}}active {{end}}item">
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
</a> </a>
{{end}} {{end}}
{{if .IsRepoIndexerEnabled}}
<a href="{{.ContextUser.HomeLink}}/-/code" class="{{if .IsCodePage}}active {{end}}item">
{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
</a>
{{end}}
{{if .ContextUser.IsOrganization}} {{if .ContextUser.IsOrganization}}
{{if .IsOrganizationMember}} {{if .IsOrganizationMember}}

View File

@ -101,7 +101,7 @@
</div> </div>
<div class="ui eleven wide column"> <div class="ui eleven wide column">
<div class="ui secondary stackable pointing tight menu"> <div class="ui secondary stackable pointing tight menu">
<a class='{{if and (ne .TabName "activity") (ne .TabName "following") (ne .TabName "followers") (ne .TabName "stars") (ne .TabName "watching") (ne .TabName "projects")}}active{{end}} item' href="{{.Owner.HomeLink}}"> <a class='{{if and (ne .TabName "activity") (ne .TabName "following") (ne .TabName "followers") (ne .TabName "stars") (ne .TabName "watching") (ne .TabName "projects") (ne .TabName "code")}}active{{end}} item' href="{{.Owner.HomeLink}}">
{{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}}
</a> </a>
{{if .IsPackageEnabled}} {{if .IsPackageEnabled}}
@ -109,6 +109,11 @@
{{svg "octicon-package"}} {{.locale.Tr "packages.title"}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}}
</a> </a>
{{end}} {{end}}
{{if .IsRepoIndexerEnabled}}
<a class='{{if eq .TabName "code"}}active{{end}} item' href="{{.Owner.HomeLink}}/-/code">
{{svg "octicon-code"}} {{.locale.Tr "user.code"}}
</a>
{{end}}
<a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity"> <a class='{{if eq .TabName "activity"}}active{{end}} item' href="{{.Owner.HomeLink}}?tab=activity">
{{svg "octicon-rss"}} {{.locale.Tr "user.activity"}} {{svg "octicon-rss"}} {{.locale.Tr "user.activity"}}
</a> </a>