diff --git a/src/hmnurl/hmnurl.go b/src/hmnurl/hmnurl.go index f25ab71..143e542 100644 --- a/src/hmnurl/hmnurl.go +++ b/src/hmnurl/hmnurl.go @@ -72,14 +72,6 @@ func ProjectUrlWithFragment(path string, query []Q, slug string, fragment string return url.String() } -func StaticUrl(path string, query []Q) string { - return Url(StaticPath+"/"+trim(path), query) -} - -func StaticThemeUrl(path string, theme string, query []Q) string { - return Url(StaticThemePath+"/"+theme+"/"+trim(path), query) -} - func trim(path string) string { if len(path) > 0 && path[0] == '/' { return path[1:] diff --git a/src/hmnurl/hmnurl_test.go b/src/hmnurl/hmnurl_test.go index b7c778d..2d756d7 100644 --- a/src/hmnurl/hmnurl_test.go +++ b/src/hmnurl/hmnurl_test.go @@ -329,14 +329,17 @@ func TestProjectCSS(t *testing.T) { } func TestPublic(t *testing.T) { - AssertRegexMatch(t, BuildPublic("test"), RegexPublic, nil) - AssertRegexMatch(t, BuildPublic("/test"), RegexPublic, nil) - AssertRegexMatch(t, BuildPublic("/test/"), RegexPublic, nil) - AssertRegexMatch(t, BuildPublic("/test/thing/image.png"), RegexPublic, nil) - assert.Panics(t, func() { BuildPublic("") }) - assert.Panics(t, func() { BuildPublic("/") }) - assert.Panics(t, func() { BuildPublic("/thing//image.png") }) - assert.Panics(t, func() { BuildPublic("/thing/ /image.png") }) + AssertRegexMatch(t, BuildPublic("test", false), RegexPublic, nil) + AssertRegexMatch(t, BuildPublic("/test", true), RegexPublic, nil) + AssertRegexMatch(t, BuildPublic("/test/", false), RegexPublic, nil) + AssertRegexMatch(t, BuildPublic("/test/thing/image.png", true), RegexPublic, nil) + assert.Panics(t, func() { BuildPublic("", false) }) + assert.Panics(t, func() { BuildPublic("/", false) }) + assert.Panics(t, func() { BuildPublic("/thing//image.png", false) }) + assert.Panics(t, func() { BuildPublic("/thing/ /image.png", false) }) + assert.Panics(t, func() { BuildPublic("/thing/image.png?hello", false) }) + + AssertRegexMatch(t, BuildTheme("test.css", "light", true), RegexPublic, nil) } func TestMarkRead(t *testing.T) { diff --git a/src/hmnurl/urls.go b/src/hmnurl/urls.go index 869b644..1c0d275 100644 --- a/src/hmnurl/urls.go +++ b/src/hmnurl/urls.go @@ -1,6 +1,7 @@ package hmnurl import ( + "fmt" "net/url" "regexp" "strconv" @@ -577,11 +578,14 @@ func BuildProjectCSS(color string) string { var RegexPublic = regexp.MustCompile("^/public/.+$") -func BuildPublic(filepath string) string { +func BuildPublic(filepath string, cachebust bool) string { filepath = strings.Trim(filepath, "/") if len(strings.TrimSpace(filepath)) == 0 { panic(oops.New(nil, "Attempted to build a /public url with no path")) } + if strings.Contains(filepath, "?") { + panic(oops.New(nil, "Public url failpath must not contain query params")) + } var builder strings.Builder builder.WriteString("/public") pathParts := strings.Split(filepath, "/") @@ -593,7 +597,18 @@ func BuildPublic(filepath string) string { builder.WriteRune('/') builder.WriteString(part) } - return Url(builder.String(), nil) + var query []Q + if cachebust { + query = []Q{{"v", cacheBust}} + } + return Url(builder.String(), query) +} + +func BuildTheme(filepath string, theme string, cachebust bool) string { + if len(theme) == 0 { + panic(oops.New(nil, "Theme can't be blank")) + } + return BuildPublic(fmt.Sprintf("themes/%s/%s", theme, strings.Trim(filepath, "/")), cachebust) } /* diff --git a/src/templates/mapping.go b/src/templates/mapping.go index 9d3b307..148a2cf 100644 --- a/src/templates/mapping.go +++ b/src/templates/mapping.go @@ -8,10 +8,10 @@ import ( "git.handmade.network/hmn/hmn/src/models" ) -func PostToTemplate(p *models.Post, author *models.User) Post { +func PostToTemplate(p *models.Post, author *models.User, currentTheme string) Post { var authorUser *User if author != nil { - authorTmpl := UserToTemplate(author) + authorTmpl := UserToTemplate(author, currentTheme) authorUser = &authorTmpl } @@ -31,11 +31,11 @@ func PostToTemplate(p *models.Post, author *models.User) Post { } } -func (p *Post) AddContentVersion(ver models.PostVersion, editor *models.User) { +func (p *Post) AddContentVersion(ver models.PostVersion, editor *models.User, currentTheme string) { p.Content = template.HTML(ver.TextParsed) if editor != nil { - editorTmpl := UserToTemplate(editor) + editorTmpl := UserToTemplate(editor, currentTheme) p.Editor = &editorTmpl p.EditDate = ver.EditDate p.EditIP = maybeIp(ver.EditIP) @@ -76,12 +76,14 @@ func ThreadToTemplate(t *models.Thread) Thread { } } -func UserToTemplate(u *models.User) User { +func UserToTemplate(u *models.User, currentTheme string) User { // TODO: Handle deleted users. Maybe not here, but if not, at call sites of this function. avatar := "" - if u.Avatar != nil { - avatar = hmnurl.StaticUrl(*u.Avatar, nil) + if u.Avatar != nil && len(*u.Avatar) > 0 { + avatar = hmnurl.BuildPublic(*u.Avatar, false) + } else { + avatar = hmnurl.BuildTheme("empty-avatar.svg", currentTheme, true) } name := u.Name @@ -99,7 +101,7 @@ func UserToTemplate(u *models.User) User { Name: name, Blurb: u.Blurb, Signature: u.Signature, - AvatarUrl: avatar, // TODO + AvatarUrl: avatar, ProfileUrl: hmnurl.Url("m/"+u.Username, nil), DarkTheme: u.DarkTheme, diff --git a/src/templates/src/forum_thread.html b/src/templates/src/forum_thread.html index 834ae4a..3355b80 100644 --- a/src/templates/src/forum_thread.html +++ b/src/templates/src/forum_thread.html @@ -86,7 +86,9 @@ Edited by {{ coalesce .Editor.Name .Editor.Username }} - {{ if and $.User.IsStaff .EditIP }}[{{ .EditIP }}]{{ end }} + {{ if $.User }} + {{ if and $.User.IsStaff .EditIP }}[{{ .EditIP }}]{{ end }} + {{ end }} on {{ timehtml (absolutedate .EditDate) .EditDate }} {{ with .EditReason }} Reason: {{ . }} diff --git a/src/templates/src/include/post_list_item.html b/src/templates/src/include/post_list_item.html index a6f4e27..d34062d 100644 --- a/src/templates/src/include/post_list_item.html +++ b/src/templates/src/include/post_list_item.html @@ -26,4 +26,4 @@ It should be called with PostListItem.
»
- \ No newline at end of file + diff --git a/src/templates/src/include/thread_list_item.html b/src/templates/src/include/thread_list_item.html index cad6dcc..94f709b 100644 --- a/src/templates/src/include/thread_list_item.html +++ b/src/templates/src/include/thread_list_item.html @@ -33,4 +33,4 @@ It should be called with ThreadListItem.
»
- \ No newline at end of file + diff --git a/src/templates/templates.go b/src/templates/templates.go index a9403c8..381dedc 100644 --- a/src/templates/templates.go +++ b/src/templates/templates.go @@ -149,28 +149,21 @@ var HMNTemplateFuncs = template.FuncMap{ } }, "static": func(filepath string) string { - return hmnurl.StaticUrl(filepath, []hmnurl.Q{{"v", cachebust}}) + return hmnurl.BuildPublic(filepath, true) }, "staticnobust": func(filepath string) string { - return hmnurl.StaticUrl(filepath, nil) + return hmnurl.BuildPublic(filepath, false) }, "statictheme": func(theme string, filepath string) string { - return hmnurl.StaticThemeUrl(filepath, theme, []hmnurl.Q{{"v", cachebust}}) + return hmnurl.BuildTheme(filepath, theme, true) }, "staticthemenobust": func(theme string, filepath string) string { - return hmnurl.StaticThemeUrl(filepath, theme, nil) + return hmnurl.BuildTheme(filepath, theme, false) }, "timehtml": func(formatted string, t time.Time) template.HTML { iso := t.Format(time.RFC3339) return template.HTML(fmt.Sprintf(``, iso, formatted)) }, - "url": func(url string) string { - return hmnurl.Url(url, nil) - }, - "urlq": func(url string, query string) string { - absUrl := hmnurl.Url(url, nil) - return fmt.Sprintf("%s?%s", absUrl, query) - }, } type ErrInvalidHexColor struct { diff --git a/src/website/feed.go b/src/website/feed.go index 1d4d601..3005582 100644 --- a/src/website/feed.go +++ b/src/website/feed.go @@ -148,6 +148,7 @@ func Feed(c *RequestContext) ResponseData { postResult.LibraryResource, !hasRead, true, + c.Theme, )) } c.Perf.EndBlock() diff --git a/src/website/forums.go b/src/website/forums.go index 0a0087b..a169029 100644 --- a/src/website/forums.go +++ b/src/website/forums.go @@ -141,9 +141,9 @@ func ForumCategory(c *RequestContext) ResponseData { return templates.ThreadListItem{ Title: row.Thread.Title, Url: hmnurl.BuildForumThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(row.Thread.CategoryID), row.Thread.ID, row.Thread.Title, 1), - FirstUser: templates.UserToTemplate(row.FirstUser), + FirstUser: templates.UserToTemplate(row.FirstUser, c.Theme), FirstDate: row.FirstPost.PostDate, - LastUser: templates.UserToTemplate(row.LastUser), + LastUser: templates.UserToTemplate(row.LastUser, c.Theme), LastDate: row.LastPost.PostDate, Unread: !hasRead, @@ -403,8 +403,8 @@ func ForumThread(c *RequestContext) ResponseData { for _, irow := range itPosts.ToSlice() { row := irow.(*postsQueryResult) - post := templates.PostToTemplate(&row.Post, row.Author) - post.AddContentVersion(row.Ver, row.Editor) + post := templates.PostToTemplate(&row.Post, row.Author, c.Theme) + post.AddContentVersion(row.Ver, row.Editor, c.Theme) post.AddUrls(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, post.ID) posts = append(posts, post) diff --git a/src/website/landing.go b/src/website/landing.go index 9a5564f..2d53565 100644 --- a/src/website/landing.go +++ b/src/website/landing.go @@ -183,7 +183,7 @@ func Index(c *RequestContext) ResponseData { landingPageProject.FeaturedPost = &LandingPageFeaturedPost{ Title: projectPost.Thread.Title, Url: hmnurl.BuildBlogPost(proj.Slug, projectPost.Thread.ID, projectPost.Post.ID), - User: templates.UserToTemplate(&projectPost.User), + User: templates.UserToTemplate(&projectPost.User, c.Theme), Date: projectPost.Post.PostDate, Unread: !hasRead, Content: template.HTML(content), @@ -200,6 +200,7 @@ func Index(c *RequestContext) ResponseData { projectPost.LibraryResource, !hasRead, false, + c.Theme, ), ) } @@ -293,7 +294,7 @@ func Index(c *RequestContext) ResponseData { NewsPost: LandingPageFeaturedPost{ Title: newsPostResult.Thread.Title, Url: hmnurl.BuildBlogPost(models.HMNProjectSlug, newsPostResult.Thread.ID, newsPostResult.Post.ID), - User: templates.UserToTemplate(&newsPostResult.User), + User: templates.UserToTemplate(&newsPostResult.User, c.Theme), Date: newsPostResult.Post.PostDate, Unread: true, // TODO Content: template.HTML(newsPostResult.PostVersion.TextParsed), diff --git a/src/website/post_helper.go b/src/website/post_helper.go index 91590be..fd21045 100644 --- a/src/website/post_helper.go +++ b/src/website/post_helper.go @@ -30,11 +30,11 @@ func UrlForGenericPost(post *models.Post, subforums []string, threadTitle string } // NOTE(asaf): THIS DOESN'T HANDLE WIKI EDIT ITEMS. Wiki edits are PostTextVersions, not Posts. -func MakePostListItem(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, thread *models.Thread, post *models.Post, user *models.User, libraryResource *models.LibraryResource, unread bool, includeBreadcrumbs bool) templates.PostListItem { +func MakePostListItem(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, thread *models.Thread, post *models.Post, user *models.User, libraryResource *models.LibraryResource, unread bool, includeBreadcrumbs bool, currentTheme string) templates.PostListItem { var result templates.PostListItem result.Title = thread.Title - result.User = templates.UserToTemplate(user) + result.User = templates.UserToTemplate(user, currentTheme) result.Date = post.PostDate result.Unread = unread libraryResourceId := 0 diff --git a/src/website/requesthandling.go b/src/website/requesthandling.go index fa14b23..aa958c2 100644 --- a/src/website/requesthandling.go +++ b/src/website/requesthandling.go @@ -121,6 +121,7 @@ type RequestContext struct { Conn *pgxpool.Pool CurrentProject *models.Project CurrentUser *models.User + Theme string Perf *perf.RequestPerf } diff --git a/src/website/routes.go b/src/website/routes.go index c814263..5a9f3b3 100644 --- a/src/website/routes.go +++ b/src/website/routes.go @@ -133,7 +133,7 @@ func getBaseData(c *RequestContext) templates.BaseData { Project: templates.ProjectToTemplate(c.CurrentProject), LoginPageUrl: hmnurl.BuildLoginPage(c.FullUrl()), User: templateUser, - Theme: "light", + Theme: c.Theme, ProjectCSSUrl: hmnurl.BuildProjectCSS(c.CurrentProject.Color1), Header: templates.Header{ AdminUrl: hmnurl.BuildHomepage(), // TODO(asaf) @@ -279,6 +279,13 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) { // http.ErrNoCookie is the only error Cookie ever returns, so no further handling to do here. } + theme := "light" + if c.CurrentUser != nil && c.CurrentUser.DarkTheme { + theme = "dark" + } + + c.Theme = theme + return true, ResponseData{} }