Get forum post creation working

This commit is contained in:
Ben Visness 2021-07-04 17:48:08 -05:00
parent de0b7a08fb
commit c1785d79a4
5 changed files with 206 additions and 46 deletions

View File

@ -0,0 +1,45 @@
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(DropThreadFields{})
}
type DropThreadFields struct{}
func (m DropThreadFields) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2021, 7, 4, 21, 36, 58, 0, time.UTC))
}
func (m DropThreadFields) Name() string {
return "DropThreadFields"
}
func (m DropThreadFields) Description() string {
return "Drop unnecessary thread fields"
}
func (m DropThreadFields) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx, `
ALTER TABLE handmade_thread
DROP hits,
DROP reply_count;
`)
if err != nil {
return oops.New(err, "failed to drop thread fields")
}
return nil
}
func (m DropThreadFields) Down(ctx context.Context, tx pgx.Tx) error {
panic("Implement me")
}

View File

@ -0,0 +1,69 @@
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(FixPostConstraints{})
}
type FixPostConstraints struct{}
func (m FixPostConstraints) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2021, 7, 4, 22, 2, 28, 0, time.UTC))
}
func (m FixPostConstraints) Name() string {
return "FixPostConstraints"
}
func (m FixPostConstraints) Description() string {
return "Update post-related constraints to make insertion sane"
}
func (m FixPostConstraints) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx, `
ALTER TABLE handmade_thread
ALTER locked SET DEFAULT FALSE,
ALTER first_id SET NOT NULL,
ALTER last_id SET NOT NULL;
`)
if err != nil {
return oops.New(err, "failed to update thread constraints")
}
_, err = tx.Exec(ctx, `
ALTER TABLE handmade_post
ALTER deleted SET DEFAULT FALSE,
ALTER readonly SET DEFAULT FALSE,
ALTER CONSTRAINT handmade_post_current_id_fkey DEFERRABLE INITIALLY DEFERRED;
`)
if err != nil {
return oops.New(err, "failed to update project constraints")
}
_, err = tx.Exec(ctx, `
CREATE SEQUENCE handmade_postversion_id_seq
START WITH 40000 -- this is well out of the way of existing IDs
OWNED BY handmade_postversion.id;
ALTER TABLE handmade_postversion
ALTER id SET DEFAULT nextval('handmade_postversion_id_seq'),
ALTER CONSTRAINT handmade_postversion_post_id_fkey DEFERRABLE INITIALLY DEFERRED;
`)
if err != nil {
return oops.New(err, "failed to update postversion constraints")
}
return nil
}
func (m FixPostConstraints) Down(ctx context.Context, tx pgx.Tx) error {
panic("Implement me")
}

View File

@ -6,8 +6,6 @@ type Thread struct {
CategoryID int `db:"category_id"`
Title string `db:"title"`
Hits int `db:"hits"`
ReplyCount int `db:"reply_count"`
Sticky bool `db:"sticky"`
Locked bool `db:"locked"`
Deleted bool `db:"deleted"`

View File

@ -46,33 +46,29 @@
<div class="flex flex-row-reverse justify-start mt2">
<input type="submit" class="button ml2" name="submit" value="{{ .SubmitLabel }}" />
<input type="submit" class="button ml2" name="preview" value="{{ .PreviewLabel }}" />
</div>
<div class="post post-preview mv3 mathjax">
<div id="preview" class="body contents"></div>
</div>
{{/*
{% if are_editing %}
{{ if .IsEditing }}
<span class="editreason">
<label for="editreason">Edit reason:</label>
<input name="editreason" maxlength="255" type="text" id="editreason" value="{% if preview_edit_reason|length > 0 %}{{preview_edit_reason}}{% endif %}"/>
<input name="editreason" maxlength="255" type="text" id="editreason" />
</span>
{% endif %}
{{ end }}
{{/* TODO: Sticky threads
{% if user.is_staff and post and post.depth == 0 %}
<div class="checkbox sticky">
<input type="checkbox" name="sticky" id="sticky" {% if thread.sticky %}checked{% endif%} />
<label for="sticky">Sticky thread</label>
</div>
{% endif %}
*/}}
{% if are_previewing %}
{% include "edit_preview.html" %}
{% endif %}
{{/*
{% if context_reply_to %}
<h4>The post you're replying to:</h4>

View File

@ -3,6 +3,7 @@ package website
import (
"errors"
"math"
"net"
"net/http"
"strconv"
"strings"
@ -522,14 +523,10 @@ type editorData struct {
PostTitle string
PostBody string
SubmitLabel string
PreviewLabel string
IsEditing bool // false if new post, true if updating existing one
}
func ForumNewThread(c *RequestContext) ResponseData {
if c.Req.Method == http.MethodPost {
// TODO: Get preview data
}
baseData := getBaseData(c)
baseData.Title = "Create New Thread"
baseData.MathjaxEnabled = true
@ -550,16 +547,7 @@ func ForumNewThread(c *RequestContext) ResponseData {
BaseData: baseData,
SubmitUrl: hmnurl.BuildForumNewThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), true),
SubmitLabel: "Post New Thread",
PreviewLabel: "Preview",
}, c.Perf)
// err := res.WriteTemplate("forum_thread.html", forumThreadData{
// BaseData: baseData,
// Thread: templates.ThreadToTemplate(&thread),
// Posts: posts,
// CategoryUrl: hmnurl.BuildForumCategory(c.CurrentProject.Slug, currentSubforumSlugs, 1),
// ReplyUrl: hmnurl.BuildForumPostReply(c.CurrentProject.Slug, currentSubforumSlugs, thread.ID, *thread.FirstID),
// Pagination: pagination,
// }, c.Perf)
if err != nil {
panic(err)
}
@ -572,6 +560,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
if err != nil {
panic(err)
}
defer tx.Rollback(c.Context())
c.Perf.StartBlock("SQL", "Fetch category tree")
categoryTree := models.GetFullCategoryTree(c.Context(), c.Conn)
@ -593,41 +582,104 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
}
parsed := parsing.ParsePostInput(unparsed, false)
now := time.Now()
ip := net.ParseIP(c.Req.RemoteAddr)
// Create thread
var threadId int
err = tx.QueryRow(c.Context(),
`
INSERT INTO handmade_thread (title, sticky, locked, category_id)
INSERT INTO handmade_thread (title, sticky, category_id, first_id, last_id)
VALUES ($1, $2, $3, $4, $5)
RETURNING id
`,
title,
sticky,
false,
currentCatId,
-1,
-1,
).Scan(&threadId)
if err != nil {
panic(oops.New(err, "failed to create thread"))
}
// Create post version
_, err = tx.Exec(c.Context(),
// Create post
var postId int
err = tx.QueryRow(c.Context(),
`
INSERT INTO handmade_postversion (post_id, text_raw, text_parsed)
VALUES ($1, $2, $3)
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)
RETURNING id
`,
// TODO: post id
now,
currentCatId,
threadId,
"lol", // TODO: Actual previews
-1,
c.CurrentUser.ID,
models.CatKindForum,
c.CurrentProject.ID,
).Scan(&postId)
if err != nil {
panic(oops.New(err, "failed to create post"))
}
// Create post version
var versionId int
err = tx.QueryRow(c.Context(),
`
INSERT INTO handmade_postversion (post_id, text_raw, text_parsed, ip, date)
VALUES ($1, $2, $3, $4, $5)
RETURNING id
`,
postId,
unparsed,
parsed,
ip,
now,
).Scan(&versionId)
if err != nil {
panic(oops.New(err, "failed to create post version"))
}
// Update post with version id
_, err = tx.Exec(c.Context(),
`
UPDATE handmade_post
SET current_id = $1
WHERE id = $2
`,
versionId,
postId,
)
if err != nil {
panic(oops.New(err, "failed to set current post version"))
}
// Update thread with post id
_, err = tx.Exec(c.Context(),
`
UPDATE handmade_thread
SET
first_id = $1,
last_id = $1
WHERE id = $2
`,
postId,
threadId,
)
if err != nil {
panic(oops.New(err, "failed to set thread post ids"))
}
err = tx.Commit(c.Context())
if err != nil {
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to create new forum thread"))
}
// TODO: Redirect to newly created thread
return c.Redirect(hmnurl.BuildForumNewThread(models.HMNProjectSlug, nil, false), http.StatusSeeOther)
newThreadUrl := hmnurl.BuildForumThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), threadId, title, 1)
return c.Redirect(newThreadUrl, http.StatusSeeOther)
}
func validateSubforums(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, catPath string) (int, bool) {