mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-19 21:28:33 +02:00
POC: Use gomponents for Explore Users page
This commit is contained in:
parent
b0936f4f41
commit
da75b09c90
25
components/components.go
Normal file
25
components/components.go
Normal file
@ -0,0 +1,25 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/svg"
|
||||
g "maragu.dev/gomponents"
|
||||
)
|
||||
|
||||
func If(condition bool, node g.Node) g.Node {
|
||||
if condition {
|
||||
return node
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SVG(icon string, others ...any) g.Node {
|
||||
return g.Raw(string(svg.RenderHTML(icon)))
|
||||
}
|
||||
|
||||
// Utility to add "active" class if condition is true
|
||||
func classIf(condition bool, class string) string {
|
||||
if condition {
|
||||
return class
|
||||
}
|
||||
return ""
|
||||
}
|
66
components/explore_navbar.go
Normal file
66
components/explore_navbar.go
Normal file
@ -0,0 +1,66 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
type ExploreNavbarProps struct {
|
||||
PageIsExploreRepositories bool
|
||||
UsersPageIsDisabled bool
|
||||
AppSubUrl string
|
||||
PageIsExploreUsers bool
|
||||
PageIsExploreCode bool
|
||||
IsRepoIndexerEnabled bool
|
||||
CodePageIsDisabled bool
|
||||
PageIsExploreOrganizations bool
|
||||
OrganizationsPageIsDisabled bool
|
||||
Locale translation.Locale
|
||||
}
|
||||
|
||||
func ExploreNavbar(data ExploreNavbarProps) g.Node {
|
||||
tr := func(key string) string {
|
||||
return string(data.Locale.Tr(key))
|
||||
}
|
||||
|
||||
isCodeGlobalDisabled := unit.TypeCode.UnitGlobalDisabled()
|
||||
|
||||
return g.El("overflow-menu",
|
||||
gh.Class("ui secondary pointing tabular top attached borderless menu secondary-nav"),
|
||||
gh.Div(
|
||||
gh.Class("overflow-menu-items tw-justify-center"),
|
||||
gh.A(
|
||||
gh.Class(classIf(data.PageIsExploreRepositories, "active ")+"item"),
|
||||
gh.Href(data.AppSubUrl+"/explore/repos"),
|
||||
SVG("octicon-repo"),
|
||||
g.Text(" "+tr("explore.repos")),
|
||||
),
|
||||
If(!data.UsersPageIsDisabled,
|
||||
gh.A(
|
||||
gh.Class(classIf(data.PageIsExploreUsers, "active ")+"item"),
|
||||
gh.Href(data.AppSubUrl+"/explore/users"),
|
||||
SVG("octicon-person"),
|
||||
g.Text(" "+tr("explore.users")),
|
||||
),
|
||||
),
|
||||
If(!data.OrganizationsPageIsDisabled,
|
||||
gh.A(
|
||||
gh.Class(classIf(data.PageIsExploreOrganizations, "active ")+"item"),
|
||||
gh.Href(data.AppSubUrl+"/explore/organizations"),
|
||||
SVG("octicon-organization"),
|
||||
g.Text(" "+tr("explore.organizations")),
|
||||
),
|
||||
),
|
||||
If(!isCodeGlobalDisabled && data.IsRepoIndexerEnabled && !data.CodePageIsDisabled,
|
||||
gh.A(
|
||||
gh.Class(classIf(data.PageIsExploreCode, "active ")+"item"),
|
||||
gh.Href(data.AppSubUrl+"/explore/code"),
|
||||
SVG("octicon-code"),
|
||||
g.Text(" "+tr("explore.code")),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
45
components/explore_search.go
Normal file
45
components/explore_search.go
Normal file
@ -0,0 +1,45 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func ExploreSearchMenu(data ExploreUsersPageProps, pageIsExploreUsers bool) g.Node {
|
||||
// Corresponds to templates/explore/search.tmpl
|
||||
|
||||
tr := func(key string) string {
|
||||
return string(data.Locale.Tr(key))
|
||||
}
|
||||
|
||||
return g.Group([]g.Node{
|
||||
gh.Div(
|
||||
gh.Class("ui small secondary filter menu tw-items-center tw-mx-0"),
|
||||
gh.Form(
|
||||
gh.Class("ui form ignore-dirty tw-flex-1"),
|
||||
If(pageIsExploreUsers,
|
||||
SearchCombo(data.Locale, data.Keyword, tr("search.user_kind")),
|
||||
),
|
||||
If(!pageIsExploreUsers,
|
||||
SearchCombo(data.Locale, data.Keyword, tr("search.org_kind")),
|
||||
),
|
||||
),
|
||||
gh.Div(
|
||||
gh.Class("ui small dropdown type jump item tw-mr-0"),
|
||||
gh.Span(
|
||||
gh.Class("text"),
|
||||
g.Text(tr("repo.issues.filter_sort")),
|
||||
),
|
||||
SVG("octicon-triangle-down", 14, "dropdown icon"),
|
||||
gh.Div(
|
||||
gh.Class("menu"),
|
||||
SortOption(data, "newest", tr("repo.issues.filter_sort.latest")),
|
||||
SortOption(data, "oldest", tr("repo.issues.filter_sort.oldest")),
|
||||
SortOption(data, "alphabetically", tr("repo.issues.label.filter_sort.alphabetically")),
|
||||
SortOption(data, "reversealphabetically", tr("repo.issues.label.filter_sort.reverse_alphabetically")),
|
||||
),
|
||||
),
|
||||
),
|
||||
gh.Div(gh.Class("divider")),
|
||||
})
|
||||
}
|
62
components/explore_users_page.go
Normal file
62
components/explore_users_page.go
Normal file
@ -0,0 +1,62 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
type ExploreUsersPageProps struct {
|
||||
Title string
|
||||
Locale translation.Locale
|
||||
Keyword string
|
||||
SortType string
|
||||
Users []*user.User
|
||||
// ContextUser *user.User
|
||||
Context *context.Context
|
||||
IsSigned bool
|
||||
}
|
||||
|
||||
func ExploreUsersPage(data ExploreUsersPageProps) g.Node {
|
||||
// pageIsExplore := true
|
||||
pageIsExploreUsers := true
|
||||
|
||||
head, err := data.Context.HTMLPartial(200, "base/head")
|
||||
if err != nil {
|
||||
panic("could not render head")
|
||||
}
|
||||
|
||||
footer, err := data.Context.HTMLPartial(200, "base/footer")
|
||||
if err != nil {
|
||||
panic("could not render footer")
|
||||
}
|
||||
|
||||
return g.Group([]g.Node{
|
||||
g.Raw(head),
|
||||
gh.Div(
|
||||
gh.Role("main"),
|
||||
gh.Aria("label", data.Title),
|
||||
gh.Class("page-content explore users"),
|
||||
ExploreNavbar(ExploreNavbarProps{
|
||||
Locale: data.Locale,
|
||||
PageIsExploreUsers: pageIsExploreUsers,
|
||||
}),
|
||||
gh.Div(
|
||||
gh.Class("ui container"),
|
||||
ExploreSearchMenu(data, true),
|
||||
UserList(UserListProps{
|
||||
// ContextUser: data.ContextUser,
|
||||
Context: data.Context,
|
||||
Users: data.Users,
|
||||
IsSigned: data.IsSigned,
|
||||
Locale: data.Locale,
|
||||
PageIsAdminUsers: false,
|
||||
}),
|
||||
// Pagination(data),
|
||||
),
|
||||
),
|
||||
g.Raw(footer),
|
||||
})
|
||||
}
|
32
components/search_button.go
Normal file
32
components/search_button.go
Normal file
@ -0,0 +1,32 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func SearchButton(disabled bool, tooltip string) g.Node {
|
||||
// Corresponds to templates/shared/search/button.tmpl
|
||||
|
||||
class := "ui icon button"
|
||||
if disabled {
|
||||
class += " disabled"
|
||||
}
|
||||
|
||||
btn := gh.Button(
|
||||
gh.Type("submit"),
|
||||
gh.Class(class),
|
||||
SVG("octicon-search", 16),
|
||||
)
|
||||
|
||||
if tooltip != "" {
|
||||
btn = gh.Button(
|
||||
gh.Type("submit"),
|
||||
gh.Class(class),
|
||||
g.Attr("data-tooltip-content", tooltip),
|
||||
SVG("octicon-search", 16),
|
||||
)
|
||||
}
|
||||
|
||||
return btn
|
||||
}
|
19
components/search_combo.go
Normal file
19
components/search_combo.go
Normal file
@ -0,0 +1,19 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func SearchCombo(locale translation.Locale, value, placeholder string) g.Node {
|
||||
// Corresponds to templates/shared/search/combo.tmpl
|
||||
|
||||
disabled := false
|
||||
return gh.Div(
|
||||
gh.Class("ui small fluid action input"),
|
||||
SearchInput(value, placeholder, disabled),
|
||||
// TODO SearchModeDropdown
|
||||
SearchButton(disabled, ""),
|
||||
)
|
||||
}
|
20
components/search_input.go
Normal file
20
components/search_input.go
Normal file
@ -0,0 +1,20 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func SearchInput(value, placeholder string, disabled bool) g.Node {
|
||||
// Corresponds to templates/shared/search/input.tmpl
|
||||
|
||||
return gh.Input(
|
||||
gh.Type("search"),
|
||||
gh.Name("q"),
|
||||
gh.MaxLength("255"),
|
||||
g.Attr("spellcheck", "false"),
|
||||
gh.Value(value),
|
||||
gh.Placeholder(placeholder),
|
||||
If(disabled, gh.Disabled()),
|
||||
)
|
||||
}
|
21
components/sort_option.go
Normal file
21
components/sort_option.go
Normal file
@ -0,0 +1,21 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
func SortOption(data ExploreUsersPageProps, sortType, label string) g.Node {
|
||||
active := ""
|
||||
if data.SortType == sortType {
|
||||
active = "active "
|
||||
}
|
||||
return gh.A(
|
||||
gh.Class(active+"item"),
|
||||
gh.Href(fmt.Sprintf("?sort=%s&q=%s", sortType, url.QueryEscape(data.Keyword))),
|
||||
g.Text(label),
|
||||
)
|
||||
}
|
92
components/user_list.go
Normal file
92
components/user_list.go
Normal file
@ -0,0 +1,92 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
templates "code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
type UserListProps struct {
|
||||
Users []*user.User
|
||||
// ContextUser *user.User
|
||||
IsSigned bool
|
||||
PageIsAdminUsers bool
|
||||
Locale translation.Locale
|
||||
Context *context.Context
|
||||
}
|
||||
|
||||
func UserList(data UserListProps) g.Node {
|
||||
tr := func(key string, args ...any) string {
|
||||
return string(data.Locale.Tr(key, args...))
|
||||
}
|
||||
|
||||
if len(data.Users) == 0 {
|
||||
return gh.Div(
|
||||
gh.Class("flex-list"),
|
||||
gh.Div(
|
||||
gh.Class("flex-item"),
|
||||
g.Text(tr("search.no_results")),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return gh.Div(
|
||||
gh.Class("flex-list"),
|
||||
g.Group(g.Map(data.Users, func(u *user.User) g.Node {
|
||||
utils := templates.NewAvatarUtils(data.Context)
|
||||
|
||||
return gh.Div(
|
||||
gh.Class("flex-item tw-items-center"),
|
||||
gh.Div(
|
||||
gh.Class("flex-item-leading"),
|
||||
g.Raw(string(utils.Avatar(u, 48))),
|
||||
),
|
||||
gh.Div(
|
||||
gh.Class("flex-item-main"),
|
||||
gh.Div(
|
||||
gh.Class("flex-item-title"),
|
||||
UserName(UserNameProps{
|
||||
Locale: data.Locale,
|
||||
User: u,
|
||||
}),
|
||||
If(u.Visibility.IsPrivate(),
|
||||
gh.Span(
|
||||
gh.Class("ui basic tiny label"),
|
||||
g.Text(tr("repo.desc.private")),
|
||||
),
|
||||
),
|
||||
),
|
||||
gh.Div(
|
||||
gh.Class("flex-item-body"),
|
||||
If(u.Location != "",
|
||||
gh.Span(
|
||||
gh.Class("flex-text-inline"),
|
||||
SVG("octicon-location", 16),
|
||||
g.Text(u.Location),
|
||||
),
|
||||
),
|
||||
If(u.Email != "" && (data.PageIsAdminUsers || (setting.UI.ShowUserEmail && data.IsSigned && !u.KeepEmailPrivate)),
|
||||
gh.Span(
|
||||
gh.Class("flex-text-inline"),
|
||||
SVG("octicon-mail", 16),
|
||||
gh.A(
|
||||
gh.Href("mailto:"+u.Email),
|
||||
g.Text(u.Email),
|
||||
),
|
||||
),
|
||||
),
|
||||
gh.Span(
|
||||
gh.Class("flex-text-inline"),
|
||||
SVG("octicon-calendar", 16),
|
||||
g.Raw(tr("user.joined_on", templates.NewDateUtils().AbsoluteShort(u.CreatedUnix))),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
})),
|
||||
)
|
||||
}
|
26
components/user_name.go
Normal file
26
components/user_name.go
Normal file
@ -0,0 +1,26 @@
|
||||
package components
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
g "maragu.dev/gomponents"
|
||||
gh "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
type UserNameProps struct {
|
||||
User *user.User
|
||||
Locale translation.Locale
|
||||
}
|
||||
|
||||
func UserName(data UserNameProps) g.Node {
|
||||
return gh.A(
|
||||
gh.Class("text muted"),
|
||||
gh.Href(data.User.HomeLink()),
|
||||
g.Group([]g.Node{
|
||||
g.Text(data.User.Name),
|
||||
If(data.User.FullName != "" && data.User.FullName != data.User.Name,
|
||||
g.Text(" ("+data.User.FullName+")"),
|
||||
),
|
||||
}),
|
||||
)
|
||||
}
|
1
go.mod
1
go.mod
@ -311,6 +311,7 @@ require (
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
maragu.dev/gomponents v1.1.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
2
go.sum
2
go.sum
@ -1004,6 +1004,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
maragu.dev/gomponents v1.1.0 h1:iCybZZChHr1eSlvkWp/JP3CrZGzctLudQ/JI3sBcO4U=
|
||||
maragu.dev/gomponents v1.1.0/go.mod h1:oEDahza2gZoXDoDHhw8jBNgH+3UR5ni7Ur648HORydM=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
|
@ -57,6 +57,17 @@ func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ct
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
|
||||
func (h *HTMLRender) Gomponents(w io.Writer, status int, data []byte) error { //nolint:revive
|
||||
if respWriter, ok := w.(http.ResponseWriter); ok {
|
||||
if respWriter.Header().Get("Content-Type") == "" {
|
||||
respWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
}
|
||||
respWriter.WriteHeader(status)
|
||||
}
|
||||
_, err := w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *HTMLRender) TemplateLookup(name string, ctx context.Context) (TemplateExecutor, error) { //nolint:revive
|
||||
tmpls := h.templates.Load()
|
||||
if tmpls == nil {
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/components"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
@ -126,6 +127,116 @@ func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, t
|
||||
ctx.HTML(http.StatusOK, tplName)
|
||||
}
|
||||
|
||||
func RenderUserSearch2(ctx *context.Context, opts user_model.SearchUserOptions, title string) {
|
||||
// Sitemap index for sitemap paths
|
||||
isSitemap := ctx.PathParam("idx") != ""
|
||||
if isSitemap {
|
||||
opts.Page = int(ctx.PathParamInt64("idx"))
|
||||
opts.PageSize = setting.UI.SitemapPagingNum
|
||||
} else {
|
||||
opts.Page = ctx.FormInt("page")
|
||||
}
|
||||
if opts.Page <= 1 {
|
||||
opts.Page = 1
|
||||
}
|
||||
|
||||
var (
|
||||
users []*user_model.User
|
||||
count int64
|
||||
err error
|
||||
orderBy db.SearchOrderBy
|
||||
)
|
||||
|
||||
// we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns
|
||||
|
||||
sortOrder := ctx.FormString("sort")
|
||||
if sortOrder == "" {
|
||||
sortOrder = setting.UI.ExploreDefaultSort
|
||||
}
|
||||
|
||||
switch sortOrder {
|
||||
case "newest":
|
||||
orderBy = "`user`.id DESC"
|
||||
case "oldest":
|
||||
orderBy = "`user`.id ASC"
|
||||
case "leastupdate":
|
||||
orderBy = "`user`.updated_unix ASC"
|
||||
case "reversealphabetically":
|
||||
orderBy = "`user`.name DESC"
|
||||
case "lastlogin":
|
||||
orderBy = "`user`.last_login_unix ASC"
|
||||
case "reverselastlogin":
|
||||
orderBy = "`user`.last_login_unix DESC"
|
||||
case "alphabetically":
|
||||
orderBy = "`user`.name ASC"
|
||||
case "recentupdate":
|
||||
fallthrough
|
||||
default:
|
||||
// in case the sortType is not valid, we set it to recentupdate
|
||||
sortOrder = "recentupdate"
|
||||
orderBy = "`user`.updated_unix DESC"
|
||||
}
|
||||
|
||||
ctx.Data["SortType"] = sortOrder
|
||||
|
||||
if opts.SupportedSortOrders != nil && !opts.SupportedSortOrders.Contains(sortOrder) {
|
||||
ctx.NotFound(nil)
|
||||
return
|
||||
}
|
||||
|
||||
opts.Keyword = ctx.FormTrim("q")
|
||||
opts.OrderBy = orderBy
|
||||
if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
|
||||
users, count, err = user_model.SearchUsers(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchUsers", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if isSitemap {
|
||||
m := sitemap.NewSitemap()
|
||||
for _, item := range users {
|
||||
m.Add(sitemap.URL{URL: item.HTMLURL(), LastMod: item.UpdatedUnix.AsTimePtr()})
|
||||
}
|
||||
ctx.Resp.Header().Set("Content-Type", "text/xml")
|
||||
if _, err := m.WriteTo(ctx.Resp); err != nil {
|
||||
log.Error("Failed writing sitemap: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Keyword"] = opts.Keyword
|
||||
ctx.Data["Total"] = count
|
||||
ctx.Data["Users"] = users
|
||||
ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus(ctx)
|
||||
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
|
||||
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
|
||||
pager.AddParamFromRequest(ctx.Req)
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
var IsSigned bool
|
||||
if val, ok := ctx.Data["IsSigned"]; ok && val != nil {
|
||||
IsSigned = *val.(*bool)
|
||||
}
|
||||
|
||||
data := components.ExploreUsersPage(components.ExploreUsersPageProps{
|
||||
Title: title,
|
||||
Locale: ctx.Base.Locale,
|
||||
Keyword: opts.Keyword,
|
||||
SortType: sortOrder,
|
||||
Users: users,
|
||||
IsSigned: IsSigned,
|
||||
Context: ctx,
|
||||
})
|
||||
|
||||
var bodyBuffer bytes.Buffer
|
||||
data.Render(&bodyBuffer)
|
||||
|
||||
ctx.Gomponents(http.StatusOK, bodyBuffer.String(), "ExploreUsersPage")
|
||||
}
|
||||
|
||||
// Users render explore users page
|
||||
func Users(ctx *context.Context) {
|
||||
if setting.Service.Explore.DisableUsersPage {
|
||||
@ -135,8 +246,6 @@ func Users(ctx *context.Context) {
|
||||
ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage
|
||||
ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage
|
||||
ctx.Data["Title"] = ctx.Tr("explore")
|
||||
ctx.Data["PageIsExplore"] = true
|
||||
ctx.Data["PageIsExploreUsers"] = true
|
||||
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||
|
||||
supportedSortOrders := container.SetOf(
|
||||
@ -151,7 +260,7 @@ func Users(ctx *context.Context) {
|
||||
ctx.SetFormString("sort", sortOrder)
|
||||
}
|
||||
|
||||
RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||
RenderUserSearch2(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
||||
@ -159,5 +268,5 @@ func Users(ctx *context.Context) {
|
||||
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
|
||||
|
||||
SupportedSortOrders: supportedSortOrders,
|
||||
}, tplExploreUsers)
|
||||
}, string(ctx.Tr("explore")))
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import (
|
||||
type Render interface {
|
||||
TemplateLookup(tmpl string, templateCtx context.Context) (templates.TemplateExecutor, error)
|
||||
HTML(w io.Writer, status int, name templates.TplName, data any, templateCtx context.Context) error
|
||||
Gomponents(w io.Writer, status int, data []byte) error
|
||||
}
|
||||
|
||||
// Context represents context of a web request.
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"html/template"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
@ -91,6 +92,31 @@ func (ctx *Context) HTML(status int, name templates.TplName) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *Context) HTMLPartial(status int, name templates.TplName) (string, error) {
|
||||
log.Debug("Partial Template: %s", name)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
err := ctx.Render.HTML(rec, status, name, ctx.Data, ctx.TemplateContext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return rec.Body.String(), nil
|
||||
}
|
||||
|
||||
func (ctx *Context) Gomponents(status int, html string, tplName string) {
|
||||
log.Debug("Component: %s", tplName)
|
||||
|
||||
err := ctx.Render.Gomponents(ctx.Resp, status, []byte(html))
|
||||
if err == nil || errors.Is(err, syscall.EPIPE) {
|
||||
return
|
||||
}
|
||||
|
||||
err = fmt.Errorf("failed to render component: %s, error: %s", tplName, err)
|
||||
ctx.ServerError("Render failed", err) // show the 500 error page
|
||||
}
|
||||
|
||||
// JSONTemplate renders the template as JSON response
|
||||
// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape
|
||||
func (ctx *Context) JSONTemplate(tmpl templates.TplName) {
|
||||
|
@ -198,3 +198,10 @@ func (tr *MockRender) HTML(w io.Writer, status int, _ templates.TplName, _ any,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr *MockRender) Gomponents(w io.Writer, status int, _ string) error {
|
||||
if resp, ok := w.(http.ResponseWriter); ok {
|
||||
resp.WriteHeader(status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user