Implement post links in feed / landing page
This commit is contained in:
parent
4f9df3382f
commit
5d697e5fff
|
@ -0,0 +1,41 @@
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/migration/types"
|
||||||
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
|
"github.com/jackc/pgx/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerMigration(RenameHMNProject{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenameHMNProject struct{}
|
||||||
|
|
||||||
|
func (m RenameHMNProject) Version() types.MigrationVersion {
|
||||||
|
return types.MigrationVersion(time.Date(2021, 4, 28, 1, 55, 37, 0, time.UTC))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RenameHMNProject) Name() string {
|
||||||
|
return "RenameHMNProject"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RenameHMNProject) Description() string {
|
||||||
|
return "Rename the special HMN project"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RenameHMNProject) Up(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
_, err := tx.Exec(ctx, `UPDATE handmade_project SET name = 'Handmade Network' WHERE id = 1`)
|
||||||
|
if err != nil {
|
||||||
|
return oops.New(err, "failed to rename project")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RenameHMNProject) Down(ctx context.Context, tx pgx.Tx) error {
|
||||||
|
panic("Implement me")
|
||||||
|
}
|
|
@ -4,19 +4,18 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/db"
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CategoryType int
|
type CategoryKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CatTypeBlog CategoryType = iota + 1
|
CatKindBlog CategoryKind = iota + 1
|
||||||
CatTypeForum
|
CatKindForum
|
||||||
CatTypeStatic
|
CatKindStatic
|
||||||
CatTypeAnnotation
|
CatKindAnnotation
|
||||||
CatTypeWiki
|
CatKindWiki
|
||||||
CatTypeLibraryResource
|
CatKindLibraryResource
|
||||||
)
|
)
|
||||||
|
|
||||||
type Category struct {
|
type Category struct {
|
||||||
|
@ -28,7 +27,7 @@ type Category struct {
|
||||||
Slug *string `db:"slug"` // TODO: Make not null
|
Slug *string `db:"slug"` // TODO: Make not null
|
||||||
Name *string `db:"name"` // TODO: Make not null
|
Name *string `db:"name"` // TODO: Make not null
|
||||||
Blurb *string `db:"blurb"` // TODO: Make not null
|
Blurb *string `db:"blurb"` // TODO: Make not null
|
||||||
Kind CategoryType `db:"kind"`
|
Kind CategoryKind `db:"kind"`
|
||||||
Color1 string `db:"color_1"`
|
Color1 string `db:"color_1"`
|
||||||
Color2 string `db:"color_2"`
|
Color2 string `db:"color_2"`
|
||||||
Depth int `db:"depth"` // TODO: What is this?
|
Depth int `db:"depth"` // TODO: What is this?
|
||||||
|
@ -72,51 +71,3 @@ func (c *Category) GetHierarchy(ctx context.Context, conn *pgxpool.Pool) []Categ
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCategoryUrls(ctx context.Context, conn *pgxpool.Pool, cats ...*Category) map[int]string {
|
|
||||||
var projectIds []int
|
|
||||||
for _, cat := range cats {
|
|
||||||
id := *cat.ProjectID
|
|
||||||
|
|
||||||
alreadyInList := false
|
|
||||||
for _, otherId := range projectIds {
|
|
||||||
if otherId == id {
|
|
||||||
alreadyInList = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !alreadyInList {
|
|
||||||
projectIds = append(projectIds, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(inarray)!!!!!
|
|
||||||
|
|
||||||
//for _, cat := range cats {
|
|
||||||
// hierarchy := makeCategoryUrl(cat.GetHierarchy(ctx, conn))
|
|
||||||
//}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeCategoryUrl(cats []*Category, subdomain string) string {
|
|
||||||
path := ""
|
|
||||||
for i, cat := range cats {
|
|
||||||
if i == 0 {
|
|
||||||
switch cat.Kind {
|
|
||||||
case CatTypeBlog:
|
|
||||||
path += "/blogs"
|
|
||||||
case CatTypeForum:
|
|
||||||
path += "/forums"
|
|
||||||
// TODO: All cat types?
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
path += "/" + *cat.Slug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hmnurl.ProjectUrl(path, nil, subdomain)
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ type Post struct {
|
||||||
CurrentID int `db:"current_id"`
|
CurrentID int `db:"current_id"`
|
||||||
ProjectID int `db:"project_id"`
|
ProjectID int `db:"project_id"`
|
||||||
|
|
||||||
CategoryType CategoryType `db:"category_kind"`
|
CategoryKind CategoryKind `db:"category_kind"`
|
||||||
|
|
||||||
Depth int `db:"depth"`
|
Depth int `db:"depth"`
|
||||||
Slug string `db:"slug"`
|
Slug string `db:"slug"`
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
|
||||||
"git.handmade.network/hmn/hmn/src/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
func PostUrl(post models.Post, catType models.CategoryType, subdomain string) string {
|
|
||||||
switch catType {
|
|
||||||
// TODO: All the relevant post types. Maybe it doesn't make sense to lump them all together here.
|
|
||||||
case models.CatTypeBlog:
|
|
||||||
return hmnurl.ProjectUrl(fmt.Sprintf("blogs/p/%d/e/%d", post.ThreadID, post.ID), nil, subdomain)
|
|
||||||
case models.CatTypeForum:
|
|
||||||
return hmnurl.ProjectUrl(fmt.Sprintf("forums/t/%d/p/%d", post.ThreadID, post.ID), nil, subdomain)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.handmade.network/hmn/hmn/src/db"
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||||
"git.handmade.network/hmn/hmn/src/logging"
|
"git.handmade.network/hmn/hmn/src/logging"
|
||||||
"git.handmade.network/hmn/hmn/src/models"
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
"git.handmade.network/hmn/hmn/src/oops"
|
"git.handmade.network/hmn/hmn/src/oops"
|
||||||
|
@ -30,14 +31,11 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
FROM
|
FROM
|
||||||
handmade_post AS post
|
handmade_post AS post
|
||||||
WHERE
|
WHERE
|
||||||
post.category_kind IN ($1, $2, $3, $4)
|
post.category_kind = ANY ($1)
|
||||||
AND NOT moderated
|
AND NOT moderated
|
||||||
`,
|
`,
|
||||||
models.CatTypeForum,
|
[]models.CategoryKind{models.CatKindForum, models.CatKindBlog, models.CatKindWiki, models.CatKindLibraryResource},
|
||||||
models.CatTypeBlog,
|
)
|
||||||
models.CatTypeWiki,
|
|
||||||
models.CatTypeLibraryResource,
|
|
||||||
) // TODO(inarray)
|
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get count of feed posts"))
|
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get count of feed posts"))
|
||||||
|
@ -74,12 +72,13 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
c.Perf.StartBlock("SQL", "Fetch posts")
|
c.Perf.StartBlock("SQL", "Fetch posts")
|
||||||
type feedPostQuery struct {
|
type feedPostQuery struct {
|
||||||
Post models.Post `db:"post"`
|
Post models.Post `db:"post"`
|
||||||
Thread models.Thread `db:"thread"`
|
Thread models.Thread `db:"thread"`
|
||||||
Proj models.Project `db:"proj"`
|
Cat models.Category `db:"cat"`
|
||||||
User models.User `db:"auth_user"`
|
Proj models.Project `db:"proj"`
|
||||||
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
|
User models.User `db:"auth_user"`
|
||||||
CatLastReadTime *time.Time `db:"clri.lastread"`
|
ThreadLastReadTime *time.Time `db:"tlri.lastread"`
|
||||||
|
CatLastReadTime *time.Time `db:"clri.lastread"`
|
||||||
}
|
}
|
||||||
posts, err := db.Query(c.Context(), c.Conn, feedPostQuery{},
|
posts, err := db.Query(c.Context(), c.Conn, feedPostQuery{},
|
||||||
`
|
`
|
||||||
|
@ -87,6 +86,7 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
FROM
|
FROM
|
||||||
handmade_post AS post
|
handmade_post AS post
|
||||||
JOIN handmade_thread AS thread ON thread.id = post.thread_id
|
JOIN handmade_thread AS thread ON thread.id = post.thread_id
|
||||||
|
JOIN handmade_category AS cat ON cat.id = post.category_id
|
||||||
JOIN handmade_project AS proj ON proj.id = post.project_id
|
JOIN handmade_project AS proj ON proj.id = post.project_id
|
||||||
LEFT OUTER JOIN handmade_threadlastreadinfo AS tlri ON (
|
LEFT OUTER JOIN handmade_threadlastreadinfo AS tlri ON (
|
||||||
tlri.thread_id = post.thread_id
|
tlri.thread_id = post.thread_id
|
||||||
|
@ -98,25 +98,24 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
)
|
)
|
||||||
LEFT OUTER JOIN auth_user ON post.author_id = auth_user.id
|
LEFT OUTER JOIN auth_user ON post.author_id = auth_user.id
|
||||||
WHERE
|
WHERE
|
||||||
post.category_kind IN ($2, $3, $4, $5)
|
post.category_kind = ANY ($2)
|
||||||
AND post.moderated = FALSE
|
AND post.moderated = FALSE
|
||||||
AND post.thread_id IS NOT NULL
|
AND post.thread_id IS NOT NULL
|
||||||
ORDER BY postdate DESC
|
ORDER BY postdate DESC
|
||||||
LIMIT $6 OFFSET $7
|
LIMIT $3 OFFSET $4
|
||||||
`,
|
`,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
models.CatTypeForum,
|
[]models.CategoryKind{models.CatKindForum, models.CatKindBlog, models.CatKindWiki, models.CatKindLibraryResource},
|
||||||
models.CatTypeBlog,
|
|
||||||
models.CatTypeWiki,
|
|
||||||
models.CatTypeLibraryResource,
|
|
||||||
postsPerPage,
|
postsPerPage,
|
||||||
howManyPostsToSkip,
|
howManyPostsToSkip,
|
||||||
) // TODO(inarray)
|
)
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
|
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
categoryUrls := GetAllCategoryUrls(c.Context(), c.Conn)
|
||||||
|
|
||||||
var postItems []templates.PostListItem
|
var postItems []templates.PostListItem
|
||||||
for _, iPostResult := range posts.ToSlice() {
|
for _, iPostResult := range posts.ToSlice() {
|
||||||
postResult := iPostResult.(*feedPostQuery)
|
postResult := iPostResult.(*feedPostQuery)
|
||||||
|
@ -128,25 +127,33 @@ func Feed(c *RequestContext) ResponseData {
|
||||||
hasRead = true
|
hasRead = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var parents []models.Category
|
parents := postResult.Cat.GetHierarchy(c.Context(), c.Conn)
|
||||||
// parents := postResult.Cat.GetHierarchy(c.Context(), c.Conn)
|
|
||||||
logging.Debug().Interface("parents", parents).Msg("")
|
logging.Debug().Interface("parents", parents).Msg("")
|
||||||
|
|
||||||
var breadcrumbs []templates.Breadcrumb
|
var breadcrumbs []templates.Breadcrumb
|
||||||
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
||||||
Name: *postResult.Proj.Name,
|
Name: *postResult.Proj.Name,
|
||||||
Url: "nargle", // TODO
|
Url: hmnurl.ProjectUrl("/", nil, postResult.Proj.Subdomain()),
|
||||||
})
|
})
|
||||||
for i := len(parents) - 1; i >= 0; i-- {
|
for _, parent := range parents {
|
||||||
|
name := *parent.Name
|
||||||
|
if parent.ParentID == nil {
|
||||||
|
switch parent.Kind {
|
||||||
|
case models.CatKindForum:
|
||||||
|
name = "Forums"
|
||||||
|
case models.CatKindBlog:
|
||||||
|
name = "Blog"
|
||||||
|
}
|
||||||
|
}
|
||||||
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
breadcrumbs = append(breadcrumbs, templates.Breadcrumb{
|
||||||
Name: *parents[i].Name,
|
Name: name,
|
||||||
Url: "nargle", // TODO
|
Url: categoryUrls[parent.ID],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
postItems = append(postItems, templates.PostListItem{
|
postItems = append(postItems, templates.PostListItem{
|
||||||
Title: postResult.Thread.Title,
|
Title: postResult.Thread.Title,
|
||||||
Url: templates.PostUrl(postResult.Post, postResult.Post.CategoryType, postResult.Proj.Subdomain()),
|
Url: PostUrl(postResult.Post, postResult.Post.CategoryKind, categoryUrls[postResult.Post.CategoryID]),
|
||||||
User: templates.UserToTemplate(&postResult.User),
|
User: templates.UserToTemplate(&postResult.User),
|
||||||
Date: postResult.Post.PostDate,
|
Date: postResult.Post.PostDate,
|
||||||
Breadcrumbs: breadcrumbs,
|
Breadcrumbs: breadcrumbs,
|
||||||
|
|
|
@ -64,6 +64,8 @@ func Index(c *RequestContext) ResponseData {
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
c.Logger.Info().Interface("allProjects", allProjects).Msg("all the projects")
|
c.Logger.Info().Interface("allProjects", allProjects).Msg("all the projects")
|
||||||
|
|
||||||
|
categoryUrls := GetAllCategoryUrls(c.Context(), c.Conn)
|
||||||
|
|
||||||
var currentUserId *int
|
var currentUserId *int
|
||||||
if c.CurrentUser != nil {
|
if c.CurrentUser != nil {
|
||||||
currentUserId = &c.CurrentUser.ID
|
currentUserId = &c.CurrentUser.ID
|
||||||
|
@ -105,7 +107,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
`,
|
`,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
proj.ID,
|
proj.ID,
|
||||||
models.CatTypeBlog, models.CatTypeForum, models.CatTypeWiki, models.CatTypeLibraryResource,
|
models.CatKindBlog, models.CatKindForum, models.CatKindWiki, models.CatKindLibraryResource,
|
||||||
maxPosts,
|
maxPosts,
|
||||||
)
|
)
|
||||||
c.Perf.EndBlock()
|
c.Perf.EndBlock()
|
||||||
|
@ -130,7 +132,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
featurable := (!proj.IsHMN() &&
|
featurable := (!proj.IsHMN() &&
|
||||||
projectPost.Post.CategoryType == models.CatTypeBlog &&
|
projectPost.Post.CategoryKind == models.CatKindBlog &&
|
||||||
projectPost.Post.ParentID == nil &&
|
projectPost.Post.ParentID == nil &&
|
||||||
landingPageProject.FeaturedPost == nil)
|
landingPageProject.FeaturedPost == nil)
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
landingPageProject.FeaturedPost = &LandingPageFeaturedPost{
|
landingPageProject.FeaturedPost = &LandingPageFeaturedPost{
|
||||||
Title: projectPost.Thread.Title,
|
Title: projectPost.Thread.Title,
|
||||||
Url: templates.PostUrl(projectPost.Post, projectPost.Post.CategoryType, proj.Subdomain()),
|
Url: PostUrl(projectPost.Post, projectPost.Post.CategoryKind, categoryUrls[projectPost.Post.CategoryID]),
|
||||||
User: templates.UserToTemplate(&projectPost.User),
|
User: templates.UserToTemplate(&projectPost.User),
|
||||||
Date: projectPost.Post.PostDate,
|
Date: projectPost.Post.PostDate,
|
||||||
Unread: !hasRead,
|
Unread: !hasRead,
|
||||||
|
@ -165,7 +167,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
} else {
|
} else {
|
||||||
landingPageProject.Posts = append(landingPageProject.Posts, templates.PostListItem{
|
landingPageProject.Posts = append(landingPageProject.Posts, templates.PostListItem{
|
||||||
Title: projectPost.Thread.Title,
|
Title: projectPost.Thread.Title,
|
||||||
Url: templates.PostUrl(projectPost.Post, projectPost.Post.CategoryType, proj.Subdomain()),
|
Url: PostUrl(projectPost.Post, projectPost.Post.CategoryKind, categoryUrls[projectPost.Post.CategoryID]),
|
||||||
User: templates.UserToTemplate(&projectPost.User),
|
User: templates.UserToTemplate(&projectPost.User),
|
||||||
Date: projectPost.Post.PostDate,
|
Date: projectPost.Post.PostDate,
|
||||||
Unread: !hasRead,
|
Unread: !hasRead,
|
||||||
|
@ -198,7 +200,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
AND cat.kind = $2
|
AND cat.kind = $2
|
||||||
`,
|
`,
|
||||||
models.HMNProjectID,
|
models.HMNProjectID,
|
||||||
models.CatTypeBlog,
|
models.CatKindBlog,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch latest news post"))
|
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch latest news post"))
|
||||||
|
@ -261,7 +263,7 @@ func Index(c *RequestContext) ResponseData {
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`,
|
`,
|
||||||
models.HMNProjectID,
|
models.HMNProjectID,
|
||||||
models.CatTypeBlog,
|
models.CatKindBlog,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post"))
|
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch news post"))
|
||||||
|
@ -272,11 +274,11 @@ func Index(c *RequestContext) ResponseData {
|
||||||
baseData.BodyClasses = append(baseData.BodyClasses, "hmdev", "landing") // TODO: Is "hmdev" necessary any more?
|
baseData.BodyClasses = append(baseData.BodyClasses, "hmdev", "landing") // TODO: Is "hmdev" necessary any more?
|
||||||
|
|
||||||
var res ResponseData
|
var res ResponseData
|
||||||
err = res.WriteTemplate("index.html", LandingTemplateData{
|
err = res.WriteTemplate("landing.html", LandingTemplateData{
|
||||||
BaseData: baseData,
|
BaseData: baseData,
|
||||||
NewsPost: LandingPageFeaturedPost{
|
NewsPost: LandingPageFeaturedPost{
|
||||||
Title: newsPostResult.Thread.Title,
|
Title: newsPostResult.Thread.Title,
|
||||||
Url: templates.PostUrl(newsPostResult.Post, models.CatTypeBlog, ""),
|
Url: PostUrl(newsPostResult.Post, models.CatKindBlog, ""),
|
||||||
User: templates.UserToTemplate(&newsPostResult.User),
|
User: templates.UserToTemplate(&newsPostResult.User),
|
||||||
Date: newsPostResult.Post.PostDate,
|
Date: newsPostResult.Post.PostDate,
|
||||||
Unread: true, // TODO
|
Unread: true, // TODO
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
package website
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
|
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||||
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
|
"github.com/jackc/pgx/v4/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
type categoryUrlQueryResult struct {
|
||||||
|
Cat models.Category `db:"cat"`
|
||||||
|
Project models.Project `db:"project"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllCategoryUrls(ctx context.Context, conn *pgxpool.Pool) map[int]string {
|
||||||
|
it, err := db.Query(ctx, conn, categoryUrlQueryResult{},
|
||||||
|
`
|
||||||
|
SELECT $columns
|
||||||
|
FROM
|
||||||
|
handmade_category AS cat
|
||||||
|
JOIN handmade_project AS project ON project.id = cat.project_id
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer it.Close()
|
||||||
|
|
||||||
|
return makeCategoryUrls(it.ToSlice())
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProjectCategoryUrls(ctx context.Context, conn *pgxpool.Pool, projectId ...int) map[int]string {
|
||||||
|
it, err := db.Query(ctx, conn, categoryUrlQueryResult{},
|
||||||
|
`
|
||||||
|
SELECT $columns
|
||||||
|
FROM
|
||||||
|
handmade_category AS cat
|
||||||
|
JOIN handmade_project AS project ON project.id = cat.project_id
|
||||||
|
WHERE
|
||||||
|
project.id = ANY ($1)
|
||||||
|
`,
|
||||||
|
projectId,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer it.Close()
|
||||||
|
|
||||||
|
return makeCategoryUrls(it.ToSlice())
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCategoryUrls(rows []interface{}) map[int]string {
|
||||||
|
categories := make(map[int]*models.Category)
|
||||||
|
for _, irow := range rows {
|
||||||
|
cat := irow.(*categoryUrlQueryResult).Cat
|
||||||
|
categories[cat.ID] = &cat
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[int]string)
|
||||||
|
for _, irow := range rows {
|
||||||
|
row := irow.(*categoryUrlQueryResult)
|
||||||
|
|
||||||
|
// get hierarchy (backwards, so current -> parent -> root)
|
||||||
|
var hierarchyReverse []*models.Category
|
||||||
|
currentCatID := row.Cat.ID
|
||||||
|
for {
|
||||||
|
cat := categories[currentCatID]
|
||||||
|
|
||||||
|
hierarchyReverse = append(hierarchyReverse, cat)
|
||||||
|
if cat.ParentID == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
currentCatID = *cat.ParentID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse to get root -> parent -> current
|
||||||
|
hierarchy := make([]*models.Category, len(hierarchyReverse))
|
||||||
|
for i := len(hierarchyReverse) - 1; i >= 0; i-- {
|
||||||
|
hierarchy[len(hierarchyReverse)-1-i] = hierarchyReverse[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
result[row.Cat.ID] = CategoryUrl(row.Project.Subdomain(), hierarchy...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func CategoryUrl(subdomain string, cats ...*models.Category) string {
|
||||||
|
path := ""
|
||||||
|
for i, cat := range cats {
|
||||||
|
if i == 0 {
|
||||||
|
switch cat.Kind {
|
||||||
|
case models.CatKindBlog:
|
||||||
|
path += "/blogs"
|
||||||
|
case models.CatKindForum:
|
||||||
|
path += "/forums"
|
||||||
|
// TODO: All cat types?
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path += "/" + *cat.Slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hmnurl.ProjectUrl(path, nil, subdomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostUrl(post models.Post, catKind models.CategoryKind, categoryUrl string) string {
|
||||||
|
categoryUrl = strings.TrimRight(categoryUrl, "/")
|
||||||
|
|
||||||
|
switch catKind {
|
||||||
|
// TODO: All the relevant post types. Maybe it doesn't make sense to lump them all together here.
|
||||||
|
case models.CatKindBlog:
|
||||||
|
return fmt.Sprintf("%s/p/%d/e/%d", categoryUrl, post.ThreadID, post.ID)
|
||||||
|
case models.CatKindForum:
|
||||||
|
return fmt.Sprintf("%s/t/%d/p/%d", categoryUrl, post.ThreadID, post.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
Loading…
Reference in New Issue