Add an explicit post reply feature
This commit is contained in:
parent
17f652191d
commit
1ccf715c2d
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
|
@ -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{
|
||||||
|
|
|
@ -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">↪</a>
|
<a class="reply action button" href="{{ .ReplyUrl }}" title="Reply">↪</a>
|
||||||
<a class="quote action button" href="{{ .QuoteUrl }}" title="Quote">❝</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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"))
|
||||||
|
|
Loading…
Reference in New Issue