Merge branch 'master' into add_categories

This commit is contained in:
Joey Yakimowich-Payne 2020-02-14 09:48:08 -07:00
commit fac828b1e9
13 changed files with 72 additions and 51 deletions

View file

@ -9,7 +9,7 @@ cache:
- "$HOME/.choosenim" - "$HOME/.choosenim"
addons: addons:
firefox: "60.0.1" firefox: "73.0"
before_install: before_install:
- sudo apt-get -qq update - sudo apt-get -qq update
@ -26,13 +26,13 @@ before_install:
- sudo make -j5 install - sudo make -j5 install
- cd .. - cd ..
- wget https://github.com/mozilla/geckodriver/releases/download/v0.20.1/geckodriver-v0.20.1-linux64.tar.gz - wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
- mkdir geckodriver - mkdir geckodriver
- tar -xzf geckodriver-v0.20.1-linux64.tar.gz -C geckodriver - tar -xzf geckodriver-v0.26.0-linux64.tar.gz -C geckodriver
- export PATH=$PATH:$PWD/geckodriver - export PATH=$PATH:$PWD/geckodriver
install: install:
- export CHOOSENIM_CHOOSE_VERSION="#f92d61b1f4e193bd" - export CHOOSENIM_CHOOSE_VERSION="stable"
- | - |
curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh
sh init.sh -y sh init.sh -y
@ -41,4 +41,5 @@ install:
script: script:
- export MOZ_HEADLESS=1 - export MOZ_HEADLESS=1
- nimble -y test - nimble -y install
- nimble -y test

View file

@ -1,5 +1,5 @@
# Package # Package
version = "2.1.0" version = "2.0.2"
author = "Dominik Picheta" author = "Dominik Picheta"
description = "The Nim forum" description = "The Nim forum"
license = "MIT" license = "MIT"
@ -12,16 +12,16 @@ skipExt = @["nim"]
# Dependencies # Dependencies
requires "nim >= 0.18.1" requires "nim >= 1.0.6"
requires "jester#22f8240" requires "jester#d8a03aa"
requires "bcrypt#head" requires "bcrypt#head"
requires "hmac#9c61ebe2fd134cf97" requires "hmac#9c61ebe2fd134cf97"
requires "recaptcha 1.0.2" requires "recaptcha#d06488e"
requires "sass#649e0701fa5c" requires "sass#649e0701fa5c"
requires "karax#c8c7b13" requires "karax#f6bda9a"
requires "webdriver#20f3c1b" requires "webdriver#c2fee57"
# Tasks # Tasks
@ -36,7 +36,7 @@ task frontend, "Builds the necessary JS frontend (with CSS)":
exec "nimble c -r src/buildcss" exec "nimble c -r src/buildcss"
exec "nimble js -d:release src/frontend/forum.nim" exec "nimble js -d:release src/frontend/forum.nim"
mkDir "public/js" mkDir "public/js"
cpFile "src/frontend/nimcache/forum.js", "public/js/forum.js" cpFile "src/frontend/forum.js", "public/js/forum.js"
task minify, "Minifies the JS using Google's closure compiler": task minify, "Minifies the JS using Google's closure compiler":
exec "closure-compiler public/js/forum.js --js_output_file public/js/forum.js.opt" exec "closure-compiler public/js/forum.js --js_output_file public/js/forum.js.opt"

View file

@ -269,6 +269,13 @@ $threads-meta-color: #545d70;
} }
// Hide all the avatars but the first on small screens.
@media screen and (max-width: 600px) {
#threads-list a:not(:first-child) > .avatar {
display: none;
}
}
.posts, .about { .posts, .about {
@extend .grid-md; @extend .grid-md;
@extend .container; @extend .container;

View file

@ -100,7 +100,7 @@ You should then create a symlink to this file inside ``/etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/<forum.hostname.com> /etc/nginx/sites-enabled/<forum.hostname.com> ln -s /etc/nginx/sites-available/<forum.hostname.com> /etc/nginx/sites-enabled/<forum.hostname.com>
``` ```
Then restart nginx by running ``sudo systemctl restart nginx``. Then reload nginx configuration by running ``sudo nginx -s reload``.
### Supervisor ### Supervisor
@ -168,4 +168,4 @@ You should see something like this:
## Conclusion ## Conclusion
That should be all you need to get started. Your forum should now be accessible That should be all you need to get started. Your forum should now be accessible
via your hostname, assuming that it points to your VPS' IP address. via your hostname, assuming that it points to your VPS' IP address.

View file

@ -457,13 +457,21 @@ proc executeReply(c: TForumData, threadId: int, content: string,
if isLocked == "1": if isLocked == "1":
raise newForumError("Cannot reply to a locked thread.") raise newForumError("Cannot reply to a locked thread.")
let retID = insertID( var retID: int64
db,
crud(crCreate, "post", "author", "ip", "content", "thread", "replyingTo"), if replyingTo.isSome():
c.userId, c.req.ip, content, $threadId, retID = insertID(
if replyingTo.isSome(): $replyingTo.get() db,
else: "" crud(crCreate, "post", "author", "ip", "content", "thread", "replyingTo"),
) c.userId, c.req.ip, content, $threadId, $replyingTo.get()
)
else:
retID = insertID(
db,
crud(crCreate, "post", "author", "ip", "content", "thread"),
c.userId, c.req.ip, content, $threadId
)
discard tryExec( discard tryExec(
db, db,
crud(crCreate, "post_fts", "id", "content"), crud(crCreate, "post_fts", "id", "content"),
@ -621,7 +629,7 @@ proc executeRegister(c: TForumData, name, pass, antibot, userIp,
raise newForumError("Invalid username", @["username"]) raise newForumError("Invalid username", @["username"])
if getValue( if getValue(
db, db,
sql"select name from person where name = ? and isDeleted = 0", sql"select name from person where name = ? collate nocase and isDeleted = 0",
name name
).len > 0: ).len > 0:
raise newForumError("Username already exists", @["username"]) raise newForumError("Username already exists", @["username"])
@ -1123,7 +1131,8 @@ routes:
except EParseError: except EParseError:
let err = PostError( let err = PostError(
errorFields: @[], errorFields: @[],
message: getCurrentExceptionMsg() message: "Message needs to be valid RST! Error: " &
getCurrentExceptionMsg()
) )
resp Http400, $(%err), "application/json" resp Http400, $(%err), "application/json"

View file

@ -90,4 +90,4 @@ when defined(js):
state.error = some(PostError( state.error = some(PostError(
errorFields: @[], errorFields: @[],
message: "Unknown error occurred." message: "Unknown error occurred."
)) ))

View file

@ -25,7 +25,7 @@ proc getInt64*(s: string, default = 0): int64 =
when defined(js): when defined(js):
include karax/prelude include karax/prelude
import karax / [kdom] import karax / [kdom, kajax]
from dom import nil from dom import nil
@ -87,16 +87,10 @@ when defined(js):
navigateTo(url) navigateTo(url)
type
FormData* = ref object
proc newFormData*(): FormData
{.importcpp: "new FormData()", constructor.}
proc newFormData*(form: dom.Element): FormData proc newFormData*(form: dom.Element): FormData
{.importcpp: "new FormData(@)", constructor.} {.importcpp: "new FormData(@)", constructor.}
proc get*(form: FormData, key: cstring): cstring proc get*(form: FormData, key: cstring): cstring
{.importcpp: "#.get(@)".} {.importcpp: "#.get(@)".}
proc append*(form: FormData, key, val: cstring)
{.importcpp: "#.append(@)".}
proc renderProfileUrl*(username: string): string = proc renderProfileUrl*(username: string): string =
makeUri(fmt"/profile/{username}") makeUri(fmt"/profile/{username}")
@ -120,4 +114,4 @@ when defined(js):
inc(i) # Skip = inc(i) # Skip =
i += query.parseUntil(val, '&', i) i += query.parseUntil(val, '&', i)
inc(i) # Skip & inc(i) # Skip &
result[$decodeUri(key)] = $decodeUri(val) result[$decodeUri(key)] = $decodeUri(val)

View file

@ -1,6 +1,6 @@
when defined(js): when defined(js):
import sugar, httpcore, options, json import sugar, httpcore, options, json
import dom except Event import dom except Event, KeyboardEvent
include karax/prelude include karax/prelude
import karax / [kajax, kdom] import karax / [kajax, kdom]
@ -93,4 +93,4 @@ when defined(js):
(state.onSignUp(); state.shown = false)): (state.onSignUp(); state.shown = false)):
text "Create account" text "Create account"
render(state.resetPasswordModal, recaptchaSiteKey) render(state.resetPasswordModal, recaptchaSiteKey)

View file

@ -26,7 +26,7 @@ when defined(js):
proc performScroll() = proc performScroll() =
let replyBox = dom.document.getElementById("reply-box") let replyBox = dom.document.getElementById("reply-box")
replyBox.scrollIntoView(false) replyBox.scrollIntoView()
proc show*(state: ReplyBox) = proc show*(state: ReplyBox) =
# Scroll to the reply box. # Scroll to the reply box.

View file

@ -1,6 +1,6 @@
when defined(js): when defined(js):
import sugar, httpcore, options, json import sugar, httpcore, options, json
import dom except Event import dom except Event, KeyboardEvent
include karax/prelude include karax/prelude
import karax / [kajax, kdom] import karax / [kajax, kdom]
@ -152,4 +152,4 @@ when defined(js):
), ),
`type`="button", `type`="button",
onClick=(ev: Event, n: VNode) => onClick(ev, n, state)): onClick=(ev: Event, n: VNode) => onClick(ev, n, state)):
text "Reset password" text "Reset password"

View file

@ -10,11 +10,6 @@ let
import frontend/[karaxutils, error] import frontend/[karaxutils, error]
export parseInt export parseInt
proc `%`*[T](opt: Option[T]): JsonNode =
## Generic constructor for JSON data. Creates a new ``JNull JsonNode``
## if ``opt`` is empty, otherwise it delegates to the underlying value.
if opt.isSome: %opt.get else: newJNull()
type type
Config* = object Config* = object
smtpAddress*: string smtpAddress*: string
@ -56,7 +51,7 @@ proc loadConfig*(filename = getCurrentDir() / "forum.json"): Config =
smtpPassword: "", mlistAddress: "") smtpPassword: "", mlistAddress: "")
let root = parseFile(filename) let root = parseFile(filename)
result.smtpAddress = root{"smtpAddress"}.getStr("") result.smtpAddress = root{"smtpAddress"}.getStr("")
result.smtpPort = root{"smtpPort"}.getNum(25).int result.smtpPort = root{"smtpPort"}.getInt(25)
result.smtpUser = root{"smtpUser"}.getStr("") result.smtpUser = root{"smtpUser"}.getStr("")
result.smtpPassword = root{"smtpPassword"}.getStr("") result.smtpPassword = root{"smtpPassword"}.getStr("")
result.smtpFromAddr = root{"smtpFromAddr"}.getStr("") result.smtpFromAddr = root{"smtpFromAddr"}.getStr("")
@ -69,7 +64,7 @@ proc loadConfig*(filename = getCurrentDir() / "forum.json"): Config =
result.name = root["name"].getStr() result.name = root["name"].getStr()
result.title = root["title"].getStr() result.title = root["title"].getStr()
result.ga = root{"ga"}.getStr() result.ga = root{"ga"}.getStr()
result.port = root{"port"}.getNum(5000).int result.port = root{"port"}.getInt(5000)
proc processGT(n: XmlNode, tag: string): (int, XmlNode, string) = proc processGT(n: XmlNode, tag: string): (int, XmlNode, string) =
result = (0, newElement(tag), tag) result = (0, newElement(tag), tag)

View file

@ -115,7 +115,7 @@ proc login*(session: Session, user, password: string) =
checkText "#profile-btn #profile-name", user checkText "#profile-btn #profile-name", user
click "#profile-btn" click "#profile-btn"
proc register*(session: Session, user, password: string) = proc register*(session: Session, user, password: string, verify = true) =
with session: with session:
click "#signup-btn" click "#signup-btn"
@ -130,11 +130,13 @@ proc register*(session: Session, user, password: string) =
click "#signup-modal .create-account-btn" click "#signup-modal .create-account-btn"
wait() wait()
# Verify that the user menu has been initialised properly. if verify:
click "#profile-btn" with session:
checkText "#profile-btn #profile-name", user # Verify that the user menu has been initialised properly.
# close menu click "#profile-btn"
click "#profile-btn" checkText "#profile-btn #profile-name", user
# close menu
click "#profile-btn"
proc createThread*(session: Session, title, content: string) = proc createThread*(session: Session, title, content: string) =
with session: with session:

View file

@ -30,5 +30,18 @@ proc test*(session: Session, baseUrl: string) =
test "can register": test "can register":
with session: with session:
register("test", "test") register("test", "test")
logout()
session.logout() test "can't register same username with different case":
with session:
register "test1", "test1", verify = false
logout()
navigate baseUrl
wait()
register "TEst1", "test1", verify = false
ensureExists "#signup-form .has-error"
navigate baseUrl
wait()