Add topics for Gists (#413)

This commit is contained in:
Thomas Miceli
2025-01-24 14:39:42 +01:00
committed by GitHub
parent 8369cbf2f0
commit f5b8881d35
25 changed files with 278 additions and 59 deletions

View File

@ -10,8 +10,6 @@ import (
"github.com/thomiceli/opengist/internal/web/handlers"
"gorm.io/gorm"
"html/template"
"regexp"
"strings"
)
func AllGists(ctx *context.Context) error {
@ -48,35 +46,25 @@ func AllGists(ctx *context.Context) error {
currentUserId = 0
}
mode := ctx.GetData("mode")
if fromUserStr == "" {
urlctx := ctx.Request().URL.Path
if strings.HasSuffix(urlctx, "search") {
if mode == "search" {
ctx.SetData("htmlTitle", ctx.TrH("gist.list.search-results"))
ctx.SetData("mode", "search")
ctx.SetData("searchQuery", ctx.QueryParam("q"))
ctx.SetData("searchQueryUrl", template.URL("&q="+ctx.QueryParam("q")))
urlPage = "search"
gists, err = db.GetAllGistsFromSearch(currentUserId, ctx.QueryParam("q"), pageInt-1, sort, order)
} else if strings.HasSuffix(urlctx, "all") {
gists, err = db.GetAllGistsFromSearch(currentUserId, ctx.QueryParam("q"), pageInt-1, sort, order, "")
} else if mode == "topics" {
ctx.SetData("htmlTitle", ctx.TrH("gist.list.topic-results-topic", ctx.Param("topic")))
ctx.SetData("topic", ctx.Param("topic"))
urlPage = "topics/" + ctx.Param("topic")
gists, err = db.GetAllGistsFromSearch(currentUserId, "", pageInt-1, sort, order, ctx.Param("topic"))
} else if mode == "all" {
ctx.SetData("htmlTitle", ctx.TrH("gist.list.all"))
ctx.SetData("mode", "all")
urlPage = "all"
gists, err = db.GetAllGistsForCurrentUser(currentUserId, pageInt-1, sort, order)
}
} else {
liked := false
forked := false
liked, err = regexp.MatchString(`/[^/]*/liked`, ctx.Request().URL.Path)
if err != nil {
return ctx.ErrorRes(500, "Error matching regexp", err)
}
forked, err = regexp.MatchString(`/[^/]*/forked`, ctx.Request().URL.Path)
if err != nil {
return ctx.ErrorRes(500, "Error matching regexp", err)
}
var fromUser *db.User
fromUser, err = db.GetUserByUsername(fromUserStr)
@ -106,20 +94,17 @@ func AllGists(ctx *context.Context) error {
ctx.SetData("countForked", countForked)
}
if liked {
if mode == "liked" {
urlPage = fromUserStr + "/liked"
ctx.SetData("htmlTitle", ctx.TrH("gist.list.all-liked-by", fromUserStr))
ctx.SetData("mode", "liked")
gists, err = db.GetAllGistsLikedByUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
} else if forked {
} else if mode == "forked" {
urlPage = fromUserStr + "/forked"
ctx.SetData("htmlTitle", ctx.TrH("gist.list.all-forked-by", fromUserStr))
ctx.SetData("mode", "forked")
gists, err = db.GetAllGistsForkedByUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
} else {
} else if mode == "fromUser" {
urlPage = fromUserStr
ctx.SetData("htmlTitle", ctx.TrH("gist.list.all-from", fromUserStr))
ctx.SetData("mode", "fromUser")
gists, err = db.GetAllGistsFromUser(fromUser.ID, currentUserId, pageInt-1, sort, order)
}
}
@ -171,6 +156,7 @@ func Search(ctx *context.Context) error {
Filename: meta["filename"],
Extension: meta["extension"],
Language: meta["language"],
Topic: meta["topic"],
}, visibleGistsIds, pageInt)
if err != nil {
return ctx.ErrorRes(500, "Error searching gists", err)

View File

@ -67,14 +67,14 @@ func ProcessCreate(ctx *context.Context) error {
if err != nil {
ctx.AddFlash(validator.ValidationMessages(&err, ctx.GetData("locale").(*i18n.Locale)), "error")
if isCreate {
return ctx.Html("create.html")
return ctx.HtmlWithCode(400, "create.html")
} else {
files, err := gist.Files("HEAD", false)
if err != nil {
return ctx.ErrorRes(500, "Error fetching files", err)
}
ctx.SetData("files", files)
return ctx.Html("edit.html")
return ctx.HtmlWithCode(400, "edit.html")
}
}

View File

@ -43,6 +43,7 @@ func Fork(ctx *context.Context) error {
UserID: currentUser.ID,
ForkedID: gist.ID,
NbFiles: gist.NbFiles,
Topics: gist.Topics,
}
if err = newGist.CreateForked(); err != nil {

View File

@ -54,6 +54,11 @@ func GistJson(ctx *context.Context) error {
renderedFiles := render.HighlightFiles(files)
ctx.SetData("files", renderedFiles)
topics, err := gist.GetTopics()
if err != nil {
return ctx.ErrorRes(500, "Error fetching topics for gist", err)
}
htmlbuf := bytes.Buffer{}
w := bufio.NewWriter(&htmlbuf)
if err = ctx.Echo().Renderer.Render(w, "gist_embed.html", ctx.DataMap(), ctx); err != nil {
@ -80,6 +85,7 @@ func GistJson(ctx *context.Context) error {
"created_at": time.Unix(gist.CreatedAt, 0).Format(time.RFC3339),
"visibility": gist.VisibilityStr(),
"files": renderedFiles,
"topics": topics,
"embed": map[string]string{
"html": htmlbuf.String(),
"css": cssUrl,

View File

@ -405,3 +405,11 @@ func gistNewPushSoftInit(next Handler) Handler {
return next(ctx)
}
}
func setAllGistsMode(mode string) Middleware {
return func(next Handler) Handler {
return func(ctx *context.Context) error {
ctx.SetData("mode", mode)
return next(ctx)
}
}
}

View File

@ -179,6 +179,16 @@ func (s *Server) setFuncMap() {
_, err := url.ParseRequestURI(s)
return err == nil
},
"topicsToStr": func(topics []db.GistTopic) string {
str := ""
for i, topic := range topics {
if i > 0 {
str += " "
}
str += topic.Topic
}
return str
},
}
t := template.Must(template.New("t").Funcs(fm).ParseFS(templates.Files, "*/*.html"))

View File

@ -90,17 +90,19 @@ func (s *Server) registerRoutes() {
r.Any("/init/*", git.GitHttp, gistNewPushSoftInit)
}
r.GET("/all", gist.AllGists, checkRequireLogin)
r.GET("/all", gist.AllGists, checkRequireLogin, setAllGistsMode("all"))
if index.Enabled() {
r.GET("/search", gist.Search, checkRequireLogin)
} else {
r.GET("/search", gist.AllGists, checkRequireLogin)
r.GET("/search", gist.AllGists, checkRequireLogin, setAllGistsMode("search"))
}
r.GET("/:user", gist.AllGists, checkRequireLogin)
r.GET("/:user/liked", gist.AllGists, checkRequireLogin)
r.GET("/:user/forked", gist.AllGists, checkRequireLogin)
r.GET("/:user", gist.AllGists, checkRequireLogin, setAllGistsMode("fromUser"))
r.GET("/:user/liked", gist.AllGists, checkRequireLogin, setAllGistsMode("liked"))
r.GET("/:user/forked", gist.AllGists, checkRequireLogin, setAllGistsMode("forked"))
r.GET("/topics/:topic", gist.AllGists, checkRequireLogin, setAllGistsMode("topics"))
sC := r.SubGroup("/:user/:gistname")
{

View File

@ -131,6 +131,7 @@ func TestAdminUser(t *testing.T) {
},
Name: []string{"gist1.txt"},
Content: []string{"yeah"},
Topics: "",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -170,6 +171,7 @@ func TestAdminGist(t *testing.T) {
},
Name: []string{"gist1.txt"},
Content: []string{"yeah"},
Topics: "",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)

View File

@ -110,6 +110,7 @@ func TestAnonymous(t *testing.T) {
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "",
}
err = s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -165,6 +166,7 @@ func TestGitOperations(t *testing.T) {
Content: []string{
"yeah",
},
Topics: "",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -180,6 +182,7 @@ func TestGitOperations(t *testing.T) {
Content: []string{
"cool",
},
Topics: "",
}
err = s.Request("POST", "/", gist2, 302)
require.NoError(t, err)
@ -195,6 +198,7 @@ func TestGitOperations(t *testing.T) {
Content: []string{
"super",
},
Topics: "",
}
err = s.Request("POST", "/", gist3, 302)
require.NoError(t, err)

View File

@ -21,7 +21,7 @@ func TestGists(t *testing.T) {
err = s.Request("GET", "/all", nil, 200)
require.NoError(t, err)
err = s.Request("POST", "/", nil, 200)
err = s.Request("POST", "/", nil, 400)
require.NoError(t, err)
gist1 := db.GistDTO{
@ -32,6 +32,7 @@ func TestGists(t *testing.T) {
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "",
}
err = s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -63,8 +64,9 @@ func TestGists(t *testing.T) {
},
Name: []string{"", "gist2.txt", "gist3.txt"},
Content: []string{"", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "",
}
err = s.Request("POST", "/", gist2, 200)
err = s.Request("POST", "/", gist2, 400)
require.NoError(t, err)
gist3 := db.GistDTO{
@ -75,6 +77,7 @@ func TestGists(t *testing.T) {
},
Name: []string{""},
Content: []string{"yeah"},
Topics: "",
}
err = s.Request("POST", "/", gist3, 302)
require.NoError(t, err)
@ -86,7 +89,7 @@ func TestGists(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "gistfile1.txt", gist3files[0])
err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/edit", nil, 200)
err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/edit", nil, 400)
require.NoError(t, err)
gist1.Name = []string{"gist1.txt"}
@ -118,6 +121,7 @@ func TestVisibility(t *testing.T) {
},
Name: []string{""},
Content: []string{"yeah"},
Topics: "",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -160,6 +164,7 @@ func TestLikeFork(t *testing.T) {
},
Name: []string{""},
Content: []string{"yeah"},
Topics: "",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -220,6 +225,7 @@ func TestCustomUrl(t *testing.T) {
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
@ -251,6 +257,7 @@ func TestCustomUrl(t *testing.T) {
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "",
}
err = s.Request("POST", "/", gist2, 302)
require.NoError(t, err)
@ -261,3 +268,75 @@ func TestCustomUrl(t *testing.T) {
require.Equal(t, gist2db.Uuid, gist2db.Identifier())
require.NotEqual(t, gist2db.URL, gist2db.Identifier())
}
func TestTopics(t *testing.T) {
s := Setup(t)
defer Teardown(t, s)
user1 := db.UserDTO{Username: "thomas", Password: "thomas"}
register(t, s, user1)
gist1 := db.GistDTO{
Title: "gist1",
URL: "my-gist",
Description: "my first gist",
VisibilityDTO: db.VisibilityDTO{
Private: 0,
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "topic1 topic2 topic3",
}
err := s.Request("POST", "/", gist1, 302)
require.NoError(t, err)
gist1db, err := db.GetGistByID("1")
require.NoError(t, err)
require.Equal(t, []db.GistTopic{
{GistID: 1, Topic: "topic1"},
{GistID: 1, Topic: "topic2"},
{GistID: 1, Topic: "topic3"},
}, gist1db.Topics)
gist2 := db.GistDTO{
Title: "gist2",
URL: "my-gist",
Description: "my second gist",
VisibilityDTO: db.VisibilityDTO{
Private: 0,
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "topic1 topic2 topic3 topic2 topic4 topic1",
}
err = s.Request("POST", "/", gist2, 302)
require.NoError(t, err)
gist2db, err := db.GetGistByID("2")
require.NoError(t, err)
require.Equal(t, []db.GistTopic{
{GistID: 2, Topic: "topic1"},
{GistID: 2, Topic: "topic2"},
{GistID: 2, Topic: "topic3"},
{GistID: 2, Topic: "topic4"},
}, gist2db.Topics)
gist3 := db.GistDTO{
Title: "gist3",
URL: "my-gist",
Description: "my third gist",
VisibilityDTO: db.VisibilityDTO{
Private: 0,
},
Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"},
Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"},
Topics: "topic1 topic2 topic3 topic4 topic5 topic6 topic7 topic8 topic9 topic10 topic11",
}
err = s.Request("POST", "/", gist3, 400)
require.NoError(t, err)
gist3.Topics = "topictoolongggggggggggggggggggggggggggggggggggggggg"
err = s.Request("POST", "/", gist3, 400)
require.NoError(t, err)
}