Implements ability to lock threads.

This commit is contained in:
Dominik Picheta 2018-05-21 19:43:16 +01:00
commit c9538ec703
5 changed files with 124 additions and 5 deletions

View file

@ -241,6 +241,10 @@ $views-color: #545d70;
margin: 0;
}
i.fas {
margin-right: $control-padding-x-sm;
}
}
.posts, .about {
@ -384,6 +388,7 @@ $views-color: #545d70;
.btn {
float: right;
margin-right: 0;
margin-left: $control-padding-x;
}
}

View file

@ -419,10 +419,28 @@ proc executeReply(c: TForumData, threadId: int, content: string,
if rateLimitCheck(c):
raise newForumError("You're posting too fast!")
if content.strip().len == 0:
raise newForumError("Message cannot be empty")
if not validateRst(c, content):
raise newForumError("Message needs to be valid RST", @["msg"])
# Verify that content can be parsed as RST.
# Ensure that the thread isn't locked.
let isLocked = getValue(
db,
sql"""
select isLocked from thread where id in (
select thread from post where id = ?
)
""",
threadId
)
if isLocked.len == 0:
raise newForumError("Thread not found.")
if isLocked == "1":
raise newForumError("Cannot reply to a locked thread.")
let retID = insertID(
db,
crud(crCreate, "post", "author", "ip", "content", "thread", "replyingTo"),
@ -630,6 +648,14 @@ proc executeUnlike(c: TForumData, postId: int) =
# Delete the like.
exec(db, crud(crDelete, "like"), likeId)
proc executeLockState(c: TForumData, threadId: int, locked: bool) =
# Verify that the logged in user has the correct permissions.
if c.rank < Moderator:
raise newForumError("You cannot lock this thread.")
# Save the like.
exec(db, crud(crUpdate, "thread", "isLocked"), locked.int, threadId)
proc executeDeletePost(c: TForumData, postId: int) =
# Verify that this post belongs to the user.
const postQuery = sql"""
@ -1127,6 +1153,33 @@ routes:
except ForumError as exc:
resp Http400, $(%exc.data), "application/json"
post re"/(lock|unlock)":
createTFD()
if not c.loggedIn():
let err = PostError(
errorFields: @[],
message: "Not logged in."
)
resp Http401, $(%err), "application/json"
let formData = request.formData
cond "id" in formData
let threadId = getInt(formData["id"].body, -1)
cond threadId != -1
try:
case request.path
of "/lock":
executeLockState(c, threadId, true)
of "/unlock":
executeLockState(c, threadId, false)
else:
assert false
resp Http200, "{}", "application/json"
except ForumError as exc:
resp Http400, $(%exc.data), "application/json"
post re"/delete(Post|Thread)":
createTFD()
if not c.loggedIn():

View file

@ -8,7 +8,7 @@ when defined(js):
include karax/prelude
import karax/[kajax, kdom]
import error, karaxutils, post, user
import error, karaxutils, post, user, threadlist
type
PostButton* = ref object
@ -142,4 +142,61 @@ when defined(js):
span(class="like-count"):
text $post.likes.len
italic(class=class({"far": not liked, "fas": liked}, "fa-heart"))
italic(class=class({"far": not liked, "fas": liked}, "fa-heart"))
type
LockButton* = ref object
error: Option[PostError]
loading: bool
proc newLockButton*(): LockButton =
LockButton()
proc onPost(httpStatus: int, response: kstring, state: LockButton,
thread: var Thread) =
postFinished:
thread.isLocked = not thread.isLocked
proc onLockClick(ev: Event, n: VNode, state: LockButton, thread: var Thread) =
if state.loading: return
state.loading = true
state.error = none[PostError]()
# TODO: This is a hack, karax should support this.
var formData = newFormData()
formData.append("id", $thread.id)
let uri =
if thread.isLocked:
makeUri("/unlock")
else:
makeUri("/lock")
ajaxPost(uri, @[], cast[cstring](formData),
(s: int, r: kstring) =>
onPost(s, r, state, thread))
ev.preventDefault()
proc render*(state: LockButton, thread: var Thread,
currentUser: Option[User]): VNode =
if currentUser.isNone() or
currentUser.get().rank < Moderator:
return buildHtml(tdiv())
let tooltip =
if state.error.isSome(): state.error.get().message
else: ""
result = buildHtml():
button(class="btn btn-secondary",
onClick=(e: Event, n: VNode) =>
onLockClick(e, n, state, thread),
"data-tooltip"=tooltip,
onmouseleave=(e: Event, n: VNode) =>
(state.error = none[PostError]())):
if thread.isLocked:
italic(class="fas fa-unlock-alt")
text " Unlock Thread"
else:
italic(class="fas fa-lock")
text " Lock Thread"

View file

@ -32,6 +32,7 @@ when defined(js):
editBox: EditBox
likeButton: LikeButton
deleteModal: DeleteModal
lockButton: LockButton
proc onReplyPosted(id: int)
proc onEditPosted(id: int, content: string, subject: Option[string])
@ -47,7 +48,8 @@ when defined(js):
replyBox: newReplyBox(onReplyPosted),
editBox: newEditBox(onEditPosted, onEditCancelled),
likeButton: newLikeButton(),
deleteModal: newDeleteModal(onDeletePost, onDeleteThread, nil)
deleteModal: newDeleteModal(onDeletePost, onDeleteThread, nil),
lockButton: newLockButton()
)
var
@ -364,6 +366,8 @@ when defined(js):
italic(class="fas fa-reply")
text " Reply"
render(state.lockButton, list.thread, currentUser)
render(state.replyBox, list.thread, state.replyingTo, false)
render(state.deleteModal)

View file

@ -121,7 +121,7 @@ when defined(js):
value=state.text)
if state.error.isSome():
p(class="text-error",
span(class="text-error",
style=style(StyleAttr.marginTop, "0.4rem")):
text state.error.get().message