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);
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
border-color: black;
|
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;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
div.town {
|
div.town {
|
||||||
& > section:not(:last-child) {
|
& > section:not(:last-child) {
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
@ -494,3 +525,9 @@ div#battle-window {
|
|||||||
color: white;
|
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) {
|
func All() ([]*Forum, error) {
|
||||||
var forums []*Forum
|
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
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Threads() ([]*Forum, error) {
|
func Threads() ([]*Forum, error) {
|
||||||
var forums []*Forum
|
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
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,37 +97,37 @@ func ByParent(parentID int) ([]*Forum, error) {
|
|||||||
var forums []*Forum
|
var forums []*Forum
|
||||||
if parentID > 0 {
|
if parentID > 0 {
|
||||||
// Replies sorted chronologically
|
// 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
|
return forums, err
|
||||||
} else {
|
} else {
|
||||||
// Threads sorted by last activity
|
// 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
|
return forums, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByAuthor(authorID int) ([]*Forum, error) {
|
func ByAuthor(authorID int) ([]*Forum, error) {
|
||||||
var forums []*Forum
|
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
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Recent(limit int) ([]*Forum, error) {
|
func Recent(limit int) ([]*Forum, error) {
|
||||||
var forums []*Forum
|
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
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Search(term string) ([]*Forum, error) {
|
func Search(term string) ([]*Forum, error) {
|
||||||
var forums []*Forum
|
var forums []*Forum
|
||||||
searchTerm := "%" + term + "%"
|
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
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Since(since int64) ([]*Forum, error) {
|
func Since(since int64) ([]*Forum, error) {
|
||||||
var forums []*Forum
|
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
|
return forums, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,11 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"dk/internal/components"
|
"dk/internal/components"
|
||||||
|
"dk/internal/database"
|
||||||
|
"dk/internal/models/forum"
|
||||||
|
"dk/internal/models/users"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
sushi "git.sharkk.net/Sharkk/Sushi"
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
||||||
"git.sharkk.net/Sharkk/Sushi/auth"
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
||||||
@ -11,8 +16,77 @@ func RegisterForumRoutes(app *sushi.App) {
|
|||||||
authed := app.Group("/forum")
|
authed := app.Group("/forum")
|
||||||
authed.Use(auth.RequireAuth())
|
authed.Use(auth.RequireAuth())
|
||||||
authed.Get("/", index)
|
authed.Get("/", index)
|
||||||
|
authed.Get("/new", showNew)
|
||||||
|
authed.Post("/new", new)
|
||||||
|
authed.Get("/:id", showThread)
|
||||||
}
|
}
|
||||||
|
|
||||||
func index(ctx sushi.Ctx) {
|
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"}
|
{include "layout.html"}
|
||||||
|
|
||||||
{block "content"}
|
{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}
|
{/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