Search gists on user profile with title, visibility, language & topics (#422)

This commit is contained in:
Thomas Miceli
2025-02-02 18:14:03 +01:00
committed by GitHub
parent 76fc129c09
commit 7aa8f84eff
23 changed files with 429 additions and 54 deletions

View File

@ -40,3 +40,9 @@ func AdminIndexGists(ctx *context.Context) error {
go actions.Run(actions.IndexGists)
return ctx.RedirectTo("/admin-panel")
}
func AdminSyncGistLanguages(ctx *context.Context) error {
ctx.AddFlash(ctx.Tr("flash.admin.sync-gist-languages"), "success")
go actions.Run(actions.SyncGistLanguages)
return ctx.RedirectTo("/admin-panel")
}

View File

@ -48,6 +48,7 @@ func AdminIndex(ctx *context.Context) error {
ctx.SetData("syncGistPreviews", actions.IsRunning(actions.SyncGistPreviews))
ctx.SetData("resetHooks", actions.IsRunning(actions.ResetHooks))
ctx.SetData("indexGists", actions.IsRunning(actions.IndexGists))
ctx.SetData("syncGistLanguages", actions.IsRunning(actions.SyncGistLanguages))
return ctx.Html("admin_index.html")
}
@ -64,7 +65,7 @@ func AdminUsers(ctx *context.Context) error {
return ctx.ErrorRes(500, "Cannot get users", err)
}
if err = handlers.Paginate(ctx, data, pageInt, 10, "data", "admin-panel/users", 1); err != nil {
if err = handlers.Paginate(ctx, data, pageInt, 10, "data", "admin-panel/users", 1, nil); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}
@ -82,7 +83,7 @@ func AdminGists(ctx *context.Context) error {
return ctx.ErrorRes(500, "Cannot get gists", err)
}
if err = handlers.Paginate(ctx, data, pageInt, 10, "data", "admin-panel/gists", 1); err != nil {
if err = handlers.Paginate(ctx, data, pageInt, 10, "data", "admin-panel/gists", 1, nil); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}

View File

@ -9,7 +9,8 @@ import (
"github.com/thomiceli/opengist/internal/web/context"
"github.com/thomiceli/opengist/internal/web/handlers"
"gorm.io/gorm"
"html/template"
"slices"
"strings"
)
func AllGists(ctx *context.Context) error {
@ -35,6 +36,11 @@ func AllGists(ctx *context.Context) error {
orderText = ctx.TrH("gist.list.order-by-asc")
}
pagination := &handlers.PaginationParams{
Sort: sort,
Order: order,
}
ctx.SetData("sort", sortText)
ctx.SetData("order", orderText)
@ -51,7 +57,7 @@ func AllGists(ctx *context.Context) error {
if mode == "search" {
ctx.SetData("htmlTitle", ctx.TrH("gist.list.search-results"))
ctx.SetData("searchQuery", ctx.QueryParam("q"))
ctx.SetData("searchQueryUrl", template.URL("&q="+ctx.QueryParam("q")))
pagination.Query = ctx.QueryParam("q")
urlPage = "search"
gists, err = db.GetAllGistsFromSearch(currentUserId, ctx.QueryParam("q"), pageInt-1, sort, order, "")
} else if mode == "topics" {
@ -66,6 +72,7 @@ func AllGists(ctx *context.Context) error {
}
} else {
var fromUser *db.User
var count int64
fromUser, err = db.GetUserByUsername(fromUserStr)
if err != nil {
@ -104,10 +111,39 @@ func AllGists(ctx *context.Context) error {
gists, err = db.GetAllGistsForkedByUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
} else if mode == "fromUser" {
urlPage = fromUserStr
if languages, err := db.GetGistLanguagesForUser(fromUser.ID, currentUserId); err != nil {
return ctx.ErrorRes(500, "Error fetching languages", err)
} else {
ctx.SetData("languages", languages)
}
title := ctx.QueryParam("title")
language := ctx.QueryParam("language")
visibility := ctx.QueryParam("visibility")
topicsStr := ctx.QueryParam("topics")
topics := strings.Fields(topicsStr)
if len(topics) > 10 {
topics = topics[:10]
}
slices.Sort(topics)
topics = slices.Compact(topics)
pagination.Title = title
pagination.Language = language
pagination.Visibility = visibility
pagination.Topics = topicsStr
ctx.SetData("title", title)
ctx.SetData("language", language)
ctx.SetData("visibility", visibility)
ctx.SetData("topics", topicsStr)
ctx.SetData("htmlTitle", ctx.TrH("gist.list.all-from", fromUserStr))
gists, err = db.GetAllGistsFromUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
gists, count, err = db.GetAllGistsFromUser(fromUser.ID, currentUserId, title, language, visibility, topics, pageInt-1, sort, order)
ctx.SetData("countFromUser", count)
}
}
if err != nil {
return ctx.ErrorRes(500, "Error fetching gists", err)
}
renderedGists := make([]*render.RenderedGist, 0, len(gists))
for _, gist := range gists {
@ -118,21 +154,20 @@ func AllGists(ctx *context.Context) error {
renderedGists = append(renderedGists, &rendered)
}
if err != nil {
return ctx.ErrorRes(500, "Error fetching gists", err)
}
if err = handlers.Paginate(ctx, renderedGists, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil {
if err = handlers.Paginate(ctx, renderedGists, pageInt, 10, "gists", urlPage, 2, pagination); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}
ctx.SetData("urlPage", urlPage)
return ctx.Html("all.html")
}
func Search(ctx *context.Context) error {
var err error
pagination := &handlers.PaginationParams{
Query: ctx.QueryParam("q"),
}
content, meta := handlers.ParseSearchQueryStr(ctx.QueryParam("q"))
pageInt := handlers.GetPage(ctx)
@ -176,19 +211,12 @@ func Search(ctx *context.Context) error {
renderedGists = append(renderedGists, &rendered)
}
if pageInt > 1 && len(renderedGists) != 0 {
ctx.SetData("prevPage", pageInt-1)
if err = handlers.Paginate(ctx, renderedGists, pageInt, 10, "gists", "search", 2, pagination); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}
if 10*pageInt < int(nbHits) {
ctx.SetData("nextPage", pageInt+1)
}
ctx.SetData("prevLabel", ctx.TrH("pagination.previous"))
ctx.SetData("nextLabel", ctx.TrH("pagination.next"))
ctx.SetData("urlPage", "search")
ctx.SetData("urlParams", template.URL("&q="+ctx.QueryParam("q")))
ctx.SetData("htmlTitle", ctx.TrH("gist.list.search-results"))
ctx.SetData("nbHits", nbHits)
ctx.SetData("gists", renderedGists)
ctx.SetData("langs", langs)
ctx.SetData("searchQuery", ctx.QueryParam("q"))
return ctx.Html("search.html")

View File

@ -137,6 +137,7 @@ func ProcessCreate(ctx *context.Context) error {
}
gist.AddInIndex()
gist.UpdateLanguages()
return ctx.RedirectTo("/" + user.Username + "/" + gist.Identifier())
}

View File

@ -77,7 +77,7 @@ func Forks(ctx *context.Context) error {
return ctx.ErrorRes(500, "Error getting users who liked this gist", err)
}
if err = handlers.Paginate(ctx, forks, pageInt, 30, "forks", gist.User.Username+"/"+gist.Identifier()+"/forks", 2); err != nil {
if err = handlers.Paginate(ctx, forks, pageInt, 30, "forks", gist.User.Username+"/"+gist.Identifier()+"/forks", 2, nil); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}

View File

@ -42,7 +42,7 @@ func Likes(ctx *context.Context) error {
return ctx.ErrorRes(500, "Error getting users who liked this gist", err)
}
if err = handlers.Paginate(ctx, likers, pageInt, 30, "likers", gist.User.Username+"/"+gist.Identifier()+"/likes", 1); err != nil {
if err = handlers.Paginate(ctx, likers, pageInt, 30, "likers", gist.User.Username+"/"+gist.Identifier()+"/likes", 1, nil); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}

View File

@ -19,7 +19,7 @@ func Revisions(ctx *context.Context) error {
return ctx.ErrorRes(500, "Error fetching commits log", err)
}
if err := handlers.Paginate(ctx, commits, pageInt, 10, "commits", userName+"/"+gistName+"/revisions", 2); err != nil {
if err := handlers.Paginate(ctx, commits, pageInt, 10, "commits", userName+"/"+gistName+"/revisions", 2, nil); err != nil {
return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil)
}

View File

@ -2,7 +2,9 @@ package handlers
import (
"errors"
"github.com/gorilla/schema"
"html/template"
"net/url"
"path/filepath"
"strconv"
"strings"
@ -24,7 +26,68 @@ func GetPage(ctx *context.Context) int {
return pageInt
}
func Paginate[T any](ctx *context.Context, data []*T, pageInt int, perPage int, templateDataName string, urlPage string, labels int, urlParams ...string) error {
type PaginationParams struct {
Page int `schema:"page,omitempty"`
Sort string `schema:"sort,omitempty"`
Order string `schema:"order,omitempty"`
Title string `schema:"title,omitempty"`
Visibility string `schema:"visibility,omitempty"`
Language string `schema:"language,omitempty"`
Topics string `schema:"topics,omitempty"`
Query string `schema:"q,omitempty"`
HasPrevious bool `schema:"-"` // Exclude from URL parameters
HasNext bool `schema:"-"`
}
var encoder = schema.NewEncoder()
func (p PaginationParams) String() string {
values := url.Values{}
err := encoder.Encode(p, values)
if err != nil {
return ""
}
if len(values) == 0 {
return ""
}
return "?" + values.Encode()
}
func (p PaginationParams) NextURL() template.URL {
p.Page++
return template.URL(p.String())
}
func (p PaginationParams) PreviousURL() template.URL {
p.Page--
return template.URL(p.String())
}
func (p PaginationParams) WithParams(pairs ...string) template.URL {
values := url.Values{}
_ = encoder.Encode(p, values)
// reset page
values.Del("page")
for i := 0; i < len(pairs); i += 2 {
values.Set(pairs[i], pairs[i+1])
}
return template.URL("?" + values.Encode())
}
func Paginate[T any](ctx *context.Context, data []*T, pageInt int, perPage int, templateDataName string, urlPage string, labels int, params *PaginationParams) error {
var paginationParams PaginationParams
if params == nil {
paginationParams = PaginationParams{}
} else {
paginationParams = *params
}
paginationParams.Page = pageInt
lenData := len(data)
if lenData == 0 && pageInt != 1 {
return errors.New("page not found")
@ -34,15 +97,13 @@ func Paginate[T any](ctx *context.Context, data []*T, pageInt int, perPage int,
if lenData > 1 {
data = data[:lenData-1]
}
ctx.SetData("nextPage", pageInt+1)
paginationParams.HasNext = true
}
if pageInt > 1 {
ctx.SetData("prevPage", pageInt-1)
paginationParams.HasPrevious = true
}
if len(urlParams) > 0 {
ctx.SetData("urlParams", template.URL(urlParams[0]))
}
ctx.SetData("pagination", paginationParams)
switch labels {
case 1:

View File

@ -82,6 +82,7 @@ func (s *Server) registerRoutes() {
sB.POST("/sync-previews", admin.AdminSyncGistPreviews)
sB.POST("/reset-hooks", admin.AdminResetHooks)
sB.POST("/index-gists", admin.AdminIndexGists)
sB.POST("/sync-languages", admin.AdminSyncGistLanguages)
sB.GET("/configuration", admin.AdminConfig)
sB.PUT("/set-config", admin.AdminSetConfig)
}