mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-22 01:44:24 +01:00
Merge pull request '[gitea] week 2024-44 cherry pick (gitea/main -> forgejo)' (#5714) from algernon/wcp/2024-44 into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5714 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
485db0a3ba
|
@ -386,7 +386,7 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
||||||
return a.createAuthSource(ctx, authSource)
|
return a.createAuthSource(ctx, authSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
|
||||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
|
@ -925,6 +925,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
|
||||||
|
;;
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -875,7 +875,7 @@ func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) (IssueList,
|
||||||
return issues, nil
|
return issues, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNewPinnedAllowed returns if a new Issue or Pull request can be pinned
|
// IsNewPinAllowed returns if a new Issue or Pull request can be pinned
|
||||||
func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
|
func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
|
||||||
var maxPin int
|
var maxPin int
|
||||||
_, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
|
_, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
|
||||||
|
|
|
@ -689,7 +689,7 @@ func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest,
|
||||||
return pr, pr.LoadAttributes(ctx)
|
return pr, pr.LoadAttributes(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPullRequestsByBaseHeadInfo returns the pull request by given base and head
|
// GetPullRequestByBaseHeadInfo returns the pull request by given base and head
|
||||||
func GetPullRequestByBaseHeadInfo(ctx context.Context, baseID, headID int64, base, head string) (*PullRequest, error) {
|
func GetPullRequestByBaseHeadInfo(ctx context.Context, baseID, headID int64, base, head string) (*PullRequest, error) {
|
||||||
pr := &PullRequest{}
|
pr := &PullRequest{}
|
||||||
sess := db.GetEngine(ctx).
|
sess := db.GetEngine(ctx).
|
||||||
|
|
|
@ -91,8 +91,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},
|
||||||
|
|
3
release-notes/5714.md
Normal file
3
release-notes/5714.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/d13a4ab5632d6a9697bd0907f9c69ed57d949340) Fixed a bug related to disabling two-factor authentication
|
||||||
|
chore: [commit](https://codeberg.org/forgejo/forgejo/commit/ab26d880932dbc116c43ea277029984c7a6d4e94) Emit a log message when failing to delete an inactive user
|
||||||
|
feat: [commit](https://codeberg.org/forgejo/forgejo/commit/ab660c5944d59cdb4ecc071401445ac9f53cee45) Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages
|
|
@ -395,12 +395,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 {
|
||||||
|
@ -887,7 +895,7 @@ func Routes() *web.Route {
|
||||||
|
|
||||||
// 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),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,9 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
|
||||||
if auth.IsErrTwoFactorNotEnrolled(err) {
|
if auth.IsErrTwoFactorNotEnrolled(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
|
ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
|
||||||
}
|
}
|
||||||
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +65,9 @@ func DisableTwoFactor(ctx *context.Context) {
|
||||||
if auth.IsErrTwoFactorNotEnrolled(err) {
|
if auth.IsErrTwoFactorNotEnrolled(err) {
|
||||||
ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
|
ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
|
||||||
}
|
}
|
||||||
ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +76,9 @@ func DisableTwoFactor(ctx *context.Context) {
|
||||||
// There is a potential DB race here - we must have been disabled by another request in the intervening period
|
// There is a potential DB race here - we must have been disabled by another request in the intervening period
|
||||||
ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
|
ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("SettingsTwoFactor: Failed to DeleteTwoFactorByID", err)
|
||||||
}
|
}
|
||||||
ctx.ServerError("SettingsTwoFactor: Failed to DeleteTwoFactorByID", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -642,7 +642,7 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Post("/logout", auth.SignOut)
|
m.Post("/logout", auth.SignOut)
|
||||||
m.Get("/task/{task}", reqSignIn, user.TaskStatus)
|
m.Get("/task/{task}", reqSignIn, user.TaskStatus)
|
||||||
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)
|
||||||
|
|
|
@ -306,6 +306,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
|
||||||
// Ignore users that were set inactive by admin.
|
// Ignore users that were set inactive by admin.
|
||||||
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) ||
|
if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) ||
|
||||||
models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) {
|
models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) {
|
||||||
|
log.Warn("Inactive user %q has repositories, organizations or packages, skipping deletion: %v", u.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -3,15 +3,18 @@
|
||||||
<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 $.UnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if and (not $.UnitTypeCode.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.getElementById('search-user-box');
|
const searchUserBox = document.getElementById('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'],
|
||||||
|
|
Loading…
Reference in a new issue