forum threads
This commit is contained in:
parent
c147df63dc
commit
340d4cf6e8
@ -272,6 +272,33 @@ form.standard {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
textarea.forum-text {
|
||||
appearance: none;
|
||||
outline: none;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.25rem 0.25rem;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
color: white;
|
||||
min-height: 200px;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,6 +314,10 @@ form.standard {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.town {
|
||||
& > section:not(:last-child) {
|
||||
margin-bottom: 2rem;
|
||||
@ -494,3 +525,9 @@ div#battle-window {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
div.thread-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
BIN
data/dk.db
BIN
data/dk.db
Binary file not shown.
@ -83,13 +83,13 @@ func Find(id int) (*Forum, error) {
|
||||
|
||||
func All() ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC")
|
||||
err := database.Select(&forums, "SELECT * FROM forum ORDER BY last_post DESC, id DESC")
|
||||
return forums, err
|
||||
}
|
||||
|
||||
func Threads() ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY newpostdate DESC, id DESC")
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = 0 ORDER BY last_post DESC, id DESC")
|
||||
return forums, err
|
||||
}
|
||||
|
||||
@ -97,37 +97,37 @@ func ByParent(parentID int) ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
if parentID > 0 {
|
||||
// Replies sorted chronologically
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY postdate ASC, id ASC", parentID)
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY posted ASC, id ASC", parentID)
|
||||
return forums, err
|
||||
} else {
|
||||
// Threads sorted by last activity
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY newpostdate DESC, id DESC", parentID)
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE parent = %d ORDER BY last_post DESC, id DESC", parentID)
|
||||
return forums, err
|
||||
}
|
||||
}
|
||||
|
||||
func ByAuthor(authorID int) ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY postdate DESC, id DESC", authorID)
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE author = %d ORDER BY posted DESC, id DESC", authorID)
|
||||
return forums, err
|
||||
}
|
||||
|
||||
func Recent(limit int) ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
err := database.Select(&forums, "SELECT * FROM forum ORDER BY newpostdate DESC, id DESC LIMIT %d", limit)
|
||||
err := database.Select(&forums, "SELECT * FROM forum ORDER BY last_post DESC, id DESC LIMIT %d", limit)
|
||||
return forums, err
|
||||
}
|
||||
|
||||
func Search(term string) ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
searchTerm := "%" + term + "%"
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY newpostdate DESC, id DESC", searchTerm, searchTerm)
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE title LIKE %s OR content LIKE %s ORDER BY last_post DESC, id DESC", searchTerm, searchTerm)
|
||||
return forums, err
|
||||
}
|
||||
|
||||
func Since(since int64) ([]*Forum, error) {
|
||||
var forums []*Forum
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE newpostdate >= %d ORDER BY newpostdate DESC, id DESC", since)
|
||||
err := database.Select(&forums, "SELECT * FROM forum WHERE last_post >= %d ORDER BY last_post DESC, id DESC", since)
|
||||
return forums, err
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,11 @@ package routes
|
||||
|
||||
import (
|
||||
"dk/internal/components"
|
||||
"dk/internal/database"
|
||||
"dk/internal/models/forum"
|
||||
"dk/internal/models/users"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
sushi "git.sharkk.net/Sharkk/Sushi"
|
||||
"git.sharkk.net/Sharkk/Sushi/auth"
|
||||
@ -11,8 +16,77 @@ func RegisterForumRoutes(app *sushi.App) {
|
||||
authed := app.Group("/forum")
|
||||
authed.Use(auth.RequireAuth())
|
||||
authed.Get("/", index)
|
||||
authed.Get("/new", showNew)
|
||||
authed.Post("/new", new)
|
||||
authed.Get("/:id", showThread)
|
||||
}
|
||||
|
||||
func index(ctx sushi.Ctx) {
|
||||
components.RenderPage(ctx, "Forum", "forum/index.html", map[string]any{})
|
||||
threads, err := forum.Threads()
|
||||
if err != nil {
|
||||
threads = make([]*forum.Forum, 0)
|
||||
}
|
||||
fmt.Printf("\nFound %d threads\n", len(threads))
|
||||
|
||||
components.RenderPage(ctx, "Forum", "forum/index.html", map[string]any{
|
||||
"threads": threads,
|
||||
})
|
||||
}
|
||||
|
||||
func showNew(ctx sushi.Ctx) {
|
||||
components.RenderPage(ctx, "New Forum Thread", "forum/new.html", map[string]any{})
|
||||
}
|
||||
|
||||
func new(ctx sushi.Ctx) {
|
||||
sess := ctx.GetCurrentSession()
|
||||
|
||||
title := strings.TrimSpace(ctx.Form("title").String())
|
||||
content := strings.TrimSpace(ctx.Form("content").String())
|
||||
|
||||
if title == "" {
|
||||
sess.SetFlash("error", "Thread title cannot be empty")
|
||||
ctx.Redirect("/forum/new")
|
||||
return
|
||||
}
|
||||
|
||||
if content == "" {
|
||||
sess.SetFlash("error", "Thread content cannot be empty")
|
||||
ctx.Redirect("/forum/new")
|
||||
return
|
||||
}
|
||||
|
||||
user := ctx.GetCurrentUser().(*users.User)
|
||||
|
||||
thread := forum.New()
|
||||
thread.Author = user.ID
|
||||
thread.Title = title
|
||||
thread.Content = content
|
||||
|
||||
database.Transaction(func() error {
|
||||
return thread.Insert()
|
||||
})
|
||||
|
||||
ctx.Redirect(fmt.Sprintf("/forum/%d", thread.ID))
|
||||
}
|
||||
|
||||
func showThread(ctx sushi.Ctx) {
|
||||
sess := ctx.GetCurrentSession()
|
||||
id := ctx.Param("id").Int()
|
||||
|
||||
thread, err := forum.Find(id)
|
||||
if err != nil {
|
||||
sess.SetFlash("error", fmt.Sprintf("Forum thread %d not found", id))
|
||||
ctx.Redirect("/forum")
|
||||
return
|
||||
}
|
||||
|
||||
if thread.Parent != 0 {
|
||||
sess.SetFlash("error", fmt.Sprintf("Forum post %d is not a thread", id))
|
||||
ctx.Redirect("/forum")
|
||||
return
|
||||
}
|
||||
|
||||
components.RenderPage(ctx, thread.Title, "forum/thread.html", map[string]any{
|
||||
"thread": thread,
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,27 @@
|
||||
{include "layout.html"}
|
||||
|
||||
{block "content"}
|
||||
FORUM PAGE
|
||||
<div class="thread-title mb-05">
|
||||
<h2>Forum</h2>
|
||||
|
||||
<div>
|
||||
<a href="/forum/new"><button class="btn btn-primary">New Thread</button></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{if #threads > 0}
|
||||
<table>
|
||||
<tbody>
|
||||
{for thread in threads}
|
||||
<tr>
|
||||
<td><a href="/forum/{thread.ID}">{thread.Title}</a></td>
|
||||
<td>{thread.Replies} Replies</td>
|
||||
</tr>
|
||||
{/for}
|
||||
</tbody>
|
||||
</table>
|
||||
{else}
|
||||
No threads!
|
||||
{/if}
|
||||
{/block}
|
||||
|
19
templates/forum/new.html
Normal file
19
templates/forum/new.html
Normal file
@ -0,0 +1,19 @@
|
||||
{include "layout.html"}
|
||||
|
||||
{block "content"}
|
||||
<h2 class="mb-1">Forum</h2>
|
||||
|
||||
<form action="/forum/new" method="post" class="standard">
|
||||
{csrf}
|
||||
|
||||
<div class="mb-1">
|
||||
<div class="mb-025"><input type="text" class="text w-full" placeholder="Thread title" name="title"></div>
|
||||
<textarea class="forum-text w-full" placeholder="Type your message here!" name="content"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary">Post</button>
|
||||
<a href="/forum"><button class="btn">Back to Forums</button></a>
|
||||
</div>
|
||||
</form>
|
||||
{/block}
|
17
templates/forum/thread.html
Normal file
17
templates/forum/thread.html
Normal file
@ -0,0 +1,17 @@
|
||||
{include "layout.html"}
|
||||
|
||||
{block "content"}
|
||||
<div class="thread-title mb-05">
|
||||
<h2>{thread.Title}</h2>
|
||||
|
||||
<div>
|
||||
<a href="/forum"><button class="btn">Back</button></a>
|
||||
<a href="/forum/{thread.ID}/reply"><button class="btn btn-primary">Reply</button></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p>
|
||||
{thread.Content}
|
||||
</p>
|
||||
{/block}
|
Loading…
x
Reference in New Issue
Block a user