M TODO => TODO +12 -3
@@ 1,15 1,24 @@
Setup fishbb.org (cloud provider)
FEATURES:
+minor/bugs:
rate limit registration
+better error handling on login
+better ux for account creation
+move lock to right place
+links on home and thread list should go to the post
+
+NOTIFICATIONS:
+- @mentions
+- auto-hyperlink @mention maybe?
+
+"shortpsot" list -> use summary code
Login/Reigster:
+add a reply button that uses @mentions
finish up oauth integration
-admin/mod ability to delete / edit posts
Notifications (Including integration with PWA / browser API)
-links on home and thread list should go to the post
Search
-thread count on index.html
----- alpha waterline ----- (may push lower to get something more complete out)
M db.go => db.go +4 -1
@@ 93,7 93,8 @@ func prepareStatements(db *sql.DB) {
stmtGetForums = prepare(db, `
select forums.id, name, description, permissions,
coalesce(threadid, 0), coalesce(latest.title, ''), coalesce(latest.id, 0), coalesce(latest.authorid, 0),
- coalesce(latest.username, ''), coalesce(latest.created, '')
+ coalesce(latest.username, ''), coalesce(latest.created, ''),
+ count(1)
from forums
left join (
select threadid, threads.title, posts.id, threads.authorid,
@@ 103,6 104,8 @@ func prepareStatements(db *sql.DB) {
join threads on posts.threadid = threads.id
group by forumid
) latest on latest.forumid = forums.id
+ left join threads on threads.forumid = forums.id
+ group by 1
`)
stmtGetForum = prepare(db, "select id, name, description, slug, permissions from forums where id = ?")
M forum.go => forum.go +2 -1
@@ 12,6 12,7 @@ type Forum struct {
// lowest level that can view this for
Permissions Role
LastPost PostSummary
+ ThreadCount int
}
func createForum(name, description string) error {
@@ 58,7 59,7 @@ func getForums() ([]Forum, error) {
err := rows.Scan(&f.ID, &f.Name, &f.Description, &f.Permissions,
&f.LastPost.ThreadID, &f.LastPost.ThreadTitle,
&f.LastPost.ID,
- &f.LastPost.Author.ID, &f.LastPost.Author.Username, &created)
+ &f.LastPost.Author.ID, &f.LastPost.Author.Username, &created, &f.ThreadCount)
if err != nil {
return nil, err
}
M views/forum.html => views/forum.html +2 -2
@@ 20,8 20,8 @@
</div>
{{ if and $.User $.User.Role.ModLevel }}
<span>
- <form style="display:inline;" method="POST" action="/f/{{$.ForumSlug}}/{{.ID}}/lock?s={{not .Locked}}"><button class="link-button" id="submit">{{ if .Locked}}Unl{{else}}L{{end}}ock</button></form>
- <form style="display:inline;" method="POST" action="/f/{{$.ForumSlug}}/{{.ID}}/pin?s={{ not .Pinned}}"><button class="link-button" id="submit">{{ if .Pinned}}Unp{{else}}P{{end}}in</button></form>
+ <form style="display:inline;" method="POST" action="/f/{{$.Forum.Slug}}/{{.ID}}/lock?s={{not .Locked}}"><button class="link-button" id="submit">{{ if .Locked}}Unl{{else}}L{{end}}ock</button></form>
+ <form style="display:inline;" method="POST" action="/f/{{$.Forum.Slug}}/{{.ID}}/pin?s={{ not .Pinned}}"><button class="link-button" id="submit">{{ if .Pinned}}Unp{{else}}P{{end}}in</button></form>
</span>
</div>
{{ end }}
M views/index.html => views/index.html +3 -2
@@ 1,15 1,16 @@
{{ template "header.html" . }}
<table class="nav-table" id="forums">
- <tr><th style="width:66%">Forum</th><th style="width:33%">Last Post</th></tr>
+ <tr><th style="width:60%">Forum</th><th>Threads</th><th style="width:30%">Last Post</th></tr>
{{ range .Forums }}
<tr>
<td>
<a class="title-link" href="/f/{{.Slug}}">{{.Name}}</a><br>
{{.Description}}
</td>
+ <td style="text-align:center;">{{.ThreadCount}}</td>
<td>
<a href="/f/{{.Slug}}/{{.LastPost.ThreadID}}?p={{.LastPost.ID}}">{{ timeago .LastPost.Created }}</a>
- by <a href="/user/{{.LastPost.Author.ID}}">{{.LastPost.Author.Username}}</a>
+ by <a href="/user/{{.LastPost.Author.ID}}">{{.LastPost.Author.Username}}</a><br>
in <a href="/f/{{.Slug}}/{{.LastPost.ThreadID}}">{{.LastPost.ThreadTitle}}</a><br>
</td>
</tr>
M views/login.html => views/login.html +1 -0
@@ 9,6 9,7 @@
<button tabindex=1 name="login" value="login">Login</button>
</form>
{{ if .Config.GoogleOAuthClientID }}
+No account yet? <a href="/register">Register!</a>
<br>
<a href="/auth/google/login">
<button class="gsi-material-button">
M views/thread.html => views/thread.html +6 -1
@@ 1,9 1,14 @@
{{ template "header.html" . }}
<div class="body-header">
+ {{ if or (not .Thread.Locked) (.User.Role.ModLevel) }}
<a href="/post/new?threadid={{.Thread.ID}}"><button class="top-button">Reply</button></a>
+ {{ end }}
{{ template "pagelist.html" . }}
</div>
-<div class="div-header"><a href="/f/{{.Forum.Slug}}">{{.Forum.Name}}</a> > {{.Thread.Title}}
+<div class="div-header">
+
+ {{ if .Thread.Locked }} {{ template "lock.svg" }} {{end}}
+ <a href="/f/{{.Forum.Slug}}">{{.Forum.Name}}</a> > {{.Thread.Title}}
</div>
{{ range .Posts }}
<div class="post" id={{.ID}}>
M web.go => web.go +8 -3
@@ 171,6 171,11 @@ func createNewPost(w http.ResponseWriter, r *http.Request) {
return // TODO 4xx
}
tid, _ := strconv.Atoi(r.URL.Query().Get("threadid"))
+ thread, _ := getThread(tid)
+ if thread.Locked && !u.Role.ModLevel() {
+ // can't post in locked thread
+ return // TODO 4xx
+ }
pid, err := createPost(u.UserID, int(tid), content)
if err != nil {
serverError(w, r, err)
@@ 196,7 201,7 @@ func doDeletePost(w http.ResponseWriter, r *http.Request) {
return
}
aid := post.Author.ID
- if u.UserID != aid {
+ if u.UserID != aid && !u.Role.ModLevel() {
unauthorized(w, r)
return
}
@@ 217,7 222,7 @@ func editPostPage(w http.ResponseWriter, r *http.Request) {
return
}
u := GetUserInfo(r)
- if post.Author.ID != u.UserID {
+ if post.Author.ID != u.UserID && !u.Role.ModLevel() {
unauthorized(w, r)
return
}
@@ 308,7 313,7 @@ func registerPage(w http.ResponseWriter, r *http.Request) {
return
}
- err := createUser(username, email, password, RoleUser, config.RequiresApproval)
+ err := createUser(username, email, password, RoleUser, !config.RequiresApproval)
if err != nil {
serverError(w, r, err)
}