Add an explicit post reply feature

This commit is contained in:
Ben Visness 2021-07-19 22:07:15 -05:00
parent 17f652191d
commit 1ccf715c2d
6 changed files with 77 additions and 19 deletions

View File

@ -342,15 +342,6 @@ func BuildForumPostReply(projectSlug string, subforums []string, threadId int, p
return ProjectUrl(builder.String(), nil, projectSlug) return ProjectUrl(builder.String(), nil, projectSlug)
} }
var RegexForumPostQuote = regexp.MustCompile(`^/forums(/(?P<cats>[^\d/]+(/[^\d]+)*))?/t/(?P<threadid>\d+)/p/(?P<postid>\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 * Blog
*/ */

View File

@ -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")
}

View File

@ -49,7 +49,6 @@ func (p *Post) AddUrls(projectSlug string, subforums []string, threadId int, pos
p.DeleteUrl = hmnurl.BuildForumPostDelete(projectSlug, subforums, threadId, postId) p.DeleteUrl = hmnurl.BuildForumPostDelete(projectSlug, subforums, threadId, postId)
p.EditUrl = hmnurl.BuildForumPostEdit(projectSlug, subforums, threadId, postId) p.EditUrl = hmnurl.BuildForumPostEdit(projectSlug, subforums, threadId, postId)
p.ReplyUrl = hmnurl.BuildForumPostReply(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{ var LifecycleBadgeClasses = map[models.ProjectLifecycle]string{

View File

@ -74,7 +74,6 @@
WARNING: locked thread - use power responsibly! WARNING: locked thread - use power responsibly!
{{ end }} {{ end }}
<a class="reply action button" href="{{ .ReplyUrl }}" title="Reply">&hookrightarrow;</a>&nbsp; <a class="reply action button" href="{{ .ReplyUrl }}" title="Reply">&hookrightarrow;</a>&nbsp;
<a class="quote action button" href="{{ .QuoteUrl }}" title="Quote">&#10077;</a>
{{ end }} {{ end }}
</div> </div>
{{ end }} {{ end }}
@ -101,6 +100,11 @@
{{ end }} {{ end }}
</div> </div>
</div> </div>
{{ if .ReplyPost }}
<div class="i c--dim f7 pb2">
Replying to {{ if .Author }}{{ .Author.Username }}{{ else }}deleted user{{ end }} (<a href="{{ .ReplyPost.Url }}">#{{ .ReplyPost.ID }}</a>)
</div>
{{ end }}
<div class="contents overflow-x-auto"> <div class="contents overflow-x-auto">
{{ .Content }} {{ .Content }}
</div> </div>

View File

@ -73,7 +73,6 @@ type Post struct {
DeleteUrl string DeleteUrl string
EditUrl string EditUrl string
ReplyUrl string ReplyUrl string
QuoteUrl string
Preview string Preview string
ReadOnly bool ReadOnly bool
@ -87,6 +86,8 @@ type Post struct {
EditReason string EditReason string
IP string IP string
ReplyPost *Post
} }
type Project struct { type Project struct {

View File

@ -387,6 +387,9 @@ func ForumThread(c *RequestContext) ResponseData {
Ver models.PostVersion `db:"ver"` Ver models.PostVersion `db:"ver"`
Author *models.User `db:"author"` Author *models.User `db:"author"`
Editor *models.User `db:"editor"` 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{}, 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 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 author ON post.author_id = author.id
LEFT JOIN auth_user AS editor ON ver.editor_id = editor.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 WHERE
post.thread_id = $1 post.thread_id = $1
AND NOT post.deleted AND NOT post.deleted
ORDER BY postdate ORDER BY post.postdate
LIMIT $2 OFFSET $3 LIMIT $2 OFFSET $3
`, `,
thread.ID, thread.ID,
@ -420,6 +425,12 @@ func ForumThread(c *RequestContext) ResponseData {
post.AddContentVersion(row.Ver, row.Editor) post.AddContentVersion(row.Ver, row.Editor)
post.AddUrls(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, post.ID) 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) posts = append(posts, post)
} }
@ -596,7 +607,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
panic(oops.New(err, "failed to create thread")) 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 // Update thread with post id
_, err = tx.Exec(c.Context(), _, err = tx.Exec(c.Context(),
@ -727,22 +738,27 @@ func ForumPostReplySubmit(c *RequestContext) ResponseData {
return FourOhFour(c) return FourOhFour(c)
} }
postId, err := strconv.Atoi(c.PathParams["postid"])
if err != nil {
return FourOhFour(c)
}
c.Req.ParseForm() c.Req.ParseForm()
unparsed := c.Req.Form.Get("body") 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()) err = tx.Commit(c.Context())
if err != nil { if err != nil {
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new forum thread")) 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) 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) parsed := parsing.ParsePostInput(unparsedContent, parsing.RealMarkdown)
now := time.Now() now := time.Now()
ip := net.ParseIP(ipString) ip := net.ParseIP(ipString)
@ -757,8 +773,8 @@ func createForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadId,
// Create post // Create post
err := tx.QueryRow(ctx, err := tx.QueryRow(ctx,
` `
INSERT INTO handmade_post (postdate, category_id, thread_id, preview, current_id, author_id, category_kind, project_id) 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) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING id RETURNING id
`, `,
now, now,
@ -769,6 +785,7 @@ func createForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadId,
userId, userId,
models.CatKindForum, models.CatKindForum,
projectId, projectId,
replyId,
).Scan(&postId) ).Scan(&postId)
if err != nil { if err != nil {
panic(oops.New(err, "failed to create post")) panic(oops.New(err, "failed to create post"))