1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-04-18 00:47:48 -04:00

Add DISABLE_ORGANIZATIONS_PAGE and DISABLE_CODE_PAGE settings for explore pages and fix an issue related to user search ()

These settings can allow users to only display the repositories explore page.

Thanks to yp05327 and wxiaoguang !

---------

Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Zettat123 2024-10-22 13:09:19 +08:00 committed by GitHub
parent 3d6ccbac3f
commit 9206fbb55f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 74 additions and 49 deletions
custom/conf
modules/setting
routers
templates/explore
web_src/js/features/comp

@ -907,6 +907,24 @@ LEVEL = Info
;; Valid site url schemes for user profiles ;; Valid site url schemes for user profiles
;VALID_SITE_URL_SCHEMES=http,https ;VALID_SITE_URL_SCHEMES=http,https
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[service.explore]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Only allow signed in users to view the explore pages.
;REQUIRE_SIGNIN_VIEW = false
;;
;; Disable the users explore page.
;DISABLE_USERS_PAGE = false
;;
;; Disable the organizations explore page.
;DISABLE_ORGANIZATIONS_PAGE = false
;;
;; Disable the code explore page.
;DISABLE_CODE_PAGE = false
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

@ -90,8 +90,10 @@ var Service = struct {
// Explore page settings // Explore page settings
Explore struct { Explore struct {
RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"`
DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"` DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"`
DisableOrganizationsPage bool `ini:"DISABLE_ORGANIZATIONS_PAGE"`
DisableCodePage bool `ini:"DISABLE_CODE_PAGE"`
} `ini:"service.explore"` } `ini:"service.explore"`
}{ }{
AllowedUserVisibilityModesSlice: []bool{true, true, true}, AllowedUserVisibilityModesSlice: []bool{true, true, true},

@ -356,12 +356,20 @@ func reqToken() func(ctx *context.APIContext) {
func reqExploreSignIn() func(ctx *context.APIContext) { func reqExploreSignIn() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { if (setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView) && !ctx.IsSigned {
ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users")
} }
} }
} }
func reqUsersExploreEnabled() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) {
if setting.Service.Explore.DisableUsersPage {
ctx.NotFound()
}
}
}
func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName {
@ -955,7 +963,7 @@ func Routes() *web.Router {
// Users (requires user scope) // Users (requires user scope)
m.Group("/users", func() { m.Group("/users", func() {
m.Get("/search", reqExploreSignIn(), user.Search) m.Get("/search", reqExploreSignIn(), reqUsersExploreEnabled(), user.Search)
m.Group("/{username}", func() { m.Group("/{username}", func() {
m.Get("", reqExploreSignIn(), user.GetInfo) m.Get("", reqExploreSignIn(), user.GetInfo)

@ -21,12 +21,13 @@ const (
// Code render explore code page // Code render explore code page
func Code(ctx *context.Context) { func Code(ctx *context.Context) {
if !setting.Indexer.RepoIndexerEnabled { if !setting.Indexer.RepoIndexerEnabled || setting.Service.Explore.DisableCodePage {
ctx.Redirect(setting.AppSubURL + "/explore") ctx.Redirect(setting.AppSubURL + "/explore")
return return
} }
ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage
ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExplore"] = true

@ -14,7 +14,13 @@ import (
// Organizations render explore organizations page // Organizations render explore organizations page
func Organizations(ctx *context.Context) { func Organizations(ctx *context.Context) {
ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage if setting.Service.Explore.DisableOrganizationsPage {
ctx.Redirect(setting.AppSubURL + "/explore")
return
}
ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage
ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage
ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExplore"] = true
ctx.Data["PageIsExploreOrganizations"] = true ctx.Data["PageIsExploreOrganizations"] = true

@ -165,7 +165,9 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
// Repos render explore repositories page // Repos render explore repositories page
func Repos(ctx *context.Context) { func Repos(ctx *context.Context) {
ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage
ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage
ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage
ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExplore"] = true
ctx.Data["PageIsExploreRepositories"] = true ctx.Data["PageIsExploreRepositories"] = true

@ -131,9 +131,11 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
// Users render explore users page // Users render explore users page
func Users(ctx *context.Context) { func Users(ctx *context.Context) {
if setting.Service.Explore.DisableUsersPage { if setting.Service.Explore.DisableUsersPage {
ctx.Redirect(setting.AppSubURL + "/explore/repos") ctx.Redirect(setting.AppSubURL + "/explore")
return return
} }
ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage
ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage
ctx.Data["Title"] = ctx.Tr("explore") ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true ctx.Data["PageIsExplore"] = true
ctx.Data["PageIsExploreUsers"] = true ctx.Data["PageIsExploreUsers"] = true

@ -8,37 +8,24 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/convert"
) )
// Search search users // SearchCandidates searches candidate users for dropdown list
func Search(ctx *context.Context) { func SearchCandidates(ctx *context.Context) {
listOptions := db.ListOptions{ users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
Page: ctx.FormInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
}
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Keyword: ctx.FormTrim("q"), Keyword: ctx.FormTrim("q"),
UID: ctx.FormInt64("uid"),
Type: user_model.UserTypeIndividual, Type: user_model.UserTypeIndividual,
IsActive: ctx.FormOptionalBool("active"), IsActive: optional.Some(true),
ListOptions: listOptions, ListOptions: db.ListOptions{PageSize: setting.UI.MembersPagingNum},
}) })
if err != nil { if err != nil {
ctx.JSON(http.StatusInternalServerError, map[string]any{ ctx.ServerError("Unable to search users", err)
"ok": false,
"error": err.Error(),
})
return return
} }
ctx.JSON(http.StatusOK, map[string]any{"data": convert.ToUsers(ctx, ctx.Doer, users)})
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, map[string]any{
"ok": true,
"data": convert.ToUsers(ctx, ctx.Doer, users),
})
} }

@ -670,7 +670,7 @@ func registerRoutes(m *web.Router) {
m.Post("/forgot_password", auth.ForgotPasswdPost) m.Post("/forgot_password", auth.ForgotPasswdPost)
m.Post("/logout", auth.SignOut) m.Post("/logout", auth.SignOut)
m.Get("/stopwatches", reqSignIn, user.GetStopwatches) m.Get("/stopwatches", reqSignIn, user.GetStopwatches)
m.Get("/search", ignExploreSignIn, user.Search) m.Get("/search_candidates", ignExploreSignIn, user.SearchCandidates)
m.Group("/oauth2", func() { m.Group("/oauth2", func() {
m.Get("/{provider}", auth.SignInOAuth) m.Get("/{provider}", auth.SignInOAuth)
m.Get("/{provider}/callback", auth.SignInOAuthCallback) m.Get("/{provider}/callback", auth.SignInOAuthCallback)

@ -3,15 +3,17 @@
<a class="{{if .PageIsExploreRepositories}}active {{end}}item" href="{{AppSubUrl}}/explore/repos"> <a class="{{if .PageIsExploreRepositories}}active {{end}}item" href="{{AppSubUrl}}/explore/repos">
{{svg "octicon-repo"}} {{ctx.Locale.Tr "explore.repos"}} {{svg "octicon-repo"}} {{ctx.Locale.Tr "explore.repos"}}
</a> </a>
{{if not .UsersIsDisabled}} {{if not .UsersPageIsDisabled}}
<a class="{{if .PageIsExploreUsers}}active {{end}}item" href="{{AppSubUrl}}/explore/users"> <a class="{{if .PageIsExploreUsers}}active {{end}}item" href="{{AppSubUrl}}/explore/users">
{{svg "octicon-person"}} {{ctx.Locale.Tr "explore.users"}} {{svg "octicon-person"}} {{ctx.Locale.Tr "explore.users"}}
</a> </a>
{{end}} {{end}}
{{if not .OrganizationsPageIsDisabled}}
<a class="{{if .PageIsExploreOrganizations}}active {{end}}item" href="{{AppSubUrl}}/explore/organizations"> <a class="{{if .PageIsExploreOrganizations}}active {{end}}item" href="{{AppSubUrl}}/explore/organizations">
{{svg "octicon-organization"}} {{ctx.Locale.Tr "explore.organizations"}} {{svg "octicon-organization"}} {{ctx.Locale.Tr "explore.organizations"}}
</a> </a>
{{if and (not ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}} {{end}}
{{if and (not ctx.Consts.RepoUnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled (not .CodePageIsDisabled)}}
<a class="{{if .PageIsExploreCode}}active {{end}}item" href="{{AppSubUrl}}/explore/code"> <a class="{{if .PageIsExploreCode}}active {{end}}item" href="{{AppSubUrl}}/explore/code">
{{svg "octicon-code"}} {{ctx.Locale.Tr "explore.code"}} {{svg "octicon-code"}} {{ctx.Locale.Tr "explore.code"}}
</a> </a>

@ -8,41 +8,38 @@ export function initCompSearchUserBox() {
const searchUserBox = document.querySelector('#search-user-box'); const searchUserBox = document.querySelector('#search-user-box');
if (!searchUserBox) return; if (!searchUserBox) return;
const $searchUserBox = $(searchUserBox);
const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true'; const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true';
const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined; const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined;
$searchUserBox.search({ $(searchUserBox).search({
minCharacters: 2, minCharacters: 2,
apiSettings: { apiSettings: {
url: `${appSubUrl}/user/search?active=1&q={query}`, url: `${appSubUrl}/user/search_candidates?q={query}`,
onResponse(response) { onResponse(response) {
const items = []; const resultItems = [];
const searchQuery = $searchUserBox.find('input').val(); const searchQuery = searchUserBox.querySelector('input').value;
const searchQueryUppercase = searchQuery.toUpperCase(); const searchQueryUppercase = searchQuery.toUpperCase();
$.each(response.data, (_i, item) => { for (const item of response.data) {
const resultItem = { const resultItem = {
title: item.login, title: item.login,
image: item.avatar_url, image: item.avatar_url,
description: htmlEscape(item.full_name),
}; };
if (item.full_name) {
resultItem.description = htmlEscape(item.full_name);
}
if (searchQueryUppercase === item.login.toUpperCase()) { if (searchQueryUppercase === item.login.toUpperCase()) {
items.unshift(resultItem); resultItems.unshift(resultItem); // add the exact match to the top
} else { } else {
items.push(resultItem); resultItems.push(resultItem);
} }
}); }
if (allowEmailInput && !items.length && looksLikeEmailAddressCheck.test(searchQuery)) { if (allowEmailInput && !resultItems.length && looksLikeEmailAddressCheck.test(searchQuery)) {
const resultItem = { const resultItem = {
title: searchQuery, title: searchQuery,
description: allowEmailDescription, description: allowEmailDescription,
}; };
items.push(resultItem); resultItems.push(resultItem);
} }
return {results: items}; return {results: resultItems};
}, },
}, },
searchFields: ['login', 'full_name'], searchFields: ['login', 'full_name'],