From 1ccf715c2da4afb71a3ab51cae674e115109e5a2 Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Mon, 19 Jul 2021 22:07:15 -0500 Subject: [PATCH] Add an explicit post reply feature --- src/hmnurl/urls.go | 9 ---- .../2021-07-20T024051Z_AddPostReplyId.go | 46 +++++++++++++++++++ src/templates/mapping.go | 1 - src/templates/src/forum_thread.html | 6 ++- src/templates/types.go | 3 +- src/website/forums.go | 31 ++++++++++--- 6 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 src/migration/migrations/2021-07-20T024051Z_AddPostReplyId.go diff --git a/src/hmnurl/urls.go b/src/hmnurl/urls.go index 3cb9360b..1fded778 100644 --- a/src/hmnurl/urls.go +++ b/src/hmnurl/urls.go @@ -342,15 +342,6 @@ func BuildForumPostReply(projectSlug string, subforums []string, threadId int, p return ProjectUrl(builder.String(), nil, projectSlug) } -var RegexForumPostQuote = regexp.MustCompile(`^/forums(/(?P[^\d/]+(/[^\d]+)*))?/t/(?P\d+)/p/(?P\d+)/quote$`) - -func BuildForumPostQuote(projectSlug string, subforums []string, threadId int, postId int) string { - defer CatchPanic() - builder := buildForumPostPath(subforums, threadId, postId) - builder.WriteString("/quote") - return ProjectUrl(builder.String(), nil, projectSlug) -} - /* * Blog */ diff --git a/src/migration/migrations/2021-07-20T024051Z_AddPostReplyId.go b/src/migration/migrations/2021-07-20T024051Z_AddPostReplyId.go new file mode 100644 index 00000000..f92cd2ed --- /dev/null +++ b/src/migration/migrations/2021-07-20T024051Z_AddPostReplyId.go @@ -0,0 +1,46 @@ +package migrations + +import ( + "context" + "time" + + "git.handmade.network/hmn/hmn/src/migration/types" + "git.handmade.network/hmn/hmn/src/oops" + "github.com/jackc/pgx/v4" +) + +func init() { + registerMigration(AddPostReplyId{}) +} + +type AddPostReplyId struct{} + +func (m AddPostReplyId) Version() types.MigrationVersion { + return types.MigrationVersion(time.Date(2021, 7, 20, 2, 40, 51, 0, time.UTC)) +} + +func (m AddPostReplyId) Name() string { + return "AddPostReplyId" +} + +func (m AddPostReplyId) Description() string { + return "Add a reply id to posts" +} + +func (m AddPostReplyId) Up(ctx context.Context, tx pgx.Tx) error { + _, err := tx.Exec(ctx, + ` + ALTER TABLE handmade_post + ADD reply_id INT REFERENCES handmade_post (id) ON DELETE SET NULL; + `, + ) + if err != nil { + return oops.New(err, "failed to add columns") + } + + return nil +} + +func (m AddPostReplyId) Down(ctx context.Context, tx pgx.Tx) error { + panic("Implement me") +} diff --git a/src/templates/mapping.go b/src/templates/mapping.go index 8741fa00..2ed94566 100644 --- a/src/templates/mapping.go +++ b/src/templates/mapping.go @@ -49,7 +49,6 @@ func (p *Post) AddUrls(projectSlug string, subforums []string, threadId int, pos p.DeleteUrl = hmnurl.BuildForumPostDelete(projectSlug, subforums, threadId, postId) p.EditUrl = hmnurl.BuildForumPostEdit(projectSlug, subforums, threadId, postId) p.ReplyUrl = hmnurl.BuildForumPostReply(projectSlug, subforums, threadId, postId) - p.QuoteUrl = hmnurl.BuildForumPostQuote(projectSlug, subforums, threadId, postId) } var LifecycleBadgeClasses = map[models.ProjectLifecycle]string{ diff --git a/src/templates/src/forum_thread.html b/src/templates/src/forum_thread.html index 0eb2aab2..6364febc 100644 --- a/src/templates/src/forum_thread.html +++ b/src/templates/src/forum_thread.html @@ -74,7 +74,6 @@ WARNING: locked thread - use power responsibly! {{ end }}   - {{ end }} {{ end }} @@ -101,6 +100,11 @@ {{ end }} + {{ if .ReplyPost }} +
+ Replying to {{ if .Author }}{{ .Author.Username }}{{ else }}deleted user{{ end }} (#{{ .ReplyPost.ID }}) +
+ {{ end }}
{{ .Content }}
diff --git a/src/templates/types.go b/src/templates/types.go index 1037923c..051792f7 100644 --- a/src/templates/types.go +++ b/src/templates/types.go @@ -73,7 +73,6 @@ type Post struct { DeleteUrl string EditUrl string ReplyUrl string - QuoteUrl string Preview string ReadOnly bool @@ -87,6 +86,8 @@ type Post struct { EditReason string IP string + + ReplyPost *Post } type Project struct { diff --git a/src/website/forums.go b/src/website/forums.go index 124b2647..1f7b381a 100644 --- a/src/website/forums.go +++ b/src/website/forums.go @@ -387,6 +387,9 @@ func ForumThread(c *RequestContext) ResponseData { Ver models.PostVersion `db:"ver"` Author *models.User `db:"author"` Editor *models.User `db:"editor"` + + ReplyPost *models.Post `db:"reply"` + ReplyAuthor *models.User `db:"reply_author"` } itPosts, err := db.Query(c.Context(), c.Conn, postsQueryResult{}, ` @@ -396,10 +399,12 @@ func ForumThread(c *RequestContext) ResponseData { JOIN handmade_postversion AS ver ON post.current_id = ver.id LEFT JOIN auth_user AS author ON post.author_id = author.id LEFT JOIN auth_user AS editor ON ver.editor_id = editor.id + LEFT JOIN handmade_post AS reply ON post.reply_id = reply.id + LEFT JOIN auth_user AS reply_author ON reply.author_id = reply_author.id WHERE post.thread_id = $1 AND NOT post.deleted - ORDER BY postdate + ORDER BY post.postdate LIMIT $2 OFFSET $3 `, thread.ID, @@ -420,6 +425,12 @@ func ForumThread(c *RequestContext) ResponseData { post.AddContentVersion(row.Ver, row.Editor) post.AddUrls(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, post.ID) + if row.ReplyPost != nil { + reply := templates.PostToTemplate(row.ReplyPost, row.ReplyAuthor, c.Theme) + reply.AddUrls(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, post.ID) + post.ReplyPost = &reply + } + posts = append(posts, post) } @@ -596,7 +607,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData { panic(oops.New(err, "failed to create thread")) } - postId, _ := createForumPostAndVersion(c.Context(), tx, currentCatId, threadId, c.CurrentUser.ID, c.CurrentProject.ID, unparsed, c.Req.Host) + postId, _ := createForumPostAndVersion(c.Context(), tx, currentCatId, threadId, c.CurrentUser.ID, c.CurrentProject.ID, unparsed, c.Req.Host, nil) // Update thread with post id _, err = tx.Exec(c.Context(), @@ -727,22 +738,27 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData { return FourOhFour(c) } + postId, err := strconv.Atoi(c.PathParams["postid"]) + if err != nil { + return FourOhFour(c) + } + c.Req.ParseForm() unparsed := c.Req.Form.Get("body") - postId, _ := createForumPostAndVersion(c.Context(), tx, currentCatId, threadId, c.CurrentUser.ID, c.CurrentProject.ID, unparsed, c.Req.Host) + newPostId, _ := createForumPostAndVersion(c.Context(), tx, currentCatId, threadId, c.CurrentUser.ID, c.CurrentProject.ID, unparsed, c.Req.Host, &postId) err = tx.Commit(c.Context()) if err != nil { return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new forum thread")) } - newPostUrl := hmnurl.BuildForumPost(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), threadId, postId) + newPostUrl := hmnurl.BuildForumPost(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), threadId, newPostId) return c.Redirect(newPostUrl, http.StatusSeeOther) } -func createForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadId, userId, projectId int, unparsedContent string, ipString string) (postId, versionId int) { +func createForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadId, userId, projectId int, unparsedContent string, ipString string, replyId *int) (postId, versionId int) { parsed := parsing.ParsePostInput(unparsedContent, parsing.RealMarkdown) now := time.Now() ip := net.ParseIP(ipString) @@ -757,8 +773,8 @@ func createForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadId, // Create post err := tx.QueryRow(ctx, ` - INSERT INTO handmade_post (postdate, category_id, thread_id, preview, current_id, author_id, category_kind, project_id) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + INSERT INTO handmade_post (postdate, category_id, thread_id, preview, current_id, author_id, category_kind, project_id, reply_id) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id `, now, @@ -769,6 +785,7 @@ func createForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadId, userId, models.CatKindForum, projectId, + replyId, ).Scan(&postId) if err != nil { panic(oops.New(err, "failed to create post"))