From dfbcfbeacc44176077f3cb6717bfea4e45054cd0 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Tue, 9 Nov 2021 20:11:39 -0800 Subject: [PATCH] Use new UrlContext for project URLs Wow that was a lot to change --- src/hmnurl/hmnurl.go | 44 ++++------ src/hmnurl/urls.go | 140 ++++++++++++++++++++----------- src/templates/mapping.go | 18 +--- src/templates/types.go | 5 +- src/website/admin.go | 2 +- src/website/base_data.go | 16 ++-- src/website/blogs.go | 69 ++++++++------- src/website/breadcrumb_helper.go | 34 ++++---- src/website/episode_guide.go | 11 ++- src/website/feed.go | 4 +- src/website/forums.go | 99 +++++++++++----------- src/website/landing.go | 6 +- src/website/post_helper.go | 40 ++++----- src/website/project_helper.go | 14 ++-- src/website/projects.go | 16 ++-- src/website/requesthandling.go | 1 + src/website/routes.go | 14 ++-- src/website/timeline_helper.go | 6 +- src/website/user.go | 8 +- 19 files changed, 288 insertions(+), 259 deletions(-) diff --git a/src/hmnurl/hmnurl.go b/src/hmnurl/hmnurl.go index 354f4d1..be701d9 100644 --- a/src/hmnurl/hmnurl.go +++ b/src/hmnurl/hmnurl.go @@ -6,8 +6,9 @@ import ( "regexp" "time" - "git.handmade.network/hmn/hmn/src/config" "git.handmade.network/hmn/hmn/src/models" + + "git.handmade.network/hmn/hmn/src/config" "git.handmade.network/hmn/hmn/src/oops" ) @@ -62,34 +63,25 @@ func GetBaseHost() string { return baseUrlParsed.Host } +type UrlContext struct { + PersonalProject bool + ProjectID int + ProjectSlug string + ProjectName string +} + +var HMNProjectContext = UrlContext{ + PersonalProject: false, + ProjectID: models.HMNProjectID, + ProjectSlug: models.HMNProjectSlug, +} + func Url(path string, query []Q) string { - return ProjectUrl(path, query, "") + return UrlWithFragment(path, query, "") } -func ProjectUrl(path string, query []Q, slug string) string { - return ProjectUrlWithFragment(path, query, slug, "") -} - -func ProjectUrlWithFragment(path string, query []Q, slug string, fragment string) string { - subdomain := slug - if slug == models.HMNProjectSlug { - subdomain = "" - } - - host := baseUrlParsed.Host - if len(subdomain) > 0 { - host = slug + "." + host - } - - url := url.URL{ - Scheme: baseUrlParsed.Scheme, - Host: host, - Path: trim(path), - RawQuery: encodeQuery(query), - Fragment: fragment, - } - - return url.String() +func UrlWithFragment(path string, query []Q, fragment string) string { + return HMNProjectContext.UrlWithFragment(path, query, fragment) } func trim(path string) string { diff --git a/src/hmnurl/urls.go b/src/hmnurl/urls.go index 3975b3c..2d269ab 100644 --- a/src/hmnurl/urls.go +++ b/src/hmnurl/urls.go @@ -21,12 +21,11 @@ var RegexOldHome = regexp.MustCompile("^/home$") var RegexHomepage = regexp.MustCompile("^/$") func BuildHomepage() string { - return Url("/", nil) + return HMNProjectContext.BuildHomepage() } -func BuildOfficialProjectHomepage(projectSlug string) string { - defer CatchPanic() - return ProjectUrl("/", nil, projectSlug) +func (c *UrlContext) BuildHomepage() string { + return c.Url("/", nil) } var RegexShowcase = regexp.MustCompile("^/showcase$") @@ -196,7 +195,7 @@ func BuildUserProfile(username string) string { var RegexUserSettings = regexp.MustCompile(`^/settings$`) func BuildUserSettings(section string) string { - return ProjectUrlWithFragment("/settings", nil, "", section) + return UrlWithFragment("/settings", nil, section) } /* @@ -302,10 +301,10 @@ func BuildPersonalProject(id int, slug string) string { var RegexProjectEdit = regexp.MustCompile("^/edit$") -func BuildProjectEdit(slug string, section string) string { +func (c *UrlContext) BuildProjectEdit(section string) string { defer CatchPanic() - return ProjectUrlWithFragment(fmt.Sprintf("/p/%s/edit", slug), nil, "", section) + return c.UrlWithFragment("/edit", nil, section) } /* @@ -367,7 +366,50 @@ func BuildPodcastEpisodeFile(filename string) string { // Make sure to match Thread before Subforum in the router. var RegexForum = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?(/(?P\d+))?$`) -func BuildForum(projectSlug string, subforums []string, page int) string { +func (c *UrlContext) Url(path string, query []Q) string { + return c.UrlWithFragment(path, query, "") +} + +func (c *UrlContext) UrlWithFragment(path string, query []Q, fragment string) string { + if c == nil { + logging.Warn().Stack().Msg("URL context was nil; defaulting to the HMN URL context") + c = &HMNProjectContext + } + + if c.PersonalProject { + url := url.URL{ + Scheme: baseUrlParsed.Scheme, + Host: baseUrlParsed.Host, + Path: fmt.Sprintf("p/%d/%s/%s", c.ProjectID, models.GeneratePersonalProjectSlug(c.ProjectName), trim(path)), + RawQuery: encodeQuery(query), + Fragment: fragment, + } + + return url.String() + } else { + subdomain := c.ProjectSlug + if c.ProjectSlug == models.HMNProjectSlug { + subdomain = "" + } + + host := baseUrlParsed.Host + if len(subdomain) > 0 { + host = c.ProjectSlug + "." + host + } + + url := url.URL{ + Scheme: baseUrlParsed.Scheme, + Host: host, + Path: trim(path), + RawQuery: encodeQuery(query), + Fragment: fragment, + } + + return url.String() + } +} + +func (c *UrlContext) BuildForum(subforums []string, page int) string { defer CatchPanic() if page < 1 { panic(oops.New(nil, "Invalid forum thread page (%d), must be >= 1", page)) @@ -380,13 +422,13 @@ func BuildForum(projectSlug string, subforums []string, page int) string { builder.WriteString(strconv.Itoa(page)) } - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexForumNewThread = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/new$`) var RegexForumNewThreadSubmit = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/new/submit$`) -func BuildForumNewThread(projectSlug string, subforums []string, submit bool) string { +func (c *UrlContext) BuildForumNewThread(subforums []string, submit bool) string { defer CatchPanic() builder := buildSubforumPath(subforums) builder.WriteString("/t/new") @@ -394,59 +436,59 @@ func BuildForumNewThread(projectSlug string, subforums []string, submit bool) st builder.WriteString("/submit") } - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexForumThread = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/(?P\d+)(-([^/]+))?(/(?P\d+))?$`) -func BuildForumThread(projectSlug string, subforums []string, threadId int, title string, page int) string { +func (c *UrlContext) BuildForumThread(subforums []string, threadId int, title string, page int) string { defer CatchPanic() builder := buildForumThreadPath(subforums, threadId, title, page) - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } -func BuildForumThreadWithPostHash(projectSlug string, subforums []string, threadId int, title string, page int, postId int) string { +func (c *UrlContext) BuildForumThreadWithPostHash(subforums []string, threadId int, title string, page int, postId int) string { defer CatchPanic() builder := buildForumThreadPath(subforums, threadId, title, page) - return ProjectUrlWithFragment(builder.String(), nil, projectSlug, strconv.Itoa(postId)) + return UrlWithFragment(builder.String(), nil, strconv.Itoa(postId)) } var RegexForumPost = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/(?P\d+)/p/(?P\d+)$`) -func BuildForumPost(projectSlug string, subforums []string, threadId int, postId int) string { +func (c *UrlContext) BuildForumPost(subforums []string, threadId int, postId int) string { defer CatchPanic() builder := buildForumPostPath(subforums, threadId, postId) - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexForumPostDelete = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/(?P\d+)/p/(?P\d+)/delete$`) -func BuildForumPostDelete(projectSlug string, subforums []string, threadId int, postId int) string { +func (c *UrlContext) BuildForumPostDelete(subforums []string, threadId int, postId int) string { defer CatchPanic() builder := buildForumPostPath(subforums, threadId, postId) builder.WriteString("/delete") - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexForumPostEdit = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/(?P\d+)/p/(?P\d+)/edit$`) -func BuildForumPostEdit(projectSlug string, subforums []string, threadId int, postId int) string { +func (c *UrlContext) BuildForumPostEdit(subforums []string, threadId int, postId int) string { defer CatchPanic() builder := buildForumPostPath(subforums, threadId, postId) builder.WriteString("/edit") - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexForumPostReply = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/(?P\d+)/p/(?P\d+)/reply$`) -func BuildForumPostReply(projectSlug string, subforums []string, threadId int, postId int) string { +func (c *UrlContext) BuildForumPostReply(subforums []string, threadId int, postId int) string { defer CatchPanic() builder := buildForumPostPath(subforums, threadId, postId) builder.WriteString("/reply") - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexWikiArticle = regexp.MustCompile(`^/wiki/(?P\d+)(-([^/]+))?$`) @@ -459,7 +501,7 @@ var RegexBlogsRedirect = regexp.MustCompile(`^/blogs(?P.*)`) var RegexBlog = regexp.MustCompile(`^/blog(/(?P\d+))?$`) -func BuildBlog(projectSlug string, page int) string { +func (c *UrlContext) BuildBlog(page int) string { defer CatchPanic() if page < 1 { panic(oops.New(nil, "Invalid blog page (%d), must be >= 1", page)) @@ -470,63 +512,63 @@ func BuildBlog(projectSlug string, page int) string { path += "/" + strconv.Itoa(page) } - return ProjectUrl(path, nil, projectSlug) + return c.Url(path, nil) } var RegexBlogThread = regexp.MustCompile(`^/blog/p/(?P\d+)(-([^/]+))?$`) -func BuildBlogThread(projectSlug string, threadId int, title string) string { +func (c *UrlContext) BuildBlogThread(threadId int, title string) string { defer CatchPanic() builder := buildBlogThreadPath(threadId, title) - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } -func BuildBlogThreadWithPostHash(projectSlug string, threadId int, title string, postId int) string { +func (c *UrlContext) BuildBlogThreadWithPostHash(threadId int, title string, postId int) string { defer CatchPanic() builder := buildBlogThreadPath(threadId, title) - return ProjectUrlWithFragment(builder.String(), nil, projectSlug, strconv.Itoa(postId)) + return c.UrlWithFragment(builder.String(), nil, strconv.Itoa(postId)) } var RegexBlogNewThread = regexp.MustCompile(`^/blog/new$`) -func BuildBlogNewThread(projectSlug string) string { +func (c *UrlContext) BuildBlogNewThread() string { defer CatchPanic() - return ProjectUrl("/blog/new", nil, projectSlug) + return c.Url("/blog/new", nil) } var RegexBlogPost = regexp.MustCompile(`^/blog/p/(?P\d+)/e/(?P\d+)$`) -func BuildBlogPost(projectSlug string, threadId int, postId int) string { +func (c *UrlContext) BuildBlogPost(threadId int, postId int) string { defer CatchPanic() builder := buildBlogPostPath(threadId, postId) - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexBlogPostDelete = regexp.MustCompile(`^/blog/p/(?P\d+)/e/(?P\d+)/delete$`) -func BuildBlogPostDelete(projectSlug string, threadId int, postId int) string { +func (c *UrlContext) BuildBlogPostDelete(threadId int, postId int) string { defer CatchPanic() builder := buildBlogPostPath(threadId, postId) builder.WriteString("/delete") - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexBlogPostEdit = regexp.MustCompile(`^/blog/p/(?P\d+)/e/(?P\d+)/edit$`) -func BuildBlogPostEdit(projectSlug string, threadId int, postId int) string { +func (c *UrlContext) BuildBlogPostEdit(threadId int, postId int) string { defer CatchPanic() builder := buildBlogPostPath(threadId, postId) builder.WriteString("/edit") - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexBlogPostReply = regexp.MustCompile(`^/blog/p/(?P\d+)/e/(?P\d+)/reply$`) -func BuildBlogPostReply(projectSlug string, threadId int, postId int) string { +func (c *UrlContext) BuildBlogPostReply(threadId int, postId int) string { defer CatchPanic() builder := buildBlogPostPath(threadId, postId) builder.WriteString("/reply") - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } /* @@ -580,7 +622,7 @@ func BuildLibraryResource(resourceId int) string { var RegexEpisodeList = regexp.MustCompile(`^/episode(/(?P[^/]+))?$`) -func BuildEpisodeList(projectSlug string, topic string) string { +func (c *UrlContext) BuildEpisodeList(topic string) string { defer CatchPanic() var builder strings.Builder @@ -589,21 +631,21 @@ func BuildEpisodeList(projectSlug string, topic string) string { builder.WriteString("/") builder.WriteString(topic) } - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexEpisode = regexp.MustCompile(`^/episode/(?P[^/]+)/(?P[^/]+)$`) -func BuildEpisode(projectSlug string, topic string, episode string) string { +func (c *UrlContext) BuildEpisode(topic string, episode string) string { defer CatchPanic() - return ProjectUrl(fmt.Sprintf("/episode/%s/%s", topic, episode), nil, projectSlug) + return c.Url(fmt.Sprintf("/episode/%s/%s", topic, episode), nil) } var RegexCineraIndex = regexp.MustCompile(`^/(?P[^/]+).index$`) -func BuildCineraIndex(projectSlug string, topic string) string { +func (c *UrlContext) BuildCineraIndex(topic string) string { defer CatchPanic() - return ProjectUrl(fmt.Sprintf("/%s.index", topic), nil, projectSlug) + return c.Url(fmt.Sprintf("/%s.index", topic), nil) } /* @@ -635,8 +677,8 @@ func BuildDiscordShowcaseBacklog() string { var RegexAssetUpload = regexp.MustCompile("^/upload_asset$") // NOTE(asaf): Providing the projectSlug avoids any CORS problems. -func BuildAssetUpload(projectSlug string) string { - return ProjectUrl("/upload_asset", nil, projectSlug) +func (c *UrlContext) BuildAssetUpload() string { + return c.Url("/upload_asset", nil) } /* @@ -715,7 +757,7 @@ func BuildUserFile(filepath string) string { var RegexForumMarkRead = regexp.MustCompile(`^/markread/(?P\d+)$`) // NOTE(asaf): subforumId == 0 means ALL SUBFORUMS -func BuildForumMarkRead(projectSlug string, subforumId int) string { +func (c *UrlContext) BuildForumMarkRead(subforumId int) string { defer CatchPanic() if subforumId < 0 { panic(oops.New(nil, "Invalid subforum ID (%d), must be >= 0", subforumId)) @@ -725,7 +767,7 @@ func BuildForumMarkRead(projectSlug string, subforumId int) string { builder.WriteString("/markread/") builder.WriteString(strconv.Itoa(subforumId)) - return ProjectUrl(builder.String(), nil, projectSlug) + return c.Url(builder.String(), nil) } var RegexCatchAll = regexp.MustCompile("^") diff --git a/src/templates/mapping.go b/src/templates/mapping.go index e6d18d4..364f4ab 100644 --- a/src/templates/mapping.go +++ b/src/templates/mapping.go @@ -59,22 +59,11 @@ var LifecycleBadgeStrings = map[models.ProjectLifecycle]string{ models.ProjectLifecycleLTS: "Complete", } -func ProjectUrl(p *models.Project) string { - var url string - if p.Personal { - url = hmnurl.BuildPersonalProject(p.ID, models.GeneratePersonalProjectSlug(p.Name)) - } else { - url = hmnurl.BuildOfficialProjectHomepage(p.Slug) - } - return url -} - -func ProjectToTemplate(p *models.Project, theme string) Project { +func ProjectToTemplate(p *models.Project, url string, theme string) Project { logo := p.LogoLight if theme == "dark" { logo = p.LogoDark } - url := ProjectUrl(p) return Project{ Name: p.Name, Subdomain: p.Subdomain(), @@ -91,9 +80,8 @@ func ProjectToTemplate(p *models.Project, theme string) Project { IsHMN: p.IsHMN(), - HasBlog: p.BlogEnabled, - HasForum: p.ForumEnabled, - HasLibrary: false, // TODO: port the library lol + HasBlog: p.BlogEnabled, + HasForum: p.ForumEnabled, DateApproved: p.DateApproved, } diff --git a/src/templates/types.go b/src/templates/types.go index 50a358e..14c25ae 100644 --- a/src/templates/types.go +++ b/src/templates/types.go @@ -126,9 +126,8 @@ type Project struct { IsHMN bool - HasBlog bool - HasForum bool - HasLibrary bool + HasBlog bool + HasForum bool UUID string DateApproved time.Time diff --git a/src/website/admin.go b/src/website/admin.go index b2f5838..2dd6b73 100644 --- a/src/website/admin.go +++ b/src/website/admin.go @@ -137,7 +137,7 @@ func AdminApprovalQueue(c *RequestContext) ResponseData { for _, p := range posts { post := templates.PostToTemplate(&p.Post, &p.Author, c.Theme) post.AddContentVersion(p.CurrentVersion, &p.Author) // NOTE(asaf): Don't care about editors here - post.Url = UrlForGenericPost(&p.Thread, &p.Post, lineageBuilder, p.Project.Slug) + post.Url = UrlForGenericPost(UrlContextForProject(&p.Project), &p.Thread, &p.Post, lineageBuilder) data.Posts = append(data.Posts, postWithTitle{ Post: post, Title: p.Thread.Title, diff --git a/src/website/base_data.go b/src/website/base_data.go index 794f20c..eb767ed 100644 --- a/src/website/base_data.go +++ b/src/website/base_data.go @@ -26,7 +26,7 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc notices := getNoticesFromCookie(c) if len(breadcrumbs) > 0 { - projectUrl := UrlForProject(c.CurrentProject) + projectUrl := c.UrlContext.BuildHomepage() if breadcrumbs[0].Url != projectUrl { rootBreadcrumb := templates.Breadcrumb{ Name: c.CurrentProject.Name, @@ -42,11 +42,11 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc Breadcrumbs: breadcrumbs, CurrentUrl: c.FullUrl(), - CurrentProjectUrl: UrlForProject(c.CurrentProject), + CurrentProjectUrl: c.UrlContext.BuildHomepage(), LoginPageUrl: hmnurl.BuildLoginPage(c.FullUrl()), ProjectCSSUrl: hmnurl.BuildProjectCSS(c.CurrentProject.Color1), - Project: templates.ProjectToTemplate(c.CurrentProject, c.Theme), + Project: templates.ProjectToTemplate(c.CurrentProject, c.UrlContext.BuildHomepage(), c.Theme), User: templateUser, Session: templateSession, Notices: notices, @@ -67,7 +67,7 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc HMNHomepageUrl: hmnurl.BuildHomepage(), ProjectIndexUrl: hmnurl.BuildProjectIndex(1), PodcastUrl: hmnurl.BuildPodcast(), - ForumsUrl: hmnurl.BuildForum(models.HMNProjectSlug, nil, 1), + ForumsUrl: hmnurl.HMNProjectContext.BuildForum(nil, 1), LibraryUrl: hmnurl.BuildLibrary(), }, Footer: templates.Footer{ @@ -77,7 +77,7 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc CodeOfConductUrl: hmnurl.BuildCodeOfConduct(), CommunicationGuidelinesUrl: hmnurl.BuildCommunicationGuidelines(), ProjectIndexUrl: hmnurl.BuildProjectIndex(1), - ForumsUrl: hmnurl.BuildForum(models.HMNProjectSlug, nil, 1), + ForumsUrl: hmnurl.HMNProjectContext.BuildForum(nil, 1), ContactUrl: hmnurl.BuildContactPage(), }, } @@ -90,15 +90,15 @@ func getBaseData(c *RequestContext, title string, breadcrumbs []templates.Breadc episodeGuideUrl := "" defaultTopic, hasAnnotations := config.Config.EpisodeGuide.Projects[c.CurrentProject.Slug] if hasAnnotations { - episodeGuideUrl = hmnurl.BuildEpisodeList(c.CurrentProject.Slug, defaultTopic) + episodeGuideUrl = c.UrlContext.BuildEpisodeList(defaultTopic) } baseData.Header.Project = &templates.ProjectHeader{ HasForums: c.CurrentProject.ForumEnabled, HasBlog: c.CurrentProject.BlogEnabled, HasEpisodeGuide: hasAnnotations, - ForumsUrl: hmnurl.BuildForum(c.CurrentProject.Slug, nil, 1), - BlogUrl: hmnurl.BuildBlog(c.CurrentProject.Slug, 1), + ForumsUrl: c.UrlContext.BuildForum(nil, 1), + BlogUrl: c.UrlContext.BuildBlog(1), EpisodeGuideUrl: episodeGuideUrl, } } diff --git a/src/website/blogs.go b/src/website/blogs.go index df76def..9b8a666 100644 --- a/src/website/blogs.go +++ b/src/website/blogs.go @@ -46,7 +46,7 @@ func BlogIndex(c *RequestContext) ResponseData { numPages := utils.NumPages(numPosts, postsPerPage) page, ok := ParsePageNumber(c, "page", numPages) if !ok { - c.Redirect(hmnurl.BuildBlog(c.CurrentProject.Slug, page), http.StatusSeeOther) + c.Redirect(c.UrlContext.BuildBlog(page), http.StatusSeeOther) } threads, err := FetchThreads(c.Context(), c.Conn, c.CurrentUser, ThreadsQuery{ @@ -63,14 +63,14 @@ func BlogIndex(c *RequestContext) ResponseData { for _, thread := range threads { entries = append(entries, blogIndexEntry{ Title: thread.Thread.Title, - Url: hmnurl.BuildBlogThread(c.CurrentProject.Slug, thread.Thread.ID, thread.Thread.Title), + Url: c.UrlContext.BuildBlogThread(thread.Thread.ID, thread.Thread.Title), Author: templates.UserToTemplate(thread.FirstPostAuthor, c.Theme), Date: thread.FirstPost.PostDate, Content: template.HTML(thread.FirstPostCurrentVersion.TextParsed), }) } - baseData := getBaseData(c, fmt.Sprintf("%s Blog", c.CurrentProject.Name), []templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)}) + baseData := getBaseData(c, fmt.Sprintf("%s Blog", c.CurrentProject.Name), []templates.Breadcrumb{BlogBreadcrumb(c.UrlContext)}) canCreate := false if c.CurrentUser != nil { @@ -97,14 +97,14 @@ func BlogIndex(c *RequestContext) ResponseData { Current: page, Total: numPages, - FirstUrl: hmnurl.BuildBlog(c.CurrentProject.Slug, 1), - LastUrl: hmnurl.BuildBlog(c.CurrentProject.Slug, numPages), - PreviousUrl: hmnurl.BuildBlog(c.CurrentProject.Slug, utils.IntClamp(1, page-1, numPages)), - NextUrl: hmnurl.BuildBlog(c.CurrentProject.Slug, utils.IntClamp(1, page+1, numPages)), + FirstUrl: c.UrlContext.BuildBlog(1), + LastUrl: c.UrlContext.BuildBlog(numPages), + PreviousUrl: c.UrlContext.BuildBlog(utils.IntClamp(1, page-1, numPages)), + NextUrl: c.UrlContext.BuildBlog(utils.IntClamp(1, page+1, numPages)), }, CanCreatePost: canCreate, - NewPostUrl: hmnurl.BuildBlogNewThread(c.CurrentProject.Slug), + NewPostUrl: c.UrlContext.BuildBlogNewThread(), }, c.Perf) return res } @@ -138,11 +138,11 @@ func BlogThread(c *RequestContext) ResponseData { for _, p := range posts { post := templates.PostToTemplate(&p.Post, p.Author, c.Theme) post.AddContentVersion(p.CurrentVersion, p.Editor) - addBlogUrlsToPost(&post, c.CurrentProject.Slug, &p.Thread, p.Post.ID) + addBlogUrlsToPost(c.UrlContext, &post, &p.Thread, p.Post.ID) if p.ReplyPost != nil { reply := templates.PostToTemplate(p.ReplyPost, p.ReplyAuthor, c.Theme) - addBlogUrlsToPost(&reply, c.CurrentProject.Slug, &p.Thread, p.Post.ID) + addBlogUrlsToPost(c.UrlContext, &reply, &p.Thread, p.Post.ID) post.ReplyPost = &reply } @@ -168,7 +168,7 @@ func BlogThread(c *RequestContext) ResponseData { } } - baseData := getBaseData(c, thread.Title, []templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)}) + baseData := getBaseData(c, thread.Title, []templates.Breadcrumb{BlogBreadcrumb(c.UrlContext)}) baseData.OpenGraphItems = append(baseData.OpenGraphItems, templates.OpenGraphItem{ Property: "og:description", Value: posts[0].Post.Preview, @@ -180,7 +180,7 @@ func BlogThread(c *RequestContext) ResponseData { Thread: templates.ThreadToTemplate(&thread), MainPost: templatePosts[0], Comments: templatePosts[1:], - ReplyLink: hmnurl.BuildBlogPostReply(c.CurrentProject.Slug, cd.ThreadID, posts[0].Post.ID), + ReplyLink: c.UrlContext.BuildBlogPostReply(cd.ThreadID, posts[0].Post.ID), LoginLink: hmnurl.BuildLoginPage(c.FullUrl()), }, c.Perf) return res @@ -202,7 +202,7 @@ func BlogPostRedirectToThread(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch thread for blog redirect")) } - threadUrl := hmnurl.BuildBlogThreadWithPostHash(c.CurrentProject.Slug, cd.ThreadID, thread.Thread.Title, cd.PostID) + threadUrl := c.UrlContext.BuildBlogThreadWithPostHash(cd.ThreadID, thread.Thread.Title, cd.PostID) return c.Redirect(threadUrl, http.StatusFound) } @@ -210,11 +210,11 @@ func BlogNewThread(c *RequestContext) ResponseData { baseData := getBaseData( c, fmt.Sprintf("Create New Post | %s", c.CurrentProject.Name), - []templates.Breadcrumb{BlogBreadcrumb(c.CurrentProject.Slug)}, + []templates.Breadcrumb{BlogBreadcrumb(c.UrlContext)}, ) - editData := getEditorDataForNew(c.CurrentUser, baseData, nil) - editData.SubmitUrl = hmnurl.BuildBlogNewThread(c.CurrentProject.Slug) + editData := getEditorDataForNew(c.UrlContext, c.CurrentUser, baseData, nil) + editData.SubmitUrl = c.UrlContext.BuildBlogNewThread() editData.SubmitLabel = "Create Post" var res ResponseData @@ -268,7 +268,7 @@ func BlogNewThreadSubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new blog post")) } - newThreadUrl := hmnurl.BuildBlogThread(c.CurrentProject.Slug, threadId, title) + newThreadUrl := c.UrlContext.BuildBlogThread(threadId, title) return c.Redirect(newThreadUrl, http.StatusSeeOther) } @@ -301,11 +301,11 @@ func BlogPostEdit(c *RequestContext) ResponseData { baseData := getBaseData( c, title, - BlogThreadBreadcrumbs(c.CurrentProject.Slug, &post.Thread), + BlogThreadBreadcrumbs(c.UrlContext, &post.Thread), ) - editData := getEditorDataForEdit(c.CurrentUser, baseData, post) - editData.SubmitUrl = hmnurl.BuildBlogPostEdit(c.CurrentProject.Slug, cd.ThreadID, cd.PostID) + editData := getEditorDataForEdit(c.UrlContext, c.CurrentUser, baseData, post) + editData.SubmitUrl = c.UrlContext.BuildBlogPostEdit(cd.ThreadID, cd.PostID) editData.SubmitLabel = "Submit Edited Post" if post.Thread.FirstID != post.Post.ID { editData.SubmitLabel = "Submit Edited Comment" @@ -373,7 +373,7 @@ func BlogPostEditSubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to edit blog post")) } - postUrl := hmnurl.BuildBlogThreadWithPostHash(c.CurrentProject.Slug, cd.ThreadID, post.Thread.Title, cd.PostID) + postUrl := c.UrlContext.BuildBlogThreadWithPostHash(cd.ThreadID, post.Thread.Title, cd.PostID) return c.Redirect(postUrl, http.StatusSeeOther) } @@ -396,14 +396,14 @@ func BlogPostReply(c *RequestContext) ResponseData { baseData := getBaseData( c, fmt.Sprintf("Replying to comment in \"%s\" | %s", post.Thread.Title, c.CurrentProject.Name), - BlogThreadBreadcrumbs(c.CurrentProject.Slug, &post.Thread), + BlogThreadBreadcrumbs(c.UrlContext, &post.Thread), ) replyPost := templates.PostToTemplate(&post.Post, post.Author, c.Theme) replyPost.AddContentVersion(post.CurrentVersion, post.Editor) - editData := getEditorDataForNew(c.CurrentUser, baseData, &replyPost) - editData.SubmitUrl = hmnurl.BuildBlogPostReply(c.CurrentProject.Slug, cd.ThreadID, cd.PostID) + editData := getEditorDataForNew(c.UrlContext, c.CurrentUser, baseData, &replyPost) + editData.SubmitUrl = c.UrlContext.BuildBlogPostReply(cd.ThreadID, cd.PostID) editData.SubmitLabel = "Submit Reply" var res ResponseData @@ -439,7 +439,7 @@ func BlogPostReplySubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to reply to blog post")) } - newPostUrl := hmnurl.BuildBlogPost(c.CurrentProject.Slug, cd.ThreadID, newPostId) + newPostUrl := c.UrlContext.BuildBlogPost(cd.ThreadID, newPostId) return c.Redirect(newPostUrl, http.StatusSeeOther) } @@ -472,7 +472,7 @@ func BlogPostDelete(c *RequestContext) ResponseData { baseData := getBaseData( c, title, - BlogThreadBreadcrumbs(c.CurrentProject.Slug, &post.Thread), + BlogThreadBreadcrumbs(c.UrlContext, &post.Thread), ) templatePost := templates.PostToTemplate(&post.Post, post.Author, c.Theme) @@ -487,7 +487,7 @@ func BlogPostDelete(c *RequestContext) ResponseData { var res ResponseData res.MustWriteTemplate("blog_post_delete.html", blogPostDeleteData{ BaseData: baseData, - SubmitUrl: hmnurl.BuildBlogPostDelete(c.CurrentProject.Slug, cd.ThreadID, cd.PostID), + SubmitUrl: c.UrlContext.BuildBlogPostDelete(cd.ThreadID, cd.PostID), Post: templatePost, }, c.Perf) return res @@ -517,8 +517,7 @@ func BlogPostDeleteSubmit(c *RequestContext) ResponseData { } if threadDeleted { - projectUrl := UrlForProject(c.CurrentProject) - return c.Redirect(projectUrl, http.StatusSeeOther) + return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther) } else { thread, err := FetchThread(c.Context(), c.Conn, c.CurrentUser, cd.ThreadID, ThreadsQuery{ ProjectIDs: []int{c.CurrentProject.ID}, @@ -529,7 +528,7 @@ func BlogPostDeleteSubmit(c *RequestContext) ResponseData { } else if err != nil { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch thread after blog post delete")) } - threadUrl := hmnurl.BuildBlogThread(c.CurrentProject.Slug, thread.Thread.ID, thread.Thread.Title) + threadUrl := c.UrlContext.BuildBlogThread(thread.Thread.ID, thread.Thread.Title) return c.Redirect(threadUrl, http.StatusSeeOther) } } @@ -608,9 +607,9 @@ func getCommonBlogData(c *RequestContext) (commonBlogData, bool) { return res, true } -func addBlogUrlsToPost(p *templates.Post, projectSlug string, thread *models.Thread, postId int) { - p.Url = hmnurl.BuildBlogThreadWithPostHash(projectSlug, thread.ID, thread.Title, postId) - p.DeleteUrl = hmnurl.BuildBlogPostDelete(projectSlug, thread.ID, postId) - p.EditUrl = hmnurl.BuildBlogPostEdit(projectSlug, thread.ID, postId) - p.ReplyUrl = hmnurl.BuildBlogPostReply(projectSlug, thread.ID, postId) +func addBlogUrlsToPost(urlContext *hmnurl.UrlContext, p *templates.Post, thread *models.Thread, postId int) { + p.Url = urlContext.BuildBlogThreadWithPostHash(thread.ID, thread.Title, postId) + p.DeleteUrl = urlContext.BuildBlogPostDelete(thread.ID, postId) + p.EditUrl = urlContext.BuildBlogPostEdit(thread.ID, postId) + p.ReplyUrl = urlContext.BuildBlogPostReply(thread.ID, postId) } diff --git a/src/website/breadcrumb_helper.go b/src/website/breadcrumb_helper.go index b40ace7..ad823e4 100644 --- a/src/website/breadcrumb_helper.go +++ b/src/website/breadcrumb_helper.go @@ -6,58 +6,58 @@ import ( "git.handmade.network/hmn/hmn/src/templates" ) -func ProjectBreadcrumb(project *models.Project) templates.Breadcrumb { +func ProjectBreadcrumb(projectUrlContext *hmnurl.UrlContext) templates.Breadcrumb { return templates.Breadcrumb{ - Name: project.Name, - Url: UrlForProject(project), + Name: projectUrlContext.ProjectName, + Url: projectUrlContext.BuildHomepage(), } } -func ForumBreadcrumb(projectSlug string) templates.Breadcrumb { +func ForumBreadcrumb(projectUrlContext *hmnurl.UrlContext) templates.Breadcrumb { return templates.Breadcrumb{ Name: "Forums", - Url: hmnurl.BuildForum(projectSlug, nil, 1), + Url: projectUrlContext.BuildForum(nil, 1), } } -func SubforumBreadcrumbs(lineageBuilder *models.SubforumLineageBuilder, project *models.Project, subforumID int) []templates.Breadcrumb { +func SubforumBreadcrumbs(projectUrlContext *hmnurl.UrlContext, lineageBuilder *models.SubforumLineageBuilder, subforumID int) []templates.Breadcrumb { var result []templates.Breadcrumb result = []templates.Breadcrumb{ - ProjectBreadcrumb(project), - ForumBreadcrumb(project.Slug), + ProjectBreadcrumb(projectUrlContext), + ForumBreadcrumb(projectUrlContext), } 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), + Url: projectUrlContext.BuildForum(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) +func ForumThreadBreadcrumbs(projectUrlContext *hmnurl.UrlContext, lineageBuilder *models.SubforumLineageBuilder, thread *models.Thread) []templates.Breadcrumb { + result := SubforumBreadcrumbs(projectUrlContext, lineageBuilder, *thread.SubforumID) result = append(result, templates.Breadcrumb{ Name: thread.Title, - Url: hmnurl.BuildForumThread(project.Slug, lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), thread.ID, thread.Title, 1), + Url: projectUrlContext.BuildForumThread(lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), thread.ID, thread.Title, 1), }) return result } -func BlogBreadcrumb(projectSlug string) templates.Breadcrumb { +func BlogBreadcrumb(projectUrlContext *hmnurl.UrlContext) templates.Breadcrumb { return templates.Breadcrumb{ Name: "Blog", - Url: hmnurl.BuildBlog(projectSlug, 1), + Url: projectUrlContext.BuildBlog(1), } } -func BlogThreadBreadcrumbs(projectSlug string, thread *models.Thread) []templates.Breadcrumb { +func BlogThreadBreadcrumbs(projectUrlContext *hmnurl.UrlContext, thread *models.Thread) []templates.Breadcrumb { result := []templates.Breadcrumb{ - BlogBreadcrumb(projectSlug), - {Name: thread.Title, Url: hmnurl.BuildBlogThread(projectSlug, thread.ID, thread.Title)}, + BlogBreadcrumb(projectUrlContext), + {Name: thread.Title, Url: projectUrlContext.BuildBlogThread(thread.ID, thread.Title)}, } return result } diff --git a/src/website/episode_guide.go b/src/website/episode_guide.go index 76cba49..23305a7 100644 --- a/src/website/episode_guide.go +++ b/src/website/episode_guide.go @@ -11,7 +11,6 @@ import ( "strings" "git.handmade.network/hmn/hmn/src/config" - "git.handmade.network/hmn/hmn/src/hmnurl" "git.handmade.network/hmn/hmn/src/templates" ) @@ -53,11 +52,11 @@ func EpisodeList(c *RequestContext) ResponseData { defaultTopic, hasEpisodeGuide := config.Config.EpisodeGuide.Projects[slug] if !hasEpisodeGuide { - return c.Redirect(UrlForProject(c.CurrentProject), http.StatusSeeOther) + return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther) } if topic == "" { - return c.Redirect(hmnurl.BuildEpisodeList(slug, defaultTopic), http.StatusSeeOther) + return c.Redirect(c.UrlContext.BuildEpisodeList(defaultTopic), http.StatusSeeOther) } allTopics, foundTopic := topicsForProject(slug, topic) @@ -82,7 +81,7 @@ func EpisodeList(c *RequestContext) ResponseData { for _, t := range allTopics { url := "" if t != foundTopic { - url = hmnurl.BuildEpisodeList(slug, t) + url = c.UrlContext.BuildEpisodeList(t) } topicLinks = append(topicLinks, templates.Link{LinkText: t, Url: url}) } @@ -114,7 +113,7 @@ func Episode(c *RequestContext) ResponseData { _, hasEpisodeGuide := config.Config.EpisodeGuide.Projects[slug] if !hasEpisodeGuide { - return c.Redirect(UrlForProject(c.CurrentProject), http.StatusSeeOther) + return c.Redirect(c.UrlContext.BuildHomepage(), http.StatusSeeOther) } _, foundTopic := topicsForProject(slug, topic) @@ -150,7 +149,7 @@ func Episode(c *RequestContext) ResponseData { baseData := getBaseData( c, title, - []templates.Breadcrumb{{Name: "Episode Guide", Url: hmnurl.BuildEpisodeList(c.CurrentProject.Slug, foundTopic)}}, + []templates.Breadcrumb{{Name: "Episode Guide", Url: c.UrlContext.BuildEpisodeList(foundTopic)}}, ) res.MustWriteTemplate("episode.html", EpisodeData{ BaseData: baseData, diff --git a/src/website/feed.go b/src/website/feed.go index 5d6f166..5250d47 100644 --- a/src/website/feed.go +++ b/src/website/feed.go @@ -71,7 +71,7 @@ func Feed(c *RequestContext) ResponseData { BaseData: baseData, AtomFeedUrl: hmnurl.BuildAtomFeed(), - MarkAllReadUrl: hmnurl.BuildForumMarkRead(c.CurrentProject.Slug, 0), + MarkAllReadUrl: c.UrlContext.BuildForumMarkRead(0), Posts: posts, Pagination: pagination, }, c.Perf) @@ -181,7 +181,7 @@ func AtomFeed(c *RequestContext) ResponseData { projectMap := make(map[int]int) // map[project id]index in slice for _, p := range projects.ToSlice() { project := p.(*projectResult).Project - templateProject := templates.ProjectToTemplate(&project, c.Theme) + templateProject := templates.ProjectToTemplate(&project, UrlContextForProject(&project).BuildHomepage(), c.Theme) templateProject.UUID = uuid.NewSHA1(uuid.NameSpaceURL, []byte(templateProject.Url)).URN() projectIds = append(projectIds, project.ID) diff --git a/src/website/forums.go b/src/website/forums.go index 190d782..efc68db 100644 --- a/src/website/forums.go +++ b/src/website/forums.go @@ -35,8 +35,6 @@ type forumSubforumData struct { TotalThreads int } -type editActionType string - type editorData struct { templates.BaseData SubmitUrl string @@ -54,13 +52,13 @@ type editorData struct { UploadUrl string } -func getEditorDataForNew(currentUser *models.User, baseData templates.BaseData, replyPost *templates.Post) editorData { +func getEditorDataForNew(urlContext *hmnurl.UrlContext, currentUser *models.User, baseData templates.BaseData, replyPost *templates.Post) editorData { result := editorData{ BaseData: baseData, CanEditTitle: replyPost == nil, PostReplyingTo: replyPost, MaxFileSize: AssetMaxSize(currentUser), - UploadUrl: hmnurl.BuildAssetUpload(baseData.Project.Subdomain), + UploadUrl: urlContext.BuildAssetUpload(), } if replyPost != nil { @@ -70,7 +68,7 @@ func getEditorDataForNew(currentUser *models.User, baseData templates.BaseData, return result } -func getEditorDataForEdit(currentUser *models.User, baseData templates.BaseData, p PostAndStuff) editorData { +func getEditorDataForEdit(urlContext *hmnurl.UrlContext, currentUser *models.User, baseData templates.BaseData, p PostAndStuff) editorData { return editorData{ BaseData: baseData, Title: p.Thread.Title, @@ -78,7 +76,7 @@ func getEditorDataForEdit(currentUser *models.User, baseData templates.BaseData, IsEditing: true, EditInitialContents: p.CurrentVersion.TextRaw, MaxFileSize: AssetMaxSize(currentUser), - UploadUrl: hmnurl.BuildAssetUpload(baseData.Project.Subdomain), + UploadUrl: urlContext.BuildAssetUpload(), } } @@ -104,7 +102,7 @@ func Forum(c *RequestContext) ResponseData { numPages := utils.NumPages(numThreads, threadsPerPage) page, ok := ParsePageNumber(c, "page", numPages) if !ok { - c.Redirect(hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, page), http.StatusSeeOther) + c.Redirect(c.UrlContext.BuildForum(currentSubforumSlugs, page), http.StatusSeeOther) } howManyThreadsToSkip := (page - 1) * threadsPerPage @@ -119,7 +117,7 @@ func Forum(c *RequestContext) ResponseData { makeThreadListItem := func(row ThreadAndStuff) templates.ThreadListItem { return templates.ThreadListItem{ Title: row.Thread.Title, - Url: hmnurl.BuildForumThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(*row.Thread.SubforumID), row.Thread.ID, row.Thread.Title, 1), + Url: c.UrlContext.BuildForumThread(cd.LineageBuilder.GetSubforumLineageSlugs(*row.Thread.SubforumID), row.Thread.ID, row.Thread.Title, 1), FirstUser: templates.UserToTemplate(row.FirstPostAuthor, c.Theme), FirstDate: row.FirstPost.PostDate, LastUser: templates.UserToTemplate(row.LastPostAuthor, c.Theme), @@ -165,7 +163,7 @@ func Forum(c *RequestContext) ResponseData { subforums = append(subforums, forumSubforumData{ Name: sfNode.Name, - Url: hmnurl.BuildForum(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(sfNode.ID), 1), + Url: c.UrlContext.BuildForum(cd.LineageBuilder.GetSubforumLineageSlugs(sfNode.ID), 1), Threads: threads, TotalThreads: numThreads, }) @@ -179,23 +177,23 @@ func Forum(c *RequestContext) ResponseData { baseData := getBaseData( c, fmt.Sprintf("%s Forums", c.CurrentProject.Name), - SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID), + SubforumBreadcrumbs(c.UrlContext, cd.LineageBuilder, cd.SubforumID), ) var res ResponseData res.MustWriteTemplate("forum.html", forumData{ BaseData: baseData, - NewThreadUrl: hmnurl.BuildForumNewThread(c.CurrentProject.Slug, currentSubforumSlugs, false), - MarkReadUrl: hmnurl.BuildForumMarkRead(c.CurrentProject.Slug, cd.SubforumID), + NewThreadUrl: c.UrlContext.BuildForumNewThread(currentSubforumSlugs, false), + MarkReadUrl: c.UrlContext.BuildForumMarkRead(cd.SubforumID), Threads: threads, Pagination: templates.Pagination{ Current: page, Total: numPages, - FirstUrl: hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, 1), - LastUrl: hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, numPages), - NextUrl: hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, utils.IntClamp(1, page+1, numPages)), - PreviousUrl: hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, utils.IntClamp(1, page-1, numPages)), + FirstUrl: c.UrlContext.BuildForum(currentSubforumSlugs, 1), + LastUrl: c.UrlContext.BuildForum(currentSubforumSlugs, numPages), + NextUrl: c.UrlContext.BuildForum(currentSubforumSlugs, utils.IntClamp(1, page+1, numPages)), + PreviousUrl: c.UrlContext.BuildForum(currentSubforumSlugs, utils.IntClamp(1, page-1, numPages)), }, Subforums: subforums, }, c.Perf) @@ -308,7 +306,7 @@ func ForumMarkRead(c *RequestContext) ResponseData { if sfId == 0 { redirUrl = hmnurl.BuildFeed() } else { - redirUrl = hmnurl.BuildForum(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(sfId), 1) + redirUrl = c.UrlContext.BuildForum(lineageBuilder.GetSubforumLineageSlugs(sfId), 1) } return c.Redirect(redirUrl, http.StatusSeeOther) } @@ -358,17 +356,17 @@ func ForumThread(c *RequestContext) ResponseData { } page, numPages, ok := getPageInfo(c.PathParams["page"], numPosts, threadPostsPerPage) if !ok { - urlNoPage := hmnurl.BuildForumThread(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, thread.Title, 1) + urlNoPage := c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, 1) return c.Redirect(urlNoPage, http.StatusSeeOther) } pagination := templates.Pagination{ Current: page, Total: numPages, - FirstUrl: hmnurl.BuildForumThread(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, thread.Title, 1), - LastUrl: hmnurl.BuildForumThread(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, thread.Title, numPages), - NextUrl: hmnurl.BuildForumThread(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, thread.Title, utils.IntClamp(1, page+1, numPages)), - PreviousUrl: hmnurl.BuildForumThread(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, thread.Title, utils.IntClamp(1, page-1, numPages)), + FirstUrl: c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, 1), + LastUrl: c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, numPages), + NextUrl: c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, utils.IntClamp(1, page+1, numPages)), + PreviousUrl: c.UrlContext.BuildForumThread(currentSubforumSlugs, thread.ID, thread.Title, utils.IntClamp(1, page-1, numPages)), } postsAndStuff, err := FetchPosts(c.Context(), c.Conn, c.CurrentUser, PostsQuery{ @@ -385,11 +383,11 @@ func ForumThread(c *RequestContext) ResponseData { for _, p := range postsAndStuff { post := templates.PostToTemplate(&p.Post, p.Author, c.Theme) post.AddContentVersion(p.CurrentVersion, p.Editor) - addForumUrlsToPost(&post, c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, post.ID) + addForumUrlsToPost(c.UrlContext, &post, currentSubforumSlugs, thread.ID, post.ID) if p.ReplyPost != nil { reply := templates.PostToTemplate(p.ReplyPost, p.ReplyAuthor, c.Theme) - addForumUrlsToPost(&reply, c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, reply.ID) + addForumUrlsToPost(c.UrlContext, &reply, currentSubforumSlugs, thread.ID, reply.ID) post.ReplyPost = &reply } @@ -418,7 +416,7 @@ func ForumThread(c *RequestContext) ResponseData { } } - baseData := getBaseData(c, thread.Title, SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID)) + baseData := getBaseData(c, thread.Title, SubforumBreadcrumbs(c.UrlContext, cd.LineageBuilder, cd.SubforumID)) baseData.OpenGraphItems = append(baseData.OpenGraphItems, templates.OpenGraphItem{ Property: "og:description", Value: threadResult.FirstPost.Preview, @@ -429,8 +427,8 @@ func ForumThread(c *RequestContext) ResponseData { BaseData: baseData, Thread: templates.ThreadToTemplate(&thread), Posts: posts, - SubforumUrl: hmnurl.BuildForum(c.CurrentProject.Slug, currentSubforumSlugs, 1), - ReplyUrl: hmnurl.BuildForumPostReply(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, thread.FirstID), + SubforumUrl: c.UrlContext.BuildForum(currentSubforumSlugs, 1), + ReplyUrl: c.UrlContext.BuildForumPostReply(currentSubforumSlugs, thread.ID, thread.FirstID), Pagination: pagination, }, c.Perf) return res @@ -466,8 +464,7 @@ func ForumPostRedirect(c *RequestContext) ResponseData { page := (postIdx / threadPostsPerPage) + 1 - return c.Redirect(hmnurl.BuildForumThreadWithPostHash( - c.CurrentProject.Slug, + return c.Redirect(c.UrlContext.BuildForumThreadWithPostHash( cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, post.Thread.Title, @@ -482,9 +479,9 @@ func ForumNewThread(c *RequestContext) ResponseData { return FourOhFour(c) } - baseData := getBaseData(c, "Create New Thread", SubforumBreadcrumbs(cd.LineageBuilder, c.CurrentProject, cd.SubforumID)) - editData := getEditorDataForNew(c.CurrentUser, baseData, nil) - editData.SubmitUrl = hmnurl.BuildForumNewThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), true) + baseData := getBaseData(c, "Create New Thread", SubforumBreadcrumbs(c.UrlContext, cd.LineageBuilder, cd.SubforumID)) + editData := getEditorDataForNew(c.UrlContext, c.CurrentUser, baseData, nil) + editData.SubmitUrl = c.UrlContext.BuildForumNewThread(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), true) editData.SubmitLabel = "Post New Thread" var res ResponseData @@ -549,7 +546,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new forum thread")) } - newThreadUrl := hmnurl.BuildForumThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), threadId, title, 1) + newThreadUrl := c.UrlContext.BuildForumThread(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), threadId, title, 1) return c.Redirect(newThreadUrl, http.StatusSeeOther) } @@ -572,14 +569,14 @@ func ForumPostReply(c *RequestContext) ResponseData { baseData := getBaseData( c, fmt.Sprintf("Replying to post | %s", cd.SubforumTree[cd.SubforumID].Name), - ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &post.Thread), + ForumThreadBreadcrumbs(c.UrlContext, cd.LineageBuilder, &post.Thread), ) replyPost := templates.PostToTemplate(&post.Post, post.Author, c.Theme) replyPost.AddContentVersion(post.CurrentVersion, post.Editor) - editData := getEditorDataForNew(c.CurrentUser, baseData, &replyPost) - editData.SubmitUrl = hmnurl.BuildForumPostReply(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID) + editData := getEditorDataForNew(c.UrlContext, c.CurrentUser, baseData, &replyPost) + editData.SubmitUrl = c.UrlContext.BuildForumPostReply(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID) editData.SubmitLabel = "Submit Reply" var res ResponseData @@ -629,7 +626,7 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to reply to forum post")) } - newPostUrl := hmnurl.BuildForumPost(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, newPostId) + newPostUrl := c.UrlContext.BuildForumPost(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, newPostId) return c.Redirect(newPostUrl, http.StatusSeeOther) } @@ -659,10 +656,10 @@ func ForumPostEdit(c *RequestContext) ResponseData { } else { title = fmt.Sprintf("Editing Post | %s", cd.SubforumTree[cd.SubforumID].Name) } - baseData := getBaseData(c, title, ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &post.Thread)) + baseData := getBaseData(c, title, ForumThreadBreadcrumbs(c.UrlContext, cd.LineageBuilder, &post.Thread)) - editData := getEditorDataForEdit(c.CurrentUser, baseData, post) - editData.SubmitUrl = hmnurl.BuildForumPostEdit(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID) + editData := getEditorDataForEdit(c.UrlContext, c.CurrentUser, baseData, post) + editData.SubmitUrl = c.UrlContext.BuildForumPostEdit(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID) editData.SubmitLabel = "Submit Edited Post" var res ResponseData @@ -727,7 +724,7 @@ func ForumPostEditSubmit(c *RequestContext) ResponseData { return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to edit forum post")) } - postUrl := hmnurl.BuildForumPost(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID) + postUrl := c.UrlContext.BuildForumPost(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID) return c.Redirect(postUrl, http.StatusSeeOther) } @@ -754,7 +751,7 @@ func ForumPostDelete(c *RequestContext) ResponseData { baseData := getBaseData( c, fmt.Sprintf("Deleting post in \"%s\" | %s", post.Thread.Title, cd.SubforumTree[cd.SubforumID].Name), - ForumThreadBreadcrumbs(cd.LineageBuilder, c.CurrentProject, &post.Thread), + ForumThreadBreadcrumbs(c.UrlContext, cd.LineageBuilder, &post.Thread), ) templatePost := templates.PostToTemplate(&post.Post, post.Author, c.Theme) @@ -769,7 +766,7 @@ func ForumPostDelete(c *RequestContext) ResponseData { var res ResponseData res.MustWriteTemplate("forum_post_delete.html", forumPostDeleteData{ BaseData: baseData, - SubmitUrl: hmnurl.BuildForumPostDelete(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID), + SubmitUrl: c.UrlContext.BuildForumPostDelete(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, cd.PostID), Post: templatePost, }, c.Perf) return res @@ -799,10 +796,10 @@ func ForumPostDeleteSubmit(c *RequestContext) ResponseData { } if threadDeleted { - forumUrl := hmnurl.BuildForum(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), 1) + forumUrl := c.UrlContext.BuildForum(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), 1) return c.Redirect(forumUrl, http.StatusSeeOther) } else { - threadUrl := hmnurl.BuildForumThread(c.CurrentProject.Slug, cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, "", 1) // TODO: Go to the last page of the thread? Or the post before the post we just deleted? + threadUrl := c.UrlContext.BuildForumThread(cd.LineageBuilder.GetSubforumLineageSlugs(cd.SubforumID), cd.ThreadID, "", 1) // TODO: Go to the last page of the thread? Or the post before the post we just deleted? return c.Redirect(threadUrl, http.StatusSeeOther) } } @@ -829,7 +826,7 @@ func WikiArticleRedirect(c *RequestContext) ResponseData { lineageBuilder := models.MakeSubforumLineageBuilder(subforumTree) c.Perf.EndBlock() - dest := UrlForGenericThread(&thread.Thread, lineageBuilder, c.CurrentProject.Slug) + dest := UrlForGenericThread(c.UrlContext, &thread.Thread, lineageBuilder) return c.Redirect(dest, http.StatusFound) } @@ -928,11 +925,11 @@ func validateSubforums(lineageBuilder *models.SubforumLineageBuilder, project *m return subforumId, valid } -func addForumUrlsToPost(p *templates.Post, projectSlug string, subforums []string, threadId int, postId int) { - p.Url = hmnurl.BuildForumPost(projectSlug, subforums, threadId, postId) - p.DeleteUrl = hmnurl.BuildForumPostDelete(projectSlug, subforums, threadId, postId) - p.EditUrl = hmnurl.BuildForumPostEdit(projectSlug, subforums, threadId, postId) - p.ReplyUrl = hmnurl.BuildForumPostReply(projectSlug, subforums, threadId, postId) +func addForumUrlsToPost(urlContext *hmnurl.UrlContext, p *templates.Post, subforums []string, threadId int, postId int) { + p.Url = urlContext.BuildForumPost(subforums, threadId, postId) + p.DeleteUrl = urlContext.BuildForumPostDelete(subforums, threadId, postId) + p.EditUrl = urlContext.BuildForumPostEdit(subforums, threadId, postId) + p.ReplyUrl = urlContext.BuildForumPostReply(subforums, threadId, postId) } // Takes a template post and adds information about how many posts the user has made diff --git a/src/website/landing.go b/src/website/landing.go index 600daf6..188655d 100644 --- a/src/website/landing.go +++ b/src/website/landing.go @@ -74,7 +74,7 @@ func Index(c *RequestContext) ResponseData { c.Logger.Warn().Err(err).Msg("failed to fetch latest posts") } for _, p := range posts { - item := PostToTimelineItem(lineageBuilder, &p.Post, &p.Thread, &p.Project, p.Author, c.Theme) + item := PostToTimelineItem(UrlContextForProject(&p.Project), lineageBuilder, &p.Post, &p.Thread, p.Author, c.Theme) if p.Thread.Type == models.ThreadTypeProjectBlogPost && p.Post.ID == p.Thread.FirstID { // blog post item.Description = template.HTML(p.CurrentVersion.TextParsed) @@ -95,7 +95,7 @@ func Index(c *RequestContext) ResponseData { var newsPostItem *templates.TimelineItem if len(newsThreads) > 0 { t := newsThreads[0] - item := PostToTimelineItem(lineageBuilder, &t.FirstPost, &t.Thread, &t.Project, t.FirstPostAuthor, c.Theme) + item := PostToTimelineItem(UrlContextForProject(&t.Project), lineageBuilder, &t.FirstPost, &t.Thread, t.FirstPostAuthor, c.Theme) item.OwnerAvatarUrl = "" item.Breadcrumbs = nil item.TypeTitle = "" @@ -167,7 +167,7 @@ func Index(c *RequestContext) ResponseData { StreamsUrl: hmnurl.BuildStreams(), ShowcaseUrl: hmnurl.BuildShowcase(), AtomFeedUrl: hmnurl.BuildAtomFeed(), - MarkAllReadUrl: hmnurl.BuildForumMarkRead(models.HMNProjectSlug, 0), + MarkAllReadUrl: hmnurl.HMNProjectContext.BuildForumMarkRead(0), WheelJamUrl: hmnurl.BuildJamIndex(), }, c.Perf) diff --git a/src/website/post_helper.go b/src/website/post_helper.go index c60c148..538b653 100644 --- a/src/website/post_helper.go +++ b/src/website/post_helper.go @@ -7,26 +7,26 @@ import ( ) // NOTE(asaf): Please don't use these if you already know the kind of the thread beforehand. Just call the appropriate build function. -func UrlForGenericThread(thread *models.Thread, lineageBuilder *models.SubforumLineageBuilder, projectSlug string) string { +func UrlForGenericThread(urlContext *hmnurl.UrlContext, thread *models.Thread, lineageBuilder *models.SubforumLineageBuilder) string { switch thread.Type { case models.ThreadTypeProjectBlogPost: - return hmnurl.BuildBlogThread(projectSlug, thread.ID, thread.Title) + return urlContext.BuildBlogThread(thread.ID, thread.Title) case models.ThreadTypeForumPost: - return hmnurl.BuildForumThread(projectSlug, lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), thread.ID, thread.Title, 1) + return urlContext.BuildForumThread(lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), thread.ID, thread.Title, 1) } - return hmnurl.BuildOfficialProjectHomepage(projectSlug) // TODO: both official and personal projects + return urlContext.BuildHomepage() } -func UrlForGenericPost(thread *models.Thread, post *models.Post, lineageBuilder *models.SubforumLineageBuilder, projectSlug string) string { +func UrlForGenericPost(urlContext *hmnurl.UrlContext, thread *models.Thread, post *models.Post, lineageBuilder *models.SubforumLineageBuilder) string { switch post.ThreadType { case models.ThreadTypeProjectBlogPost: - return hmnurl.BuildBlogThreadWithPostHash(projectSlug, post.ThreadID, thread.Title, post.ID) + return urlContext.BuildBlogThreadWithPostHash(post.ThreadID, thread.Title, post.ID) case models.ThreadTypeForumPost: - return hmnurl.BuildForumPost(projectSlug, lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), post.ThreadID, post.ID) + return urlContext.BuildForumPost(lineageBuilder.GetSubforumLineageSlugs(*thread.SubforumID), post.ThreadID, post.ID) } - return hmnurl.BuildOfficialProjectHomepage(projectSlug) // TODO: both official and personal projects + return urlContext.BuildHomepage() } var PostTypeMap = map[models.ThreadType][]templates.PostType{ @@ -47,33 +47,33 @@ var ThreadTypeDisplayNames = map[models.ThreadType]string{ models.ThreadTypeForumPost: "Forums", } -func GenericThreadBreadcrumbs(lineageBuilder *models.SubforumLineageBuilder, project *models.Project, thread *models.Thread) []templates.Breadcrumb { +func GenericThreadBreadcrumbs(urlContext *hmnurl.UrlContext, lineageBuilder *models.SubforumLineageBuilder, thread *models.Thread) []templates.Breadcrumb { var result []templates.Breadcrumb if thread.Type == models.ThreadTypeForumPost { - result = SubforumBreadcrumbs(lineageBuilder, project, *thread.SubforumID) + result = SubforumBreadcrumbs(urlContext, lineageBuilder, *thread.SubforumID) } else { result = []templates.Breadcrumb{ { - Name: project.Name, - Url: UrlForProject(project), + Name: urlContext.ProjectName, + Url: urlContext.BuildHomepage(), }, { Name: ThreadTypeDisplayNames[thread.Type], - Url: BuildProjectRootResourceUrl(project.Slug, thread.Type), + Url: BuildProjectRootResourceUrl(urlContext, thread.Type), }, } } return result } -func BuildProjectRootResourceUrl(projectSlug string, kind models.ThreadType) string { +func BuildProjectRootResourceUrl(urlContext *hmnurl.UrlContext, kind models.ThreadType) string { switch kind { case models.ThreadTypeProjectBlogPost: - return hmnurl.BuildBlog(projectSlug, 1) + return urlContext.BuildBlog(1) case models.ThreadTypeForumPost: - return hmnurl.BuildForum(projectSlug, nil, 1) + return urlContext.BuildForum(nil, 1) } - return hmnurl.BuildOfficialProjectHomepage(projectSlug) // TODO: both official and personal projects + return urlContext.BuildHomepage() } func MakePostListItem( @@ -88,11 +88,13 @@ func MakePostListItem( ) templates.PostListItem { var result templates.PostListItem + urlContext := UrlContextForProject(project) + result.Title = thread.Title result.User = templates.UserToTemplate(user, currentTheme) result.Date = post.PostDate result.Unread = unread - result.Url = UrlForGenericPost(thread, post, lineageBuilder, project.Slug) + result.Url = UrlForGenericPost(urlContext, thread, post, lineageBuilder) result.Preview = post.Preview postType := templates.PostTypeUnknown @@ -108,7 +110,7 @@ func MakePostListItem( result.PostTypePrefix = PostTypePrefix[result.PostType] if includeBreadcrumbs { - result.Breadcrumbs = GenericThreadBreadcrumbs(lineageBuilder, project, thread) + result.Breadcrumbs = GenericThreadBreadcrumbs(urlContext, lineageBuilder, thread) } return result diff --git a/src/website/project_helper.go b/src/website/project_helper.go index de6e758..26e800b 100644 --- a/src/website/project_helper.go +++ b/src/website/project_helper.go @@ -3,8 +3,9 @@ package website import ( "context" - "git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/hmnurl" + + "git.handmade.network/hmn/hmn/src/db" "git.handmade.network/hmn/hmn/src/models" "git.handmade.network/hmn/hmn/src/oops" ) @@ -388,10 +389,11 @@ func FetchProjectOwners( return projectOwners[0].Owners, nil } -func UrlForProject(p *models.Project) string { - if p.Personal { - return hmnurl.BuildPersonalProject(p.ID, models.GeneratePersonalProjectSlug(p.Name)) - } else { - return hmnurl.BuildOfficialProjectHomepage(p.Slug) +func UrlContextForProject(p *models.Project) *hmnurl.UrlContext { + return &hmnurl.UrlContext{ + PersonalProject: p.Personal, + ProjectID: p.ID, + ProjectSlug: p.Slug, + ProjectName: p.Name, } } diff --git a/src/website/projects.go b/src/website/projects.go index 54e6da9..f906fee 100644 --- a/src/website/projects.go +++ b/src/website/projects.go @@ -62,7 +62,7 @@ func ProjectIndex(c *RequestContext) ResponseData { var restProjects []templates.Project now := time.Now() for _, p := range officialProjects { - templateProject := templates.ProjectToTemplate(&p.Project, c.Theme) + templateProject := templates.ProjectToTemplate(&p.Project, UrlContextForProject(&p.Project).BuildHomepage(), c.Theme) if p.Project.Slug == "hero" { // NOTE(asaf): Handmade Hero gets special treatment. Must always be first in the list. handmadeHero = &templateProject @@ -121,7 +121,11 @@ func ProjectIndex(c *RequestContext) ResponseData { if i >= maxPersonalProjects { break } - personalProjects = append(personalProjects, templates.ProjectToTemplate(&p.Project, c.Theme)) + personalProjects = append(personalProjects, templates.ProjectToTemplate( + &p.Project, + UrlContextForProject(&p.Project).BuildHomepage(), + c.Theme, + )) } } @@ -136,7 +140,7 @@ func ProjectIndex(c *RequestContext) ResponseData { PersonalProjects: personalProjects, ProjectAtomFeedUrl: hmnurl.BuildAtomFeedForProjects(), - WIPForumUrl: hmnurl.BuildForum(models.HMNProjectSlug, []string{"wip"}, 1), + WIPForumUrl: hmnurl.HMNProjectContext.BuildForum([]string{"wip"}, 1), }, c.Perf) return res } @@ -249,7 +253,7 @@ func ProjectHomepage(c *RequestContext) ResponseData { Value: c.CurrentProject.Blurb, }) - projectHomepageData.Project = templates.ProjectToTemplate(c.CurrentProject, c.Theme) + projectHomepageData.Project = templates.ProjectToTemplate(c.CurrentProject, c.UrlContext.BuildHomepage(), c.Theme) for _, owner := range owners { projectHomepageData.Owners = append(projectHomepageData.Owners, templates.UserToTemplate(owner, c.Theme)) } @@ -268,7 +272,7 @@ func ProjectHomepage(c *RequestContext) ResponseData { "unapproved", fmt.Sprintf( "NOTICE: This project has not yet been submitted for approval. It is only visible to owners. Please submit it for approval when the project content is ready for review.", - hmnurl.BuildProjectEdit(c.CurrentProject.Slug, "submit"), + c.UrlContext.BuildProjectEdit("submit"), ), ) case models.ProjectLifecycleApprovalRequired: @@ -309,10 +313,10 @@ func ProjectHomepage(c *RequestContext) ResponseData { for _, post := range postQueryResult.ToSlice() { projectHomepageData.RecentActivity = append(projectHomepageData.RecentActivity, PostToTimelineItem( + c.UrlContext, lineageBuilder, &post.(*postQuery).Post, &post.(*postQuery).Thread, - c.CurrentProject, &post.(*postQuery).Author, c.Theme, )) diff --git a/src/website/requesthandling.go b/src/website/requesthandling.go index 0a6eee5..eec5729 100644 --- a/src/website/requesthandling.go +++ b/src/website/requesthandling.go @@ -156,6 +156,7 @@ type RequestContext struct { CurrentUser *models.User CurrentSession *models.Session Theme string + UrlContext *hmnurl.UrlContext Perf *perf.RequestPerf diff --git a/src/website/routes.go b/src/website/routes.go index a53d73c..d080d35 100644 --- a/src/website/routes.go +++ b/src/website/routes.go @@ -251,9 +251,8 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe rb.GET(hmnurl.RegexBlogPostDelete, authMiddleware(BlogPostDelete)) rb.POST(hmnurl.RegexBlogPostDelete, authMiddleware(csrfMiddleware(BlogPostDeleteSubmit))) rb.GET(hmnurl.RegexBlogsRedirect, func(c *RequestContext) ResponseData { - return c.Redirect(hmnurl.ProjectUrl( + return c.Redirect(c.UrlContext.Url( fmt.Sprintf("blog%s", c.PathParams["remainder"]), nil, - c.CurrentProject.Slug, ), http.StatusMovedPermanently) }) } @@ -281,7 +280,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe if !p.Project.Personal { // TODO: Redirect to the same page on the other prefix - return c.Redirect(hmnurl.BuildOfficialProjectHomepage(p.Project.Slug), http.StatusSeeOther) + return c.Redirect(UrlContextForProject(&p.Project).BuildHomepage(), http.StatusSeeOther) } if c.PathParams["slug"] != models.GeneratePersonalProjectSlug(p.Project.Name) { @@ -290,6 +289,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe } c.CurrentProject = &p.Project + c.UrlContext = UrlContextForProject(c.CurrentProject) return h(c) }) @@ -458,15 +458,15 @@ func LoadCommonWebsiteData(c *RequestContext) (bool, ResponseData) { if c.CurrentProject == nil { panic("failed to load project data") } + + c.UrlContext = UrlContextForProject(c.CurrentProject) } - theme := "light" + c.Theme = "light" if c.CurrentUser != nil && c.CurrentUser.DarkTheme { - theme = "dark" + c.Theme = "dark" } - c.Theme = theme - return true, ResponseData{} } diff --git a/src/website/timeline_helper.go b/src/website/timeline_helper.go index f5b06e9..ef3ca3c 100644 --- a/src/website/timeline_helper.go +++ b/src/website/timeline_helper.go @@ -24,18 +24,18 @@ var TimelineTypeTitleMap = map[models.ThreadType]TimelineTypeTitles{ } func PostToTimelineItem( + urlContext *hmnurl.UrlContext, lineageBuilder *models.SubforumLineageBuilder, post *models.Post, thread *models.Thread, - project *models.Project, owner *models.User, currentTheme string, ) templates.TimelineItem { item := templates.TimelineItem{ Date: post.PostDate, Title: thread.Title, - Breadcrumbs: GenericThreadBreadcrumbs(lineageBuilder, project, thread), - Url: UrlForGenericPost(thread, post, lineageBuilder, project.Slug), + Breadcrumbs: GenericThreadBreadcrumbs(urlContext, lineageBuilder, thread), + Url: UrlForGenericPost(urlContext, thread, post, lineageBuilder), OwnerAvatarUrl: templates.UserAvatarUrl(owner, currentTheme), OwnerName: owner.BestName(), diff --git a/src/website/user.go b/src/website/user.go index e1f79b7..597aa3b 100644 --- a/src/website/user.go +++ b/src/website/user.go @@ -121,7 +121,11 @@ func UserProfile(c *RequestContext) ResponseData { templateProjects := make([]templates.Project, 0, len(projectQuerySlice)) for _, projectRow := range projectQuerySlice { projectData := projectRow.(*projectQuery) - templateProjects = append(templateProjects, templates.ProjectToTemplate(&projectData.Project, c.Theme)) + templateProjects = append(templateProjects, templates.ProjectToTemplate( + &projectData.Project, + UrlContextForProject(&projectData.Project).BuildHomepage(), + c.Theme, + )) } c.Perf.EndBlock() @@ -166,10 +170,10 @@ func UserProfile(c *RequestContext) ResponseData { for _, post := range posts { timelineItems = append(timelineItems, PostToTimelineItem( + UrlContextForProject(&post.Project), lineageBuilder, &post.Post, &post.Thread, - &post.Project, profileUser, c.Theme, ))