mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-25 07:34:01 +01:00
Only show relevant repositories on explore page (#19361)
Adds a new option to only show relevant repo's on the explore page, for bigger Gitea instances like Codeberg this is a nice option to enable to make the explore page more populated with unique and "high" quality repo's. A note is shown that the results are filtered and have the possibility to see the unfiltered results. Co-authored-by: vednoc <vednoc@protonmail.com> Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
dc0253b063
commit
27ac65a124
8 changed files with 65 additions and 8 deletions
|
@ -1164,6 +1164,10 @@ ROUTER = console
|
||||||
;;
|
;;
|
||||||
;; Whether to enable a Service Worker to cache frontend assets
|
;; Whether to enable a Service Worker to cache frontend assets
|
||||||
;USE_SERVICE_WORKER = false
|
;USE_SERVICE_WORKER = false
|
||||||
|
;;
|
||||||
|
;; Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
|
||||||
|
;; A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
|
||||||
|
;ONLY_SHOW_RELEVANT_REPOS = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -194,6 +194,8 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
||||||
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
|
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
|
||||||
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
|
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
|
||||||
- `USE_SERVICE_WORKER`: **false**: Whether to enable a Service Worker to cache frontend assets.
|
- `USE_SERVICE_WORKER`: **false**: Whether to enable a Service Worker to cache frontend assets.
|
||||||
|
- `ONLY_SHOW_RELEVANT_REPOS`: **false** Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
|
||||||
|
A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
|
||||||
|
|
||||||
### UI - Admin (`ui.admin`)
|
### UI - Admin (`ui.admin`)
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,10 @@ type SearchRepoOptions struct {
|
||||||
HasMilestones util.OptionalBool
|
HasMilestones util.OptionalBool
|
||||||
// LowerNames represents valid lower names to restrict to
|
// LowerNames represents valid lower names to restrict to
|
||||||
LowerNames []string
|
LowerNames []string
|
||||||
|
// When specified true, apply some filters over the conditions:
|
||||||
|
// - Don't show forks, when opts.Fork is OptionalBoolNone.
|
||||||
|
// - Do not display repositories that don't have a description, an icon and topics.
|
||||||
|
OnlyShowRelevant bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchOrderBy is used to sort the result
|
// SearchOrderBy is used to sort the result
|
||||||
|
@ -463,8 +467,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
||||||
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
|
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Fork != util.OptionalBoolNone {
|
if opts.Fork != util.OptionalBoolNone || opts.OnlyShowRelevant {
|
||||||
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
|
if opts.OnlyShowRelevant && opts.Fork == util.OptionalBoolNone {
|
||||||
|
cond = cond.And(builder.Eq{"is_fork": false})
|
||||||
|
} else {
|
||||||
|
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.Mirror != util.OptionalBoolNone {
|
if opts.Mirror != util.OptionalBoolNone {
|
||||||
|
@ -486,6 +494,25 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
||||||
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
|
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.OnlyShowRelevant {
|
||||||
|
// Only show a repo that either has a topic or description.
|
||||||
|
subQueryCond := builder.NewCond()
|
||||||
|
|
||||||
|
// Topic checking. Topics is non-null.
|
||||||
|
subQueryCond = subQueryCond.Or(builder.And(builder.Neq{"topics": "null"}, builder.Neq{"topics": "[]"}))
|
||||||
|
|
||||||
|
// Description checking. Description not empty.
|
||||||
|
subQueryCond = subQueryCond.Or(builder.Neq{"description": ""})
|
||||||
|
|
||||||
|
// Repo has a avatar.
|
||||||
|
subQueryCond = subQueryCond.Or(builder.Neq{"avatar": ""})
|
||||||
|
|
||||||
|
// Always hide repo's that are empty.
|
||||||
|
subQueryCond = subQueryCond.And(builder.Eq{"is_empty": false})
|
||||||
|
|
||||||
|
cond = cond.And(subQueryCond)
|
||||||
|
}
|
||||||
|
|
||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,7 @@ var (
|
||||||
CustomEmojisMap map[string]string `ini:"-"`
|
CustomEmojisMap map[string]string `ini:"-"`
|
||||||
SearchRepoDescription bool
|
SearchRepoDescription bool
|
||||||
UseServiceWorker bool
|
UseServiceWorker bool
|
||||||
|
OnlyShowRelevantRepos bool
|
||||||
|
|
||||||
Notification struct {
|
Notification struct {
|
||||||
MinTimeout time.Duration
|
MinTimeout time.Duration
|
||||||
|
@ -1087,6 +1088,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||||
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
|
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
|
||||||
UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
|
UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
|
||||||
UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(false)
|
UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(false)
|
||||||
|
UI.OnlyShowRelevantRepos = Cfg.Section("ui").Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false)
|
||||||
|
|
||||||
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
|
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -277,6 +277,9 @@ org_no_results = No matching organizations found.
|
||||||
code_no_results = No source code matching your search term found.
|
code_no_results = No source code matching your search term found.
|
||||||
code_search_results = Search results for '%s'
|
code_search_results = Search results for '%s'
|
||||||
code_last_indexed_at = Last indexed %s
|
code_last_indexed_at = Last indexed %s
|
||||||
|
relevant_repositories_tooltip = Repositories that are forks or that have no topic, no icon, and no description are hidden.
|
||||||
|
relevant_repositories = Only relevant repositories are being shown, <a href="%s">show unfiltered results</a>.
|
||||||
|
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
create_new_account = Register Account
|
create_new_account = Register Account
|
||||||
|
|
|
@ -48,10 +48,11 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
repos []*repo_model.Repository
|
repos []*repo_model.Repository
|
||||||
count int64
|
count int64
|
||||||
err error
|
err error
|
||||||
orderBy db.SearchOrderBy
|
orderBy db.SearchOrderBy
|
||||||
|
onlyShowRelevant bool
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx.Data["SortType"] = ctx.FormString("sort")
|
ctx.Data["SortType"] = ctx.FormString("sort")
|
||||||
|
@ -60,8 +61,6 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
orderBy = db.SearchOrderByNewest
|
orderBy = db.SearchOrderByNewest
|
||||||
case "oldest":
|
case "oldest":
|
||||||
orderBy = db.SearchOrderByOldest
|
orderBy = db.SearchOrderByOldest
|
||||||
case "recentupdate":
|
|
||||||
orderBy = db.SearchOrderByRecentUpdated
|
|
||||||
case "leastupdate":
|
case "leastupdate":
|
||||||
orderBy = db.SearchOrderByLeastUpdated
|
orderBy = db.SearchOrderByLeastUpdated
|
||||||
case "reversealphabetically":
|
case "reversealphabetically":
|
||||||
|
@ -83,9 +82,16 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
default:
|
default:
|
||||||
ctx.Data["SortType"] = "recentupdate"
|
ctx.Data["SortType"] = "recentupdate"
|
||||||
orderBy = db.SearchOrderByRecentUpdated
|
orderBy = db.SearchOrderByRecentUpdated
|
||||||
|
onlyShowRelevant = setting.UI.OnlyShowRelevantRepos && !ctx.FormBool("no_filter")
|
||||||
}
|
}
|
||||||
|
|
||||||
keyword := ctx.FormTrim("q")
|
keyword := ctx.FormTrim("q")
|
||||||
|
if keyword != "" {
|
||||||
|
onlyShowRelevant = false
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["OnlyShowRelevant"] = onlyShowRelevant
|
||||||
|
|
||||||
topicOnly := ctx.FormBool("topic")
|
topicOnly := ctx.FormBool("topic")
|
||||||
ctx.Data["TopicOnly"] = topicOnly
|
ctx.Data["TopicOnly"] = topicOnly
|
||||||
|
|
||||||
|
@ -107,6 +113,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
TopicOnly: topicOnly,
|
TopicOnly: topicOnly,
|
||||||
Language: language,
|
Language: language,
|
||||||
IncludeDescription: setting.UI.SearchRepoDescription,
|
IncludeDescription: setting.UI.SearchRepoDescription,
|
||||||
|
OnlyShowRelevant: onlyShowRelevant,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("SearchRepository", err)
|
ctx.ServerError("SearchRepository", err)
|
||||||
|
@ -133,6 +140,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
|
||||||
pager.SetDefaultParams(ctx)
|
pager.SetDefaultParams(ctx)
|
||||||
pager.AddParam(ctx, "topic", "TopicOnly")
|
pager.AddParam(ctx, "topic", "TopicOnly")
|
||||||
pager.AddParam(ctx, "language", "Language")
|
pager.AddParam(ctx, "language", "Language")
|
||||||
|
pager.AddParamString("no_filter", ctx.FormString("no_filter"))
|
||||||
ctx.Data["Page"] = pager
|
ctx.Data["Page"] = pager
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, opts.TplName)
|
ctx.HTML(http.StatusOK, opts.TplName)
|
||||||
|
|
|
@ -29,4 +29,9 @@
|
||||||
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
|
<button class="ui primary button">{{.locale.Tr "explore.search"}}</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{{if .OnlyShowRelevant}}
|
||||||
|
<div class="ui blue attached message explore-relevancy-note">
|
||||||
|
<span class="ui tooltip" data-content="{{.locale.Tr "explore.relevant_repositories_tooltip"}}">{{.locale.Tr "explore.relevant_repositories" ((printf "%s%s" $.Link "?no_filter=1")|Escape) | Safe}}</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
|
|
@ -89,3 +89,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui.explore-relevancy-note {
|
||||||
|
border-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue