diff --git a/src/forum.nim b/src/forum.nim index c2682eb..c85ad2e 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -276,7 +276,7 @@ template createTFD() = new(c) init(c) c.req = request - if cookies(request).len > 0: + if request.cookies.len > 0: checkLoggedIn(c) #[ DB functions. TODO: Move to another module? ]# @@ -400,10 +400,10 @@ proc selectThread(threadRow: seq[string], author: User): Thread = id: threadRow[0].parseInt, topic: threadRow[1], category: Category( - id: threadRow[6].parseInt, - name: threadRow[7], - description: threadRow[8], - color: threadRow[9] + id: threadRow[5].parseInt, + name: threadRow[6], + description: threadRow[7], + color: threadRow[8] ), users: @[], replies: posts[0].parseInt-1, @@ -412,7 +412,6 @@ proc selectThread(threadRow: seq[string], author: User): Thread = creation: posts[1].parseInt, isLocked: threadRow[4] == "1", isSolved: false, # TODO: Add a field to `post` to identify the solution. - isPinned: threadRow[5] == "1" ) # Gather the users list. @@ -710,13 +709,6 @@ proc executeLockState(c: TForumData, threadId: int, locked: bool) = # Save the like. exec(db, crud(crUpdate, "thread", "isLocked"), locked.int, threadId) -proc executePinState(c: TForumData, threadId: int, pinned: bool) = - if c.rank < Moderator: - raise newForumError("You do not have permission to pin this thread.") - - # (Un)pin this thread - exec(db, crud(crUpdate, "thread", "isPinned"), pinned.int, threadId) - proc executeDeletePost(c: TForumData, postId: int) = # Verify that this post belongs to the user. const postQuery = sql""" @@ -841,7 +833,7 @@ routes: categoryArgs.insert($categoryId, 0) const threadsQuery = - """select t.id, t.name, views, strftime('%s', modified), isLocked, isPinned, + """select t.id, t.name, views, strftime('%s', modified), isLocked, c.id, c.name, c.description, c.color, u.id, u.name, u.email, strftime('%s', u.lastOnline), strftime('%s', u.previousVisitAt), u.status, u.isDeleted @@ -854,14 +846,14 @@ routes: order by p.author limit 1 ) - order by isPinned desc, modified desc limit ?, ?;""" + order by modified desc limit ?, ?;""" let thrCount = getValue(db, countQuery, countArgs).parseInt() let moreCount = max(0, thrCount - (start + count)) var list = ThreadList(threads: @[], moreCount: moreCount) for data in getAllRows(db, sql(threadsQuery % categorySection), categoryArgs): - let thread = selectThread(data[0 .. 9], selectUser(data[10 .. ^1])) + let thread = selectThread(data[0 .. 8], selectUser(data[9 .. ^1])) list.threads.add(thread) resp $(%list), "application/json" @@ -876,17 +868,12 @@ routes: count = 10 const threadsQuery = - sql"""select t.id, t.name, views, strftime('%s', modified), isLocked, isPinned, + sql"""select t.id, t.name, views, strftime('%s', modified), isLocked, c.id, c.name, c.description, c.color from thread t, category c where t.id = ? and isDeleted = 0 and category = c.id;""" let threadRow = getRow(db, threadsQuery, id) - if threadRow[0].len == 0: - let err = PostError( - message: "Specified thread does not exist" - ) - resp Http404, $(%err), "application/json" let thread = selectThread(threadRow, selectThreadAuthor(id)) let postsQuery = @@ -932,14 +919,9 @@ routes: get "/specific_posts.json": createTFD() - var ids: JsonNode - try: + var ids = parseJson(@"ids") - except JsonParsingError: - let err = PostError( - message: "Invalid JSON in the `ids` parameter" - ) - resp Http400, $(%err), "application/json" + cond ids.kind == JArray let intIDs = ids.elems.map(x => x.getInt()) let postsQuery = sql(""" @@ -1357,33 +1339,6 @@ routes: except ForumError as exc: resp Http400, $(%exc.data), "application/json" - post re"/(pin|unpin)": - 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 "/pin": - executePinState(c, threadId, true) - of "/unpin": - executePinState(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(): diff --git a/src/frontend/header.nim b/src/frontend/header.nim index 7cfb133..cde48de 100644 --- a/src/frontend/header.nim +++ b/src/frontend/header.nim @@ -96,8 +96,8 @@ when defined(js): section(class="navbar-section"): tdiv(class="input-group input-inline"): input(class="search-input input-sm", - `type`="search", placeholder="Search", - id="search-box", required="required", + `type`="text", placeholder="search", + id="search-box", onKeyDown=onKeyDown) if state.loading: tdiv(class="loading") diff --git a/src/frontend/post.nim b/src/frontend/post.nim index dc12e47..0530814 100644 --- a/src/frontend/post.nim +++ b/src/frontend/post.nim @@ -64,4 +64,4 @@ when defined(js): renderPostUrl(thread.id, post.id) proc renderPostUrl*(link: PostLink): string = - renderPostUrl(link.threadId, link.postId) + renderPostUrl(link.threadId, link.postId) \ No newline at end of file diff --git a/src/frontend/postbutton.nim b/src/frontend/postbutton.nim index 9fa4ab4..8bd9c34 100644 --- a/src/frontend/postbutton.nim +++ b/src/frontend/postbutton.nim @@ -190,7 +190,7 @@ when defined(js): else: "" result = buildHtml(): - button(class="btn btn-secondary", id="lock-btn", + button(class="btn btn-secondary", onClick=(e: Event, n: VNode) => onLockClick(e, n, state, thread), "data-tooltip"=tooltip, @@ -201,61 +201,4 @@ when defined(js): text " Unlock Thread" else: italic(class="fas fa-lock") - text " Lock Thread" - - type - PinButton* = ref object - error: Option[PostError] - loading: bool - - proc newPinButton*(): PinButton = - PinButton() - - proc onPost(httpStatus: int, response: kstring, state: PinButton, - thread: var Thread) = - postFinished: - thread.isPinned = not thread.isPinned - - proc onPinClick(ev: Event, n: VNode, state: PinButton, thread: var Thread) = - if state.loading: return - - state.loading = true - state.error = none[PostError]() - - # Same as LockButton so the following is still a hack and karax should support this. - var formData = newFormData() - formData.append("id", $thread.id) - let uri = - if thread.isPinned: - makeUri("/unpin") - else: - makeUri("/pin") - ajaxPost(uri, @[], formData.to(cstring), - (s: int, r: kstring) => onPost(s, r, state, thread)) - - ev.preventDefault() - - proc render*(state: PinButton, 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", id="pin-btn", - onClick=(e: Event, n: VNode) => - onPinClick(e, n, state, thread), - "data-tooltip"=tooltip, - onmouseleave=(e: Event, n: VNode) => - (state.error = none[PostError]())): - if thread.isPinned: - italic(class="fas fa-thumbtack") - text " Unpin Thread" - else: - italic(class="fas fa-thumbtack") - text " Pin Thread" - + text " Lock Thread" \ No newline at end of file diff --git a/src/frontend/postlist.nim b/src/frontend/postlist.nim index 66b3162..7da57c1 100644 --- a/src/frontend/postlist.nim +++ b/src/frontend/postlist.nim @@ -36,7 +36,6 @@ when defined(js): likeButton: LikeButton deleteModal: DeleteModal lockButton: LockButton - pinButton: PinButton categoryPicker: CategoryPicker proc onReplyPosted(id: int) @@ -57,7 +56,6 @@ when defined(js): likeButton: newLikeButton(), deleteModal: newDeleteModal(onDeletePost, onDeleteThread, nil), lockButton: newLockButton(), - pinButton: newPinButton(), categoryPicker: newCategoryPicker(onCategoryChanged) ) @@ -413,7 +411,6 @@ when defined(js): text " Reply" render(state.lockButton, list.thread, currentUser) - render(state.pinButton, list.thread, currentUser) render(state.replyBox, list.thread, state.replyingTo, false) diff --git a/src/frontend/threadlist.nim b/src/frontend/threadlist.nim index ecec6da..e0b2d36 100644 --- a/src/frontend/threadlist.nim +++ b/src/frontend/threadlist.nim @@ -15,7 +15,6 @@ type creation*: int64 ## Unix timestamp isLocked*: bool isSolved*: bool - isPinned*: bool ThreadList* = ref object threads*: seq[Thread] @@ -97,18 +96,15 @@ when defined(js): else: return $duration.inSeconds & "s" - proc genThread(pos: int, thread: Thread, isNew: bool, noBorder: bool, displayCategory=true): VNode = + proc genThread(thread: Thread, isNew: bool, noBorder: bool, displayCategory=true): VNode = let isOld = (getTime() - thread.creation.fromUnix).inWeeks > 2 let isBanned = thread.author.rank.isBanned() result = buildHtml(): - tr(class=class({"no-border": noBorder, "banned": isBanned, "pinned": thread.isPinned, "thread-" & $pos: true})): + tr(class=class({"no-border": noBorder, "banned": isBanned})): td(class="thread-title"): if thread.isLocked: italic(class="fas fa-lock fa-xs", title="Thread cannot be replied to") - if thread.isPinned: - italic(class="fas fa-thumbtack fa-xs", - title="Pinned post") if isBanned: italic(class="fas fa-ban fa-xs", title="Thread author is banned") @@ -227,7 +223,7 @@ when defined(js): let isLastThread = i+1 == list.threads.len let (isLastUnseen, isNew) = getInfo(list.threads, i, currentUser) - genThread(i+1, thread, isNew, + genThread(thread, isNew, noBorder=isLastUnseen or isLastThread, displayCategory=displayCategory) if isLastUnseen and (not isLastThread): diff --git a/src/fts.sql b/src/fts.sql index 1590a05..ee6f1af 100644 --- a/src/fts.sql +++ b/src/fts.sql @@ -7,7 +7,6 @@ SELECT post_id, post_content, cdate, - person.id, person.name AS author, person.email AS email, strftime('%s', person.lastOnline) AS lastOnline, diff --git a/src/setup_nimforum.nim b/src/setup_nimforum.nim index c80ad3b..34511fa 100644 --- a/src/setup_nimforum.nim +++ b/src/setup_nimforum.nim @@ -81,7 +81,6 @@ proc initialiseDb(admin: tuple[username, password, email: string], isLocked boolean not null default 0, solution integer, isDeleted boolean not null default 0, - isPinned boolean not null default 0, foreign key (category) references category(id), foreign key (solution) references post(id) diff --git a/tests/browsertests/common.nim b/tests/browsertests/common.nim index e5924f3..d906675 100644 --- a/tests/browsertests/common.nim +++ b/tests/browsertests/common.nim @@ -30,8 +30,7 @@ proc elementIsSome(element: Option[Element]): bool = proc elementIsNone(element: Option[Element]): bool = return element.isNone -proc waitForElement*(session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, - waitCondition: proc(element: Option[Element]): bool = elementIsSome): Option[Element] +proc waitForElement*(session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, waitCondition=elementIsSome): Option[Element] proc click*(session: Session, element: string, strategy=CssSelector) = let el = session.waitForElement(element, strategy) @@ -72,14 +71,14 @@ proc setColor*(session: Session, element, color: string, strategy=CssSelector) = proc checkIsNone*(session: Session, element: string, strategy=CssSelector) = discard session.waitForElement(element, strategy, waitCondition=elementIsNone) -template checkText*(session: Session, element, expectedValue: string) = +proc checkText*(session: Session, element, expectedValue: string) = let el = session.waitForElement(element) check el.get().getText() == expectedValue proc waitForElement*( session: Session, selector: string, strategy=CssSelector, timeout=20000, pollTime=50, - waitCondition: proc(element: Option[Element]): bool = elementIsSome + waitCondition=elementIsSome ): Option[Element] = var waitTime = 0 diff --git a/tests/browsertests/scenario1.nim b/tests/browsertests/scenario1.nim index dc78007..6e10e4c 100644 --- a/tests/browsertests/scenario1.nim +++ b/tests/browsertests/scenario1.nim @@ -40,4 +40,4 @@ proc test*(session: Session, baseUrl: string) = register "TEst1", "test1", verify = false ensureExists "#signup-form .has-error" - navigate baseUrl + navigate baseUrl \ No newline at end of file diff --git a/tests/browsertests/threads.nim b/tests/browsertests/threads.nim index 32ce686..e80b9c0 100644 --- a/tests/browsertests/threads.nim +++ b/tests/browsertests/threads.nim @@ -1,4 +1,5 @@ import unittest, common + import webdriver let @@ -57,35 +58,10 @@ proc userTests(session: Session, baseUrl: string) = # Make sure the forum post is gone checkIsNone "To be deleted", LinkTextSelector - test "cannot (un)pin thread": - with session: - navigate(baseUrl) - - click "#new-thread-btn" - - sendKeys "#thread-title", "Unpinnable" - sendKeys "#reply-textarea", "Cannot (un)pin as an user" - - click "#create-thread-btn" - - checkIsNone "#pin-btn" - - test "cannot lock threads": - with session: - navigate(baseUrl) - - click "#new-thread-btn" - - sendKeys "#thread-title", "Locking" - sendkeys "#reply-textarea", "Cannot lock as an user" - - click "#create-thread-btn" - - checkIsNone "#lock-btn" - session.logout() proc anonymousTests(session: Session, baseUrl: string) = + suite "anonymous user tests": with session: navigate baseUrl @@ -185,70 +161,6 @@ proc adminTests(session: Session, baseUrl: string) = # Make sure the forum post is gone checkIsNone adminTitleStr, LinkTextSelector - test "can pin a thread": - with session: - click "#new-thread-btn" - sendKeys "#thread-title", "Pinned post" - sendKeys "#reply-textarea", "A pinned post" - click "#create-thread-btn" - - navigate(baseUrl) - click "#new-thread-btn" - sendKeys "#thread-title", "Normal post" - sendKeys "#reply-textarea", "A normal post" - click "#create-thread-btn" - - navigate(baseUrl) - click "Pinned post", LinkTextSelector - click "#pin-btn" - checkText "#pin-btn", "Unpin Thread" - - navigate(baseUrl) - - # Make sure pin exists - ensureExists "#threads-list .thread-1 .thread-title i" - - checkText "#threads-list .thread-1 .thread-title a", "Pinned post" - checkText "#threads-list .thread-2 .thread-title a", "Normal post" - - test "can unpin a thread": - with session: - click "Pinned post", LinkTextSelector - click "#pin-btn" - checkText "#pin-btn", "Pin Thread" - - navigate(baseUrl) - - checkIsNone "#threads-list .thread-2 .thread-title i" - - checkText "#threads-list .thread-1 .thread-title a", "Normal post" - checkText "#threads-list .thread-2 .thread-title a", "Pinned post" - - test "can lock a thread": - with session: - click "Locking", LinkTextSelector - click "#lock-btn" - - ensureExists "#thread-title i.fas.fa-lock.fa-xs" - - test "locked thread appears on frontpage": - with session: - click "#new-thread-btn" - sendKeys "#thread-title", "A new locked thread" - sendKeys "#reply-textarea", "This thread should appear locked on the frontpage" - click "#create-thread-btn" - click "#lock-btn" - - navigate(baseUrl) - ensureExists "#threads-list .thread-1 .thread-title i.fas.fa-lock.fa-xs" - - test "can unlock a thread": - with session: - click "Locking", LinkTextSelector - click "#lock-btn" - - checkIsNone "#thread-title i.fas.fa-lock.fa-xs" - session.logout() proc test*(session: Session, baseUrl: string) =