Get forum post creation working
This commit is contained in:
parent
de0b7a08fb
commit
c1785d79a4
|
@ -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")
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -6,8 +6,6 @@ type Thread struct {
|
||||||
CategoryID int `db:"category_id"`
|
CategoryID int `db:"category_id"`
|
||||||
|
|
||||||
Title string `db:"title"`
|
Title string `db:"title"`
|
||||||
Hits int `db:"hits"`
|
|
||||||
ReplyCount int `db:"reply_count"`
|
|
||||||
Sticky bool `db:"sticky"`
|
Sticky bool `db:"sticky"`
|
||||||
Locked bool `db:"locked"`
|
Locked bool `db:"locked"`
|
||||||
Deleted bool `db:"deleted"`
|
Deleted bool `db:"deleted"`
|
||||||
|
|
|
@ -46,33 +46,29 @@
|
||||||
|
|
||||||
<div class="flex flex-row-reverse justify-start mt2">
|
<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="submit" value="{{ .SubmitLabel }}" />
|
||||||
<input type="submit" class="button ml2" name="preview" value="{{ .PreviewLabel }}" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="post post-preview mv3 mathjax">
|
<div class="post post-preview mv3 mathjax">
|
||||||
<div id="preview" class="body contents"></div>
|
<div id="preview" class="body contents"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/*
|
{{ if .IsEditing }}
|
||||||
{% if are_editing %}
|
|
||||||
<span class="editreason">
|
<span class="editreason">
|
||||||
<label for="editreason">Edit reason:</label>
|
<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>
|
</span>
|
||||||
{% endif %}
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* TODO: Sticky threads
|
||||||
{% if user.is_staff and post and post.depth == 0 %}
|
{% if user.is_staff and post and post.depth == 0 %}
|
||||||
<div class="checkbox sticky">
|
<div class="checkbox sticky">
|
||||||
<input type="checkbox" name="sticky" id="sticky" {% if thread.sticky %}checked{% endif%} />
|
<input type="checkbox" name="sticky" id="sticky" {% if thread.sticky %}checked{% endif%} />
|
||||||
<label for="sticky">Sticky thread</label>
|
<label for="sticky">Sticky thread</label>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
*/}}
|
||||||
|
|
||||||
|
{{/*
|
||||||
|
|
||||||
{% if are_previewing %}
|
|
||||||
{% include "edit_preview.html" %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if context_reply_to %}
|
{% if context_reply_to %}
|
||||||
<h4>The post you're replying to:</h4>
|
<h4>The post you're replying to:</h4>
|
||||||
|
|
|
@ -3,6 +3,7 @@ package website
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -522,14 +523,10 @@ type editorData struct {
|
||||||
PostTitle string
|
PostTitle string
|
||||||
PostBody string
|
PostBody string
|
||||||
SubmitLabel string
|
SubmitLabel string
|
||||||
PreviewLabel string
|
IsEditing bool // false if new post, true if updating existing one
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForumNewThread(c *RequestContext) ResponseData {
|
func ForumNewThread(c *RequestContext) ResponseData {
|
||||||
if c.Req.Method == http.MethodPost {
|
|
||||||
// TODO: Get preview data
|
|
||||||
}
|
|
||||||
|
|
||||||
baseData := getBaseData(c)
|
baseData := getBaseData(c)
|
||||||
baseData.Title = "Create New Thread"
|
baseData.Title = "Create New Thread"
|
||||||
baseData.MathjaxEnabled = true
|
baseData.MathjaxEnabled = true
|
||||||
|
@ -550,16 +547,7 @@ func ForumNewThread(c *RequestContext) ResponseData {
|
||||||
BaseData: baseData,
|
BaseData: baseData,
|
||||||
SubmitUrl: hmnurl.BuildForumNewThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), true),
|
SubmitUrl: hmnurl.BuildForumNewThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), true),
|
||||||
SubmitLabel: "Post New Thread",
|
SubmitLabel: "Post New Thread",
|
||||||
PreviewLabel: "Preview",
|
|
||||||
}, c.Perf)
|
}, 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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -572,6 +560,7 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
defer tx.Rollback(c.Context())
|
||||||
|
|
||||||
c.Perf.StartBlock("SQL", "Fetch category tree")
|
c.Perf.StartBlock("SQL", "Fetch category tree")
|
||||||
categoryTree := models.GetFullCategoryTree(c.Context(), c.Conn)
|
categoryTree := models.GetFullCategoryTree(c.Context(), c.Conn)
|
||||||
|
@ -593,41 +582,104 @@ func ForumNewThreadSubmit(c *RequestContext) ResponseData {
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed := parsing.ParsePostInput(unparsed, false)
|
parsed := parsing.ParsePostInput(unparsed, false)
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
ip := net.ParseIP(c.Req.RemoteAddr)
|
||||||
|
|
||||||
// Create thread
|
// Create thread
|
||||||
var threadId int
|
var threadId int
|
||||||
err = tx.QueryRow(c.Context(),
|
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
|
RETURNING id
|
||||||
`,
|
`,
|
||||||
title,
|
title,
|
||||||
sticky,
|
sticky,
|
||||||
false,
|
|
||||||
currentCatId,
|
currentCatId,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
).Scan(&threadId)
|
).Scan(&threadId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(oops.New(err, "failed to create thread"))
|
panic(oops.New(err, "failed to create thread"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create post version
|
// Create post
|
||||||
_, err = tx.Exec(c.Context(),
|
var postId int
|
||||||
|
err = tx.QueryRow(c.Context(),
|
||||||
`
|
`
|
||||||
INSERT INTO handmade_postversion (post_id, text_raw, text_parsed)
|
INSERT INTO handmade_post (postdate, category_id, thread_id, preview, current_id, author_id, category_kind, project_id)
|
||||||
VALUES ($1, $2, $3)
|
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,
|
unparsed,
|
||||||
parsed,
|
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())
|
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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Redirect to newly created thread
|
newThreadUrl := hmnurl.BuildForumThread(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), threadId, title, 1)
|
||||||
return c.Redirect(hmnurl.BuildForumNewThread(models.HMNProjectSlug, nil, false), http.StatusSeeOther)
|
return c.Redirect(newThreadUrl, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSubforums(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, catPath string) (int, bool) {
|
func validateSubforums(lineageBuilder *models.CategoryLineageBuilder, project *models.Project, catPath string) (int, bool) {
|
||||||
|
|
Loading…
Reference in New Issue