Breadcrumbs
This commit is contained in:
parent
1f39b166cb
commit
d78a2e8e82
|
@ -88,7 +88,7 @@
|
|||
{{- if gt $i 0 -}}
|
||||
<span class="ph2">»</span>
|
||||
{{- end -}}
|
||||
<a class="breadcrumb {{ if .Current }}current{{ end }}" href="{{ .Url }}">{{ .Name }}</a>
|
||||
<a class="breadcrumb" href="{{ .Url }}">{{ .Name }}</a>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/oops"
|
||||
|
@ -34,6 +35,10 @@ func Int64Max(a, b int64) int64 {
|
|||
return b
|
||||
}
|
||||
|
||||
func NumPages(numThings, thingsPerPage int) int {
|
||||
return IntMax(int(math.Ceil(float64(numThings)/float64(thingsPerPage))), 1)
|
||||
}
|
||||
|
||||
/*
|
||||
Recover a panic and convert it to a returned error. Call it like so:
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ func LoginPage(c *RequestContext) ResponseData {
|
|||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("auth_login.html", LoginPageData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseDataAutocrumb(c, "Log in"),
|
||||
RedirectUrl: c.Req.URL.Query().Get("redirect"),
|
||||
ForgotPasswordUrl: hmnurl.BuildRequestPasswordReset(),
|
||||
}, c.Perf)
|
||||
|
@ -63,7 +63,7 @@ func Login(c *RequestContext) ResponseData {
|
|||
|
||||
showLoginWithFailure := func(c *RequestContext, redirect string) ResponseData {
|
||||
var res ResponseData
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseDataAutocrumb(c, "Log in")
|
||||
baseData.AddImmediateNotice("failure", "Incorrect username or password")
|
||||
res.MustWriteTemplate("auth_login.html", LoginPageData{
|
||||
BaseData: baseData,
|
||||
|
@ -122,7 +122,7 @@ func RegisterNewUser(c *RequestContext) ResponseData {
|
|||
}
|
||||
// TODO(asaf): Do something to prevent bot registration
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("auth_register.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("auth_register.html", getBaseDataAutocrumb(c, "Register"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -275,7 +275,7 @@ func RegisterNewUserSuccess(c *RequestContext) ResponseData {
|
|||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("auth_register_success.html", RegisterNewUserSuccessData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseDataAutocrumb(c, "Register"),
|
||||
ContactUsUrl: hmnurl.BuildContactPage(),
|
||||
}, c.Perf)
|
||||
return res
|
||||
|
@ -321,7 +321,7 @@ func EmailConfirmation(c *RequestContext) ResponseData {
|
|||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("auth_email_validation.html", EmailValidationData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseDataAutocrumb(c, "Register"),
|
||||
Token: token,
|
||||
Username: username,
|
||||
}, c.Perf)
|
||||
|
@ -346,11 +346,11 @@ func EmailConfirmationSubmit(c *RequestContext) ResponseData {
|
|||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
} else if !success {
|
||||
var res ResponseData
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseDataAutocrumb(c, "Register")
|
||||
// NOTE(asaf): We can report that the password is incorrect, because an attacker wouldn't have a valid token to begin with.
|
||||
baseData.AddImmediateNotice("failure", "Incorrect password. Please try again.")
|
||||
res.MustWriteTemplate("auth_email_validation.html", EmailValidationData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: baseData,
|
||||
Token: token,
|
||||
Username: username,
|
||||
}, c.Perf)
|
||||
|
@ -424,7 +424,7 @@ func RequestPasswordReset(c *RequestContext) ResponseData {
|
|||
return c.Redirect(hmnurl.BuildHomepage(), http.StatusSeeOther)
|
||||
}
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("auth_password_reset.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("auth_password_reset.html", getBaseDataAutocrumb(c, "Password Reset"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -548,7 +548,7 @@ func PasswordResetSent(c *RequestContext) ResponseData {
|
|||
}
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("auth_password_reset_sent.html", PasswordResetSentData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseDataAutocrumb(c, "Password Reset"),
|
||||
ContactUsUrl: hmnurl.BuildContactPage(),
|
||||
}, c.Perf)
|
||||
return res
|
||||
|
@ -582,7 +582,7 @@ func DoPasswordReset(c *RequestContext) ResponseData {
|
|||
}
|
||||
|
||||
res.MustWriteTemplate("auth_do_password_reset.html", DoPasswordResetData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseDataAutocrumb(c, "Password Reset"),
|
||||
Username: username,
|
||||
Token: token,
|
||||
}, c.Perf)
|
||||
|
|
|
@ -53,7 +53,7 @@ func BlogIndex(c *RequestContext) ResponseData {
|
|||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch total number of blog posts"))
|
||||
}
|
||||
|
||||
numPages := NumPages(numPosts, postsPerPage)
|
||||
numPages := utils.NumPages(numPosts, postsPerPage)
|
||||
page, ok := ParsePageNumber(c, "page", numPages)
|
||||
if !ok {
|
||||
c.Redirect(hmnurl.BuildBlog(c.CurrentProject.Slug, page), http.StatusSeeOther)
|
||||
|
@ -104,8 +104,7 @@ func BlogIndex(c *RequestContext) ResponseData {
|
|||
})
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = fmt.Sprintf("%s Blog", c.CurrentProject.Name)
|
||||
baseData := getBaseData(c, fmt.Sprintf("%s Blog", c.CurrentProject.Name), []templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)})
|
||||
|
||||
canCreate := false
|
||||
if c.CurrentUser != nil {
|
||||
|
@ -181,8 +180,7 @@ func BlogThread(c *RequestContext) ResponseData {
|
|||
templatePosts = append(templatePosts, post)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = thread.Title
|
||||
baseData := getBaseData(c, thread.Title, []templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)})
|
||||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("blog_post.html", blogPostData{
|
||||
|
@ -209,9 +207,11 @@ func BlogPostRedirectToThread(c *RequestContext) ResponseData {
|
|||
}
|
||||
|
||||
func BlogNewThread(c *RequestContext) ResponseData {
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = fmt.Sprintf("Create New Post | %s", c.CurrentProject.Name)
|
||||
// TODO(ben): Set breadcrumbs
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Create New Post | %s", c.CurrentProject.Name),
|
||||
[]templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)},
|
||||
)
|
||||
|
||||
editData := getEditorDataForNew(baseData, nil)
|
||||
editData.SubmitUrl = hmnurl.BuildBlogNewThread(c.CurrentProject.Slug)
|
||||
|
@ -284,13 +284,17 @@ func BlogPostEdit(c *RequestContext) ResponseData {
|
|||
|
||||
postData := FetchPostAndStuff(c.Context(), c.Conn, cd.ThreadID, cd.PostID)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
title := ""
|
||||
if postData.Thread.FirstID == postData.Post.ID {
|
||||
baseData.Title = fmt.Sprintf("Editing \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
title = fmt.Sprintf("Editing \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
} else {
|
||||
baseData.Title = fmt.Sprintf("Editing Post | %s", c.CurrentProject.Name)
|
||||
title = fmt.Sprintf("Editing Post | %s", c.CurrentProject.Name)
|
||||
}
|
||||
// TODO(ben): Set breadcrumbs
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
title,
|
||||
BlogThreadBreadcrumbs(c.CurrentProject.Slug, &postData.Thread),
|
||||
)
|
||||
|
||||
editData := getEditorDataForEdit(baseData, postData)
|
||||
editData.SubmitUrl = hmnurl.BuildBlogPostEdit(c.CurrentProject.Slug, cd.ThreadID, cd.PostID)
|
||||
|
@ -352,9 +356,11 @@ func BlogPostReply(c *RequestContext) ResponseData {
|
|||
|
||||
postData := FetchPostAndStuff(c.Context(), c.Conn, cd.ThreadID, cd.PostID)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = fmt.Sprintf("Replying to comment in \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
// TODO(ben): Set breadcrumbs
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Replying to comment in \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name),
|
||||
BlogThreadBreadcrumbs(c.CurrentProject.Slug, &postData.Thread),
|
||||
)
|
||||
|
||||
replyPost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
||||
replyPost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
||||
|
@ -412,12 +418,17 @@ func BlogPostDelete(c *RequestContext) ResponseData {
|
|||
|
||||
postData := FetchPostAndStuff(c.Context(), c.Conn, cd.ThreadID, cd.PostID)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
title := ""
|
||||
if postData.Thread.FirstID == postData.Post.ID {
|
||||
baseData.Title = fmt.Sprintf("Deleting \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
title = fmt.Sprintf("Deleting \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
} else {
|
||||
baseData.Title = fmt.Sprintf("Deleting comment in \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
title = fmt.Sprintf("Deleting comment in \"%s\" | %s", postData.Thread.Title, c.CurrentProject.Name)
|
||||
}
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
title,
|
||||
BlogThreadBreadcrumbs(c.CurrentProject.Slug, &postData.Thread),
|
||||
)
|
||||
// TODO(ben): Set breadcrumbs
|
||||
|
||||
templatePost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
package website
|
||||
|
||||
import (
|
||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
"git.handmade.network/hmn/hmn/src/templates"
|
||||
)
|
||||
|
||||
func ProjectBreadcrumb(project *models.Project) templates.Breadcrumb {
|
||||
return templates.Breadcrumb{
|
||||
Name: project.Name,
|
||||
Url: hmnurl.BuildProjectHomepage(project.Slug),
|
||||
}
|
||||
}
|
||||
|
||||
func ForumBreadcrumb(projectSlug string) templates.Breadcrumb {
|
||||
return templates.Breadcrumb{
|
||||
Name: "Forums",
|
||||
Url: hmnurl.BuildForum(projectSlug, nil, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func SubforumBreadcrumbs(lineageBuilder *models.SubforumLineageBuilder, project *models.Project, subforumID int) []templates.Breadcrumb {
|
||||
var result []templates.Breadcrumb
|
||||
result = []templates.Breadcrumb{
|
||||
ProjectBreadcrumb(project),
|
||||
ForumBreadcrumb(project.Slug),
|
||||
}
|
||||
subforums := lineageBuilder.GetSubforumLineage(subforumID)
|
||||
slugs := lineageBuilder.GetSubforumLineageSlugs(subforumID)
|
||||
for i, subforum := range subforums {
|
||||
result = append(result, templates.Breadcrumb{
|
||||
Name: subforum.Name,
|
||||
Url: hmnurl.BuildForum(project.Slug, slugs[0:i+1], 1),
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func ForumThreadBreadcrumbs(lineageBuilder *models.SubforumLineageBuilder, project *models.Project, thread *models.Thread) []templates.Breadcrumb {
|
||||
result := SubforumBreadcrumbs(lineageBuilder, project, *thread.SubforumID)
|
||||
result = append(result, templates.Breadcrumb{
|
||||
Name: thread.Title,
|
||||
Url: hmnurl.BuildForumThread(project.Slug, lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), thread.ID, thread.Title, 1),
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func BlogBreadcrumb(projectSlug string) templates.Breadcrumb {
|
||||
return templates.Breadcrumb{
|
||||
Name: "Blog",
|
||||
Url: hmnurl.BuildBlog(projectSlug, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func BlogThreadBreadcrumbs(projectSlug string, thread *models.Thread) []templates.Breadcrumb {
|
||||
result := []templates.Breadcrumb{
|
||||
BlogBreadcrumb(projectSlug),
|
||||
{Name: thread.Title, Url: hmnurl.BuildBlogThread(projectSlug, thread.ID, thread.Title)},
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -88,7 +88,7 @@ func EpisodeList(c *RequestContext) ResponseData {
|
|||
}
|
||||
|
||||
var res ResponseData
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseDataAutocrumb(c, fmt.Sprintf("Episode Guide"))
|
||||
res.MustWriteTemplate("episode_list.html", EpisodeListData{
|
||||
BaseData: baseData,
|
||||
Content: template.HTML(guide),
|
||||
|
@ -147,8 +147,11 @@ func Episode(c *RequestContext) ResponseData {
|
|||
content := contentMatches[episodeContentRegex.SubexpIndex("content")]
|
||||
|
||||
var res ResponseData
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = title
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
title,
|
||||
[]templates.Breadcrumb{{Name: "Episode Guide", Url: hmnurl.BuildEpisodeList(c.CurrentProject.Slug, foundTopic)}},
|
||||
)
|
||||
res.MustWriteTemplate("episode.html", EpisodeData{
|
||||
BaseData: baseData,
|
||||
Content: template.HTML(content),
|
||||
|
|
|
@ -90,7 +90,7 @@ func Feed(c *RequestContext) ResponseData {
|
|||
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch feed posts"))
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseDataAutocrumb(c, "Feed")
|
||||
baseData.BodyClasses = append(baseData.BodyClasses, "feed")
|
||||
|
||||
var res ResponseData
|
||||
|
|
|
@ -100,7 +100,7 @@ func Forum(c *RequestContext) ResponseData {
|
|||
}
|
||||
c.Perf.EndBlock()
|
||||
|
||||
numPages := NumPages(numThreads, threadsPerPage)
|
||||
numPages := utils.NumPages(numThreads, threadsPerPage)
|
||||
page, ok := ParsePageNumber(c, "page", numPages)
|
||||
if !ok {
|
||||
c.Redirect(hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, page), http.StatusSeeOther)
|
||||
|
@ -259,27 +259,11 @@ func Forum(c *RequestContext) ResponseData {
|
|||
// Template assembly
|
||||
// ---------------------
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = c.CurrentProject.Name + " Forums"
|
||||
baseData.Breadcrumbs = []templates.Breadcrumb{
|
||||
{
|
||||
Name: c.CurrentProject.Name,
|
||||
Url: hmnurl.BuildProjectHomepage(c.CurrentProject.Slug),
|
||||
},
|
||||
{
|
||||
Name: "Forums",
|
||||
Url: hmnurl.BuildForum(c.CurrentProject.Slug, nil, 1),
|
||||
Current: true,
|
||||
},
|
||||
}
|
||||
|
||||
currentSubforums := cd.LineageBuilder.GetSubforumLineage(cd.SubforumID)
|
||||
for i, subforum := range currentSubforums {
|
||||
baseData.Breadcrumbs = append(baseData.Breadcrumbs, templates.Breadcrumb{
|
||||
Name: subforum.Name,
|
||||
Url: hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs[0:i+1], 1),
|
||||
})
|
||||
}
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("%s Forums", c.CurrentProject.Name),
|
||||
SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID),
|
||||
)
|
||||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("forum.html", forumData{
|
||||
|
@ -511,9 +495,7 @@ func ForumThread(c *RequestContext) ResponseData {
|
|||
}
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = thread.Title
|
||||
// TODO(asaf): Set breadcrumbs
|
||||
baseData := getBaseData(c, thread.Title, SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID))
|
||||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("forum_thread.html", forumThreadData{
|
||||
|
@ -596,15 +578,12 @@ func ForumPostRedirect(c *RequestContext) ResponseData {
|
|||
}
|
||||
|
||||
func ForumNewThread(c *RequestContext) ResponseData {
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = "Create New Thread"
|
||||
// TODO(ben): Set breadcrumbs
|
||||
|
||||
cd, ok := getCommonForumData(c)
|
||||
if !ok {
|
||||
return FourOhFour(c)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c, "Create New Thread", SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID))
|
||||
editData := getEditorDataForNew(baseData, nil)
|
||||
editData.SubmitUrl = hmnurl.BuildForumNewThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), true)
|
||||
editData.SubmitLabel = "Post New Thread"
|
||||
|
@ -683,9 +662,11 @@ func ForumPostReply(c *RequestContext) ResponseData {
|
|||
|
||||
postData := FetchPostAndStuff(c.Context(), c.Conn, cd.ThreadID, cd.PostID)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = fmt.Sprintf("Replying to post | %s", cd.SubforumTree[cd.SubforumID].Name)
|
||||
// TODO(ben): Set breadcrumbs
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Replying to post | %s", cd.SubforumTree[cd.SubforumID].Name),
|
||||
ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &postData.Thread),
|
||||
)
|
||||
|
||||
replyPost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
||||
replyPost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
||||
|
@ -743,13 +724,13 @@ func ForumPostEdit(c *RequestContext) ResponseData {
|
|||
|
||||
postData := FetchPostAndStuff(c.Context(), c.Conn, cd.ThreadID, cd.PostID)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
title := ""
|
||||
if postData.Thread.FirstID == postData.Post.ID {
|
||||
baseData.Title = fmt.Sprintf("Editing \"%s\" | %s", postData.Thread.Title, cd.SubforumTree[cd.SubforumID].Name)
|
||||
title = fmt.Sprintf("Editing \"%s\" | %s", postData.Thread.Title, cd.SubforumTree[cd.SubforumID].Name)
|
||||
} else {
|
||||
baseData.Title = fmt.Sprintf("Editing Post | %s", cd.SubforumTree[cd.SubforumID].Name)
|
||||
title = fmt.Sprintf("Editing Post | %s", cd.SubforumTree[cd.SubforumID].Name)
|
||||
}
|
||||
// TODO(ben): Set breadcrumbs
|
||||
baseData := getBaseData(c, title, ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &postData.Thread))
|
||||
|
||||
editData := getEditorDataForEdit(baseData, postData)
|
||||
editData.SubmitUrl = hmnurl.BuildForumPostEdit(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID)
|
||||
|
@ -806,9 +787,11 @@ func ForumPostDelete(c *RequestContext) ResponseData {
|
|||
|
||||
postData := FetchPostAndStuff(c.Context(), c.Conn, cd.ThreadID, cd.PostID)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = fmt.Sprintf("Deleting post in \"%s\" | %s", postData.Thread.Title, cd.SubforumTree[cd.SubforumID].Name)
|
||||
// TODO(ben): Set breadcrumbs
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Deleting post in \"%s\" | %s", postData.Thread.Title, cd.SubforumTree[cd.SubforumID].Name),
|
||||
ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &postData.Thread),
|
||||
)
|
||||
|
||||
templatePost := templates.PostToTemplate(&postData.Post, postData.Author, c.Theme)
|
||||
templatePost.AddContentVersion(postData.CurrentVersion, postData.Editor)
|
||||
|
|
|
@ -13,8 +13,7 @@ func JamIndex(c *RequestContext) ResponseData {
|
|||
ogimageurl = urljoin(current_site_host(), ogimagepath)
|
||||
*/
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = "Wheel Reinvention Jam"
|
||||
baseData := getBaseDataAutocrumb(c, "Wheel Reinvention Jam")
|
||||
baseData.OpenGraphItems = []templates.OpenGraphItem{
|
||||
{Property: "og:site_name", Value: "Handmade.Network"},
|
||||
{Property: "og:type", Value: "website"},
|
||||
|
|
|
@ -316,7 +316,7 @@ func Index(c *RequestContext) ResponseData {
|
|||
showcaseJson := templates.TimelineItemsToJSON(showcaseItems)
|
||||
c.Perf.EndBlock()
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseData(c, "", nil)
|
||||
baseData.BodyClasses = append(baseData.BodyClasses, "hmdev", "landing") // TODO: Is "hmdev" necessary any more?
|
||||
|
||||
var res ResponseData
|
||||
|
|
|
@ -44,8 +44,7 @@ func PodcastIndex(c *RequestContext) ResponseData {
|
|||
return c.ErrorResponse(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = podcastResult.Podcast.Title
|
||||
baseData := getBaseDataAutocrumb(c, podcastResult.Podcast.Title)
|
||||
|
||||
podcastIndexData := PodcastIndexData{
|
||||
BaseData: baseData,
|
||||
|
@ -89,8 +88,11 @@ func PodcastEdit(c *RequestContext) ResponseData {
|
|||
}
|
||||
|
||||
podcast := templates.PodcastToTemplate(c.CurrentProject.Slug, podcastResult.Podcast, podcastResult.ImageFile)
|
||||
baseData := getBaseData(c)
|
||||
baseData.Breadcrumbs = []templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}}
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Edit %s", podcast.Title),
|
||||
[]templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}},
|
||||
)
|
||||
podcastEditData := PodcastEditData{
|
||||
BaseData: baseData,
|
||||
Podcast: podcast,
|
||||
|
@ -229,9 +231,11 @@ func PodcastEpisode(c *RequestContext) ResponseData {
|
|||
|
||||
podcast := templates.PodcastToTemplate(c.CurrentProject.Slug, podcastResult.Podcast, podcastResult.ImageFile)
|
||||
episode := templates.PodcastEpisodeToTemplate(c.CurrentProject.Slug, podcastResult.Episodes[0], 0, podcastResult.ImageFile)
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = podcastResult.Podcast.Title
|
||||
baseData.Breadcrumbs = []templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}}
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("%s | %s", episode.Title, podcast.Title),
|
||||
[]templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}},
|
||||
)
|
||||
|
||||
podcastEpisodeData := PodcastEpisodeData{
|
||||
BaseData: baseData,
|
||||
|
@ -280,8 +284,11 @@ func PodcastEpisodeNew(c *RequestContext) ResponseData {
|
|||
|
||||
podcast := templates.PodcastToTemplate(c.CurrentProject.Slug, podcastResult.Podcast, "")
|
||||
var res ResponseData
|
||||
baseData := getBaseData(c)
|
||||
baseData.Breadcrumbs = []templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}}
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("New episode | %s", podcast.Title),
|
||||
[]templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}},
|
||||
)
|
||||
err = res.WriteTemplate("podcast_episode_edit.html", PodcastEpisodeEditData{
|
||||
BaseData: baseData,
|
||||
IsEdit: false,
|
||||
|
@ -321,8 +328,11 @@ func PodcastEpisodeEdit(c *RequestContext) ResponseData {
|
|||
|
||||
podcast := templates.PodcastToTemplate(c.CurrentProject.Slug, podcastResult.Podcast, "")
|
||||
podcastEpisode := templates.PodcastEpisodeToTemplate(c.CurrentProject.Slug, episode, 0, "")
|
||||
baseData := getBaseData(c)
|
||||
baseData.Breadcrumbs = []templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}, {Name: podcastEpisode.Title, Url: podcastEpisode.Url}}
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Edit episode %s | %s", podcastEpisode.Title, podcast.Title),
|
||||
[]templates.Breadcrumb{{Name: podcast.Title, Url: podcast.Url}, {Name: podcastEpisode.Title, Url: podcastEpisode.Url}},
|
||||
)
|
||||
podcastEpisodeEditData := PodcastEpisodeEditData{
|
||||
BaseData: baseData,
|
||||
IsEdit: true,
|
||||
|
|
|
@ -31,30 +31,40 @@ var PostTypePrefix = map[templates.PostType]string{
|
|||
templates.PostTypeForumReply: "Forum reply",
|
||||
}
|
||||
|
||||
func PostBreadcrumbs(lineageBuilder *models.SubforumLineageBuilder, project *models.Project, thread *models.Thread) []templates.Breadcrumb {
|
||||
var ThreadTypeDisplayNames = map[models.ThreadType]string{
|
||||
models.ThreadTypeProjectBlogPost: "Blog",
|
||||
models.ThreadTypeForumPost: "Forums",
|
||||
}
|
||||
|
||||
func GenericThreadBreadcrumbs(lineageBuilder *models.SubforumLineageBuilder, project *models.Project, thread *models.Thread) []templates.Breadcrumb {
|
||||
var result []templates.Breadcrumb
|
||||
result = append(result, templates.Breadcrumb{
|
||||
Name: project.Name,
|
||||
Url: hmnurl.BuildProjectHomepage(project.Slug),
|
||||
})
|
||||
result = append(result, templates.Breadcrumb{
|
||||
Name: ThreadTypeDisplayNames[thread.Type],
|
||||
Url: BuildProjectRootResourceUrl(project.Slug, thread.Type),
|
||||
})
|
||||
switch thread.Type {
|
||||
case models.ThreadTypeForumPost:
|
||||
subforums := lineageBuilder.GetSubforumLineage(*thread.SubforumID)
|
||||
slugs := lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID)
|
||||
for i, subforum := range subforums {
|
||||
result = append(result, templates.Breadcrumb{
|
||||
Name: subforum.Name,
|
||||
Url: hmnurl.BuildForum(project.Slug, slugs[0:i+1], 1),
|
||||
})
|
||||
if thread.Type == models.ThreadTypeForumPost {
|
||||
result = SubforumBreadcrumbs(lineageBuilder, project, *thread.SubforumID)
|
||||
} else {
|
||||
result = []templates.Breadcrumb{
|
||||
{
|
||||
Name: project.Name,
|
||||
Url: hmnurl.BuildProjectHomepage(project.Slug),
|
||||
},
|
||||
{
|
||||
Name: ThreadTypeDisplayNames[thread.Type],
|
||||
Url: BuildProjectRootResourceUrl(project.Slug, thread.Type),
|
||||
},
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func BuildProjectRootResourceUrl(projectSlug string, kind models.ThreadType) string {
|
||||
switch kind {
|
||||
case models.ThreadTypeProjectBlogPost:
|
||||
return hmnurl.BuildBlog(projectSlug, 1)
|
||||
case models.ThreadTypeForumPost:
|
||||
return hmnurl.BuildForum(projectSlug, nil, 1)
|
||||
}
|
||||
return hmnurl.BuildProjectHomepage(projectSlug)
|
||||
}
|
||||
|
||||
func MakePostListItem(
|
||||
lineageBuilder *models.SubforumLineageBuilder,
|
||||
project *models.Project,
|
||||
|
@ -87,7 +97,7 @@ func MakePostListItem(
|
|||
result.PostTypePrefix = PostTypePrefix[result.PostType]
|
||||
|
||||
if includeBreadcrumbs {
|
||||
result.Breadcrumbs = PostBreadcrumbs(lineageBuilder, project, thread)
|
||||
result.Breadcrumbs = GenericThreadBreadcrumbs(lineageBuilder, project, thread)
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
@ -181,8 +181,7 @@ func ProjectIndex(c *RequestContext) ResponseData {
|
|||
}
|
||||
c.Perf.EndBlock()
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = "Project List"
|
||||
baseData := getBaseDataAutocrumb(c, "Project List")
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("project_index.html", ProjectTemplateData{
|
||||
BaseData: baseData,
|
||||
|
@ -369,7 +368,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
|||
|
||||
var projectHomepageData ProjectHomepageData
|
||||
|
||||
projectHomepageData.BaseData = getBaseData(c)
|
||||
projectHomepageData.BaseData = getBaseData(c, project.Name, nil)
|
||||
if canEdit {
|
||||
projectHomepageData.BaseData.Header.EditUrl = hmnurl.BuildProjectEdit(project.Slug, "")
|
||||
}
|
||||
|
|
|
@ -280,7 +280,7 @@ func (c *RequestContext) ErrorResponse(status int, errs ...error) ResponseData {
|
|||
StatusCode: status,
|
||||
Errors: errs,
|
||||
}
|
||||
res.MustWriteTemplate("error.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("error.html", getBaseData(c, "", nil), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
|
@ -252,7 +252,13 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
|||
return router
|
||||
}
|
||||
|
||||
func getBaseData(c *RequestContext) templates.BaseData {
|
||||
func getBaseDataAutocrumb(c *RequestContext, title string) templates.BaseData {
|
||||
return getBaseData(c, title, []templates.Breadcrumb{{Name: title, Url: ""}})
|
||||
}
|
||||
|
||||
// NOTE(asaf): If you set breadcrumbs, the breadcrumb for the current project will automatically be prepended when necessary.
|
||||
// If you pass nil, no breadcrumbs will be created.
|
||||
func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadcrumb) templates.BaseData {
|
||||
var templateUser *templates.User
|
||||
var templateSession *templates.Session
|
||||
if c.CurrentUser != nil {
|
||||
|
@ -264,6 +270,17 @@ func getBaseData(c *RequestContext) templates.BaseData {
|
|||
|
||||
notices := getNoticesFromCookie(c)
|
||||
|
||||
if len(breadcrumbs) > 0 {
|
||||
projectUrl := hmnurl.BuildProjectHomepage(c.CurrentProject.Slug)
|
||||
if breadcrumbs[0].Url != projectUrl {
|
||||
rootBreadcrumb := templates.Breadcrumb{
|
||||
Name: c.CurrentProject.Name,
|
||||
Url: projectUrl,
|
||||
}
|
||||
breadcrumbs = append([]templates.Breadcrumb{rootBreadcrumb}, breadcrumbs...)
|
||||
}
|
||||
}
|
||||
|
||||
episodeGuideUrl := ""
|
||||
defaultTopic, hasAnnotations := config.Config.EpisodeGuide.Projects[c.CurrentProject.Slug]
|
||||
if hasAnnotations {
|
||||
|
@ -271,7 +288,9 @@ func getBaseData(c *RequestContext) templates.BaseData {
|
|||
}
|
||||
|
||||
baseData := templates.BaseData{
|
||||
Theme: c.Theme,
|
||||
Theme: c.Theme,
|
||||
Title: title,
|
||||
Breadcrumbs: breadcrumbs,
|
||||
|
||||
CurrentUrl: c.FullUrl(),
|
||||
LoginPageUrl: hmnurl.BuildLoginPage(c.FullUrl()),
|
||||
|
@ -365,7 +384,7 @@ func ProjectCSS(c *RequestContext) ResponseData {
|
|||
return c.ErrorResponse(http.StatusBadRequest, NewSafeError(nil, "You must provide a 'color' parameter.\n"))
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseData(c, "", nil)
|
||||
|
||||
bgColor := noire.NewHex(color)
|
||||
h, s, l := bgColor.HSL()
|
||||
|
@ -408,7 +427,7 @@ func FourOhFour(c *RequestContext) ResponseData {
|
|||
templates.BaseData
|
||||
Wanted string
|
||||
}{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseData(c, "Page not found", nil),
|
||||
Wanted: c.FullUrl(),
|
||||
}
|
||||
res.MustWriteTemplate("404.html", templateData, c.Perf)
|
||||
|
@ -426,7 +445,7 @@ type RejectData struct {
|
|||
func RejectRequest(c *RequestContext, reason string) ResponseData {
|
||||
var res ResponseData
|
||||
err := res.WriteTemplate("reject.html", RejectData{
|
||||
BaseData: getBaseData(c),
|
||||
BaseData: getBaseData(c, "Rejected", nil),
|
||||
RejectReason: reason,
|
||||
}, c.Perf)
|
||||
if err != nil {
|
||||
|
|
|
@ -53,8 +53,7 @@ func Showcase(c *RequestContext) ResponseData {
|
|||
jsonItems := templates.TimelineItemsToJSON(showcaseItems)
|
||||
c.Perf.EndBlock()
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = "Community Showcase"
|
||||
baseData := getBaseDataAutocrumb(c, "Community Showcase")
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("showcase.html", ShowcaseData{
|
||||
BaseData: baseData,
|
||||
|
|
|
@ -103,7 +103,11 @@ func Snippet(c *RequestContext) ResponseData {
|
|||
opengraph = append(opengraph, opengraphYoutube...)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData := getBaseData(
|
||||
c,
|
||||
fmt.Sprintf("Snippet by %s", snippet.OwnerName),
|
||||
[]templates.Breadcrumb{{Name: snippet.OwnerName, Url: snippet.OwnerUrl}},
|
||||
)
|
||||
baseData.OpenGraphItems = opengraph // NOTE(asaf): We're overriding the defaults on purpose.
|
||||
var res ResponseData
|
||||
err = res.WriteTemplate("snippet.html", SnippetData{
|
||||
|
|
|
@ -2,42 +2,42 @@ package website
|
|||
|
||||
func Manifesto(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("manifesto.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("manifesto.html", getBaseDataAutocrumb(c, "Manifesto"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
func About(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("about.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("about.html", getBaseDataAutocrumb(c, "About"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
func CodeOfConduct(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("code_of_conduct.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("code_of_conduct.html", getBaseDataAutocrumb(c, "Code of Conduct"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
func CommunicationGuidelines(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("communication_guidelines.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("communication_guidelines.html", getBaseDataAutocrumb(c, "Communication Guidelines"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
func ContactPage(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("contact.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("contact.html", getBaseDataAutocrumb(c, "Contact Us"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
func MonthlyUpdatePolicy(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("monthly_update_policy.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("monthly_update_policy.html", getBaseDataAutocrumb(c, "Monthly Update Policy"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
||||
func ProjectSubmissionGuidelines(c *RequestContext) ResponseData {
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("project_submission_guidelines.html", getBaseData(c), c.Perf)
|
||||
res.MustWriteTemplate("project_submission_guidelines.html", getBaseDataAutocrumb(c, "Project Submission Guidelines"), c.Perf)
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package website
|
||||
|
||||
import (
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
)
|
||||
|
||||
var ThreadTypeDisplayNames = map[models.ThreadType]string{
|
||||
models.ThreadTypeProjectBlogPost: "Blog",
|
||||
models.ThreadTypeForumPost: "Forums",
|
||||
}
|
|
@ -70,7 +70,7 @@ func PostToTimelineItem(lineageBuilder *models.SubforumLineageBuilder, post *mod
|
|||
Description: "", // NOTE(asaf): No description for posts
|
||||
|
||||
Title: thread.Title,
|
||||
Breadcrumbs: PostBreadcrumbs(lineageBuilder, project, thread),
|
||||
Breadcrumbs: GenericThreadBreadcrumbs(lineageBuilder, project, thread),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package website
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/hmnurl"
|
||||
"git.handmade.network/hmn/hmn/src/models"
|
||||
"git.handmade.network/hmn/hmn/src/utils"
|
||||
)
|
||||
|
||||
func NumPages(numThings, thingsPerPage int) int {
|
||||
return utils.IntMax(int(math.Ceil(float64(numThings)/float64(thingsPerPage))), 1)
|
||||
}
|
||||
|
||||
func BuildProjectRootResourceUrl(projectSlug string, kind models.ThreadType) string {
|
||||
switch kind {
|
||||
case models.ThreadTypeProjectBlogPost:
|
||||
return hmnurl.BuildBlog(projectSlug, 1)
|
||||
case models.ThreadTypeForumPost:
|
||||
return hmnurl.BuildForum(projectSlug, nil, 1)
|
||||
}
|
||||
return hmnurl.BuildProjectHomepage(projectSlug)
|
||||
}
|
|
@ -224,8 +224,7 @@ func UserProfile(c *RequestContext) ResponseData {
|
|||
|
||||
templateUser := templates.UserToTemplate(profileUser, c.Theme)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = templateUser.Name
|
||||
baseData := getBaseDataAutocrumb(c, templateUser.Name)
|
||||
|
||||
var res ResponseData
|
||||
res.MustWriteTemplate("user_profile.html", UserProfileTemplateData{
|
||||
|
@ -322,8 +321,7 @@ func UserSettings(c *RequestContext) ResponseData {
|
|||
|
||||
templateUser := templates.UserToTemplate(c.CurrentUser, c.Theme)
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = templateUser.Name
|
||||
baseData := getBaseDataAutocrumb(c, templateUser.Name)
|
||||
|
||||
res.MustWriteTemplate("user_settings.html", UserSettingsTemplateData{
|
||||
BaseData: baseData,
|
||||
|
|
|
@ -25,8 +25,7 @@ func WhenIsIt(c *RequestContext) ResponseData {
|
|||
hasTimestamp = (err == nil)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = "When is it?"
|
||||
baseData := getBaseDataAutocrumb(c, "When is it?")
|
||||
|
||||
baseData.OpenGraphItems = append(baseData.OpenGraphItems, templates.OpenGraphItem{
|
||||
Property: "og:title",
|
||||
|
|
Loading…
Reference in New Issue