From 4c0ef19344aec12f60af66b38a62ca5b3b01ce6d Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 20 May 2018 13:54:51 +0100 Subject: [PATCH] Removes a lot of unused code from backend. --- src/forms.tmpl | 584 ---------------------------------------- src/forum.nim | 718 ++----------------------------------------------- src/main.tmpl | 174 +----------- 3 files changed, 26 insertions(+), 1450 deletions(-) delete mode 100644 src/forms.tmpl diff --git a/src/forms.tmpl b/src/forms.tmpl deleted file mode 100644 index f50fb25..0000000 --- a/src/forms.tmpl +++ /dev/null @@ -1,584 +0,0 @@ -#? stdtmpl | standard -# -#template `!`(idx: untyped): untyped = -# row[idx] -#end template -# -# -#proc genThreadsList(c: TForumData, count: var int): string = -# const queryModAdmin = sql"""select id, name, views, modified from thread -# where id in (select thread from post where author in -# (select id from person where status not in ('Spammer') or id = ?)) -# order by modified desc limit ?, ?""" -# const query = sql"""select id, name, views, modified from thread -# where id in (select thread from post where author in -# (select id from person where status not in ('Moderated', 'Spammer') or id = ?)) -# order by modified desc limit ?, ?""" -# const threadId = 0 -# const name = 1 -# const views = 2 -# -# result = "" -# count = 0 -
-
-
- Topic - - - -
-
-
Users
-
Details
-
-
- Activity - - - -
-
-
-
-# for row in rows(db, if c.rank >= Moderator: queryModAdmin else: query, -# c.userId, $((c.pageNum-1) * ThreadsPerPage), $ThreadsPerPage): -# inc(count) -
-
-
- ${xmlEncode(!name)} - ${genPagenumLocalNav(c, (!threadid).parseInt)} -
-
- - #let users = getAllRows(db, - # sql("select distinct name, email from person where id in " & - # "(select author from post where thread = ?)"), !threadId) -
-
- #for i in 0 .. min(6, users.len-1): - - #end for -
-
- - #let latestReplyAuthor = getValue(db, sql("select name from person where id = " & - # "(select author from post where id = " & - # "(select max(id) from post where thread = ?))"), !threadId) - - #let replyProfileUrl = c.req.makeUri("profile/", false) & - # xmlEncode(latestReplyAuthor) - -# let posts = getValue(db, sql"select count(*) from post where thread = ?", !threadId) -
-
${xmlEncode(!views)}
-
$posts
-
- - #let latestReplyDate = getValue(db, sql("SELECT strftime('%s', " & - # "(select creation from post where id = (select max(id) from post where thread = ?)))"), !threadId) - #let timeStr = formatTimestamp(latestReplyDate.parseInt()) -
-
- $latestReplyAuthor replied $timeStr -
-
-
-# end for -
- -#end proc -# -# -#proc genPostPreview(c: TForumData, -# title, content, author, date: string): string = -# result = "" - -
-
-
-
- #let profileUrl = c.req.makeUri("profile/", false) & xmlEncode(author) - ${xmlEncode(author)} -
-
-
-
- #try: - ${content.rstToHtml} - #except EParseError: - # c.errorMsg = getCurrentExceptionMsg() - #end - ${xmlEncode(date)} -
-
-
- -
-#end proc -# -# -#proc genPostsList(c: TForumData, threadId: string, count: var int): string = -# let query = sql("""select p.id, u.name, p.header, p.content, p.creation, p.author, u.email from post p, -# person u -# where u.id = p.author and p.thread = ? and $# -# and (u.status <> 'Spammer' or p.author = ?) -# order by p.id limit ?, ?""" % -# (if c.rank >= Moderator: "(1 or u.id = ?)" else: "(u.status <> 'Moderated' or p.author = ?)")) -# const postId = 0 -# const userName = 1 -# const postHeader = 2 -# const postContent = 3 -# const postCreation = 4 -# const postAuthor = 5 -# const userEmail = 6 -# result = "" -# count = 0 -# let posts = getAllRows(db, query, threadId, c.userId, c.userId, $((c.pageNum-1) * PostsPerPage), $PostsPerPage) -# if posts.len < 1: return "" -# end if -
-
-
- forum index > - ${posts[0][postHeader]} -
-
-
-
-# for row in posts: -# inc(count) - -
-
-
- #let profileUrl = c.req.makeUri("profile/", false) & xmlEncode(!userName) -
${genGravatar(!userEmail)}
- ${xmlEncode(!userName)} - #if c.userId == !postAuthor and c.currentPost.subject.len == 0: -
Edit post - #elif c.rank >= Moderator and c.currentPost.subject.len == 0: -
Edit post - #end if -
-
-
-
- #try: - ${(!postContent).rstToHtml} - #except EParseError: - # c.errorMsg = getCurrentExceptionMsg() - #end - ${xmlEncode(!postCreation)} -
-
-
-# end for - -
-#end proc -# -#proc genMarkHelp(): string -#end proc -#proc genFormPost(c: TForumData, action: string, -# topText, title, content: string, isEdit: bool): string = -# result = "" -
- -
-
-
-
- forum index > - $topText -
-
-
-
- #if action == "doreply": - ${hiddenField(c, "subject", title)} - #else: - ${fieldValid(c, "subject", "Subject:")} - ${textWidget(c, "subject", title, maxlength=100)} -
- #end if - ${fieldValid(c, "content", "Content:")}
- ${textAreaWidget(c, "content", content)}
- ${formSession(c, action)} - - # if isEdit: - Delete Post
- # end if - #if c.errorMsg != "": -
- $c.errorMsg -
- #end if -
- - - - - ${genMarkHelp()} -
-
-#end proc -# -# -#proc genFormRegister(c: TForumData): string = -# result = "" -
-
-
- forum index > - Register -
-
-
-
- - - - - - - - - - - - - - - - -
${fieldValid(c, "name", "Username:")}${textWidget(c, "name", reuseText, maxlength=20)}
${fieldValid(c, "new_password", "Password:")}
${fieldValid(c, "email", "E-Mail:")}${textWidget(c, "email", reuseText, maxlength=300)}
${fieldValid(c, "g-recaptcha-response", "Captcha:")}${captcha.render(includeNoScript=true)}
- #if c.errorMsg != "": -
- $c.errorMsg -
- #end if - -
-#end proc -# -#proc genFormSetRank(c: TForumData; ui: TUserInfo): string = -# result = "" -
- - - - - - - - - -
Reason${textWidget(c, "reason", ui.ban, maxlength=100)}
Rank
- -
-#end proc -# -#proc genFormLogin(c: TForumData): string = -# result = "" -# if not c.loggedIn: -
- - - -
Username: -
Password: -
- -
- $c.loginErrorMsg -# else: - You're already logged in! -# end if -#end proc -# -# -#proc genListOnline(c: TForumData, stats: TForumStats): string = -# result = "" -# var active: seq[string] = @[] -# for i in stats.activeUsers: -# active.add(i.nick) -# end for -# let profileUrl = c.req.makeUri("profile/", false) & -# xmlEncode(stats.newestMember.nick) - - ${stats.activeUsers.len} of ${stats.totalUsers} users online  |  - ${stats.totalThreads} threads  |  ${stats.totalPosts} posts  |  - newest member: ${stats.newestMember.nick} -#end proc -# -# -# -# -#proc genSearchResults(c: TForumData, -# results: iterator: db_sqlite.Row {.closure, tags: [ReadDbEffect].}, -# count: var int): string = -# const threadId = 0 -# const threadName = 1 -# const postId = 2 -# const postHeader = 3 -# const postContent = 4 -# const userName = 5 -# const postCreation = 6 -# const postAuthor = 7 -# const userEmail = 8 -# const what = 9 -# result = "" -# count = 0 -# var whCount: array[bool, int] -
-
-
- Search results for: ${xmlEncode(c.search.replace(""","\""))}. -
-
-
-
-# for row in results(): -# inc(count) -# let isThread = !what == "0" -# inc(whCount[isThread]) -# let postUrl = c.genThreadUrl(!postId,"",!threadId,"") -# let threadUrl = c.genThreadUrl("","",!threadId) -# var headersDiffer = false -
-
-
- #let profileUrl = c.req.makeUri("profile/", false) & xmlEncode(!userName) - - - #if c.userId == !postAuthor and c.currentPost.subject.len == 0: -
Edit post - #elif c.rank >= Moderator and c.currentPost.subject.len == 0: -
Edit post - #end if -
-
-
-
- #if !postHeader != "": - - #end if - #if not isThread: - #try: - ${(!postContent).rstToHtml} - #except EParseError: - # c.errorMsg = getCurrentExceptionMsg() - ${xmlEncode(!postContent)} - #end - #end if - ${xmlEncode(!postCreation)} -
-
-
-# end for -
-# if c.pageNum > 1: -
- - -
-# end if -# if whCount[true] == ThreadsPerPage or whCount[false] == ThreadsPerPage: -
- - -
-# end if -#end proc -# -# -#proc genFormResetPassword(c: TForumData): string = -# result = "" -
-
-
- forum index > - Reset Password -
-
-
-
- - - - - - - - - -
${fieldValid(c, "nick", "Your nickname:")}
${fieldValid(c, "g-recaptcha-response", "Captcha:")}${captcha.render(includeNoScript=true)}
- #if c.errorMsg != "": -
- $c.errorMsg -
- #end if - -
-#end proc -#proc genMarkHelp(): string = -#result = "" -
-

nimforum uses a slightly-customized version of - reStructuredText for formatting. See below for some basics, or check - this link for a more detailed help reference.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
you type: - you see: -
*italics*italics -
**bold**bold -
`nim! <http://nim-lang.org>`_nim! -
* item 1 -
* item 2 -
* item 3
-
    -
  • item 1
  • -
  • item 2
  • -
  • item 3
  • -
-
> quoted text -
quoted text
-
The forum supports the Github Markdown syntax -
for code listings: -
-
```nim -
if 1 * 2 < 3: -
  echo "hello, world!" -
``` -
-
The forum supports the Github Markdown syntax -
for code listings: -
-
-if 1*2 < 3:
-  echo "hello, world!"
-                    
-
A horizontal rule can be created
----
but it needs text after it
A horizontal rule can be created
but it needs text after it
-
-#end proc diff --git a/src/forum.nim b/src/forum.nim index 582b4a9..a98c04d 100644 --- a/src/forum.nim +++ b/src/forum.nim @@ -63,25 +63,6 @@ type noPagenumumNav: bool config: Config - TStyledButton = tuple[text: string, link: string] - - TForumStats = object - totalUsers: int - totalPosts: int - totalThreads: int - newestMember: tuple[nick: string, id: int] - activeUsers: seq[tuple[nick: string, id: int]] - - TUserInfo = object - nick: string - posts: int - threads: int - lastOnline: int - email: string - ban: string - rank: Rank - lastIp: string - ForumError = object of Exception data: PostError @@ -121,36 +102,6 @@ proc loggedIn(c: TForumData): bool = # --------------- HTML widgets ------------------------------------------------ -# for widgets "" means the empty string as usual; should the old value be -# used again, pass `reuseText` instead: -const - reuseText = "\1" - -proc textWidget(c: TForumData, name, defaultText: string, - maxlength = 30, size = -1): string = - let x = if defaultText != reuseText: defaultText - else: xmlEncode(c.req.params.getOrDefault(name)) - return """""" % [ - name, $maxlength, x, if size != -1: "size=\"" & $size & "\"" else: ""] - -proc hiddenField(c: TForumData, name, defaultText: string): string = - let x = xmlencode( - if defaultText != reuseText: defaultText - else: c.req.params.getOrDefault(name) - ) - return """""" % [name, x] - -proc textAreaWidget(c: TForumData, name, defaultText: string): string = - let x = if defaultText != reuseText: defaultText - else: xmlEncode(c.req.params.getOrDefault(name)) - return """""" % [ - name, x] - -proc fieldValid(c: TForumData, name, text: string): string = - if name == c.invalidField: - result = """$1""" % text - else: - result = text proc genThreadUrl(c: TForumData, postId = "", action = "", threadid = "", pageNum = ""): string = result = "/t/" & (if threadid == "": $c.threadId else: threadid) @@ -164,84 +115,17 @@ proc genThreadUrl(c: TForumData, postId = "", action = "", threadid = "", pageNu result.add("#" & postId) result = c.req.makeUri(result, absolute = false) -proc formSession(c: TForumData, nextAction: string): string = - return """ - """ % [ - $c.threadId, $c.postid] - -proc urlButton(c: TForumData, text, url: string): string = - return ("""$2""") % [ - url, text] - -proc genButtons(c: TForumData, btns: seq[TStyledButton]): string = - if btns.len == 1: - var anchor = "" - - result = ("""$2""") % [ - btns[0].link, btns[0].text, anchor] - else: - result = "" - for i, btn in pairs(btns): - var anchor = "" - - var class = "" - if i == 0: class = "left " - elif i == btns.len()-1: class = "right " - else: class = "middle " - result.add(("""$2""") % [ - btns[i].link, btns[i].text, class, anchor]) - -proc toInterval(diff: int64): TimeInterval = - var remaining = diff - let years = remaining div 31536000 - remaining -= years * 31536000 - let months = remaining div 2592000 - remaining -= months * 2592000 - let days = remaining div 86400 - remaining -= days * 86400 - let hours = remaining div 3600 - remaining -= hours * 3600 - let minutes = remaining div 60 - remaining -= minutes * 60 - result = initInterval(remaining.int, minutes.int, hours.int, days.int, - months.int, years.int) - -proc formatTimestamp(t: int): string = - let t2 = fromUnix(t) - let now = getTime() - # let diff = (now - t2).toInterval() - # if diff.years > 0: - # return getGMTime(t2).format("MMMM d',' yyyy") - # elif diff.months > 0: - # return $diff.months & (if diff.months > 1: " months ago" else: " month ago") - # elif diff.days > 0: - # return $diff.days & (if diff.days > 1: " days ago" else: " day ago") - # elif diff.hours > 0: - # return $diff.hours & (if diff.hours > 1: " hours ago" else: " hour ago") - # elif diff.minutes > 0: - # return $diff.minutes & - # (if diff.minutes > 1: " minutes ago" else: " minute ago") - # else: - return "just now" proc getGravatarUrl(email: string, size = 80): string = let emailMD5 = email.toLowerAscii.toMD5 return ("https://www.gravatar.com/avatar/" & $emailMD5 & "?s=" & $size & "&d=identicon") -proc genGravatar(email: string, size: int = 80): string = - result = "" % - [$size, $size, getGravatarUrl(email, size)] - # ----------------------------------------------------------------------------- template `||`(x: untyped): untyped = (if not isNil(x): x else: "") -proc validThreadId(c: TForumData): bool = - result = getValue(db, sql"select id from thread where id = ?", - $c.threadId).len > 0 - proc setError(c: TForumData, field, msg: string): bool {.inline.} = c.invalidField = field c.errorMsg = "Error: " & msg @@ -290,16 +174,6 @@ proc logout(c: TForumData) = c.userpass = "" exec(db, query, c.req.ip, c.req.cookies["sid"]) -proc getBanErrorMsg(banValue: string; rank: Rank): string = - if banValue.len > 0: - return "You have been banned: " & banValue - case rank - of Spammer, Troll, Banned: return "You have been banned." - of EmailUnconfirmed: - return "You need to confirm your email first." - of Moderated, Rank.User, Moderator, Admin: - return "" - proc checkLoggedIn(c: TForumData) = if not c.req.cookies.hasKey("sid"): return let pass = c.req.cookies["sid"] @@ -325,15 +199,9 @@ proc checkLoggedIn(c: TForumData) = else: echo("SID not found in sessions. Assuming logged out.") -proc incrementViews(c: TForumData) = +proc incrementViews(c: TForumData, threadId: int) = const query = sql"update thread set views = views + 1 where id = ?" - exec(db, query, $c.threadId) - -proc isPreview(c: TForumData): bool = - result = c.req.params.hasKey("previewBtn") - -proc isDelete(c: TForumData): bool = - result = c.req.params.hasKey("delete") + exec(db, query, threadId) proc validateRst(c: TForumData, content: string): bool = result = true @@ -370,131 +238,6 @@ proc crud(c: TCrud, table: string, data: varargs[string]): SqlQuery = of crDelete: result = sql("delete from " & table & " where id = ?") -template retrSubject(c: untyped) = - if not c.req.params.hasKey("subject"): - raise newException(ForumError, "Subject empty") - let subject {.inject.} = c.req.params["subject"] - if subject.strip.len < 3: - return setError(c, "subject", "Subject not long enough") - -template retrContent(c: untyped) = - if not c.req.params.hasKey("content"): - raise newException(ForumError, "Content empty") - let content {.inject.} = c.req.params["content"] - if content.strip.len < 2: - return setError(c, "content", "Content not long enough") - - if not validateRst(c, content): return false - -template retrPost(c: untyped) = - retrSubject(c) - retrContent(c) - -template checkLogin(c: untyped) = - if not loggedIn(c): return setError(c, "", "User is not logged in") - -template checkOwnership(c, postId: untyped) = - if c.rank < Moderator: - let x = getValue(db, sql"select author from post where id = ?", - postId) - if x != c.userId: - return setError(c, "", "You are not the owner of this post") - -template setPreviewData(c: untyped) {.dirty.} = - c.currentPost.subject = subject - c.currentPost.content = content - -template writeToDb(c, cr, setPostId: untyped) = - # insert a comment in the DB - let retID = insertID(db, crud(cr, "post", "author", "ip", "header", "content", "thread"), - c.userId, c.req.ip, subject, content, $c.threadId, "") - discard tryExec(db, crud(cr, "post_fts", "id", "header", "content"), - retID.int, subject, content) - if setPostId: - c.postId = retID.int - -proc updateThreads(c: TForumData): int = - ## Removes threads if they have no posts, or changes their modified field - ## if they still contain posts. - const query = - sql"delete from thread where id not in (select thread from post)" - result = execAffectedRows(db, query).int - if result > 0: - discard tryExec(db, sql"delete from thread_fts where id not in (select thread from post)") - else: - # Update corresponding thread's modified field. - let getModifiedSql = "(select creation from post where post.thread = ?" & - " order by creation desc limit 1)" - let updateSql = sql("update thread set modified=" & getModifiedSql & - " where id = ?") - if not tryExec(db, updateSql, $c.threadId, $c.threadId): - result = -1 - discard setError(c, "", "database error") - -proc edit(c: TForumData, postId: int): bool = - checkLogin(c) - if c.isPreview: - retrPost(c) - setPreviewData(c) - elif c.isDelete: - checkOwnership(c, $postId) - if not tryExec(db, crud(crDelete, "post"), $postId): - return setError(c, "", "database error") - discard tryExec(db, crud(crDelete, "post_fts"), $postId) - result = true - # delete corresponding thread: - let updateResult = updateThreads(c) - if updateResult > 0: - # whole thread has been deleted, so: - c.threadId = unselectedThread - elif updateResult < 0: - # error occurred - return false - else: - checkOwnership(c, $postId) - retrPost(c) - exec(db, crud(crUpdate, "post", "header", "content"), - subject, content, $postId) - exec(db, crud(crUpdate, "post_fts", "header", "content"), - subject, content, $postId) - # Check if post is the first post of the thread. - let rows = db.getAllRows(sql("select id, thread, creation from post " & - "where thread = ? order by creation asc"), $c.threadId) - if rows[0][0] == $postId: - exec(db, crud(crUpdate, "thread", "name"), subject, $c.threadId) - result = true - -proc gatherUserInfo(c: TForumData, nick: string, ui: var TUserInfo): bool -proc spamCheck(c: TForumData, subject, content: string): bool = - # Check current user's info - var ui: TUserInfo - if gatherUserInfo(c, c.userName, ui): - if ui.posts > 1: return - - # Strip all punctuation - var subjAlphabet = "" - for i in subject: - if i in Letters: - subjAlphabet.add(i) - case i - of '!': - subjAlphabet.add("i") - else: discard - var contentAlphabet = "" - for i in content: - if i in Letters: - contentAlphabet.add(i) - case i - of '!': - subjAlphabet.add("i") - else: discard - - for word in ["appliance", "kitchen", "cheap", "sale", "relocating", - "packers", "lenders", "fifa", "coins"]: - if word in subjAlphabet.toLowerAscii() or - word in contentAlphabet.toLowerAscii(): - return true - proc rateLimitCheck(c: TForumData): bool = const query40 = sql("SELECT count(*) FROM post where author = ? and " & @@ -514,56 +257,6 @@ proc rateLimitCheck(c: TForumData): bool = if last300s > 6: return true return false -proc makeThreadURL(c: TForumData): string = - c.req.makeUri("/t/" & $c.threadId) - -template postChecks() {.dirty.} = - if spamCheck(c, subject, content): - echo("[WARNING] Found spam: ", subject) - return true - if rateLimitCheck(c): - return setError(c, "subject", "You're posting too fast.") - -proc reply(c: TForumData): bool = - # reply to an existing thread - checkLogin(c) - retrPost(c) - if c.isPreview: - setPreviewData(c) - else: - postChecks() - writeToDb(c, crCreate, true) - - exec(db, sql"update thread set modified = DATETIME('now') where id = ?", - $c.threadId) - if c.rank >= Rank.User: - asyncCheck sendMailToMailingList(c.config, c.username, c.email, - subject, content, threadId=c.threadId, postId=c.postID, is_reply=true, - threadUrl=c.makeThreadURL()) - result = true - -proc newThread(c: TForumData): bool = - # create new conversation thread (permanent or transient) - const query = sql"insert into thread(name, views, modified) values (?, 0, DATETIME('now'))" - checkLogin(c) - retrPost(c) - if c.isPreview: - setPreviewData(c) - c.threadID = transientThread - else: - postChecks() - c.threadID = tryInsertID(db, query, c.req.params["subject"]).int - if c.threadID < 0: return setError(c, "subject", "Subject already exists") - discard tryExec(db, crud(crCreate, "thread_fts", "id", "name"), - c.threadID, c.req.params["subject"]) - writeToDb(c, crCreate, false) - discard tryExec(db, sql"insert into post_fts(post_fts) values('optimize')") - discard tryExec(db, sql"insert into post_fts(thread_fts) values('optimize')") - if c.rank >= Rank.User: - asyncCheck sendMailToMailingList(c.config, c.username, c.email, - subject, content, threadId=c.threadID, postId=c.postID, is_reply=false, - threadUrl=c.makeThreadURL()) - result = true proc verifyIdentHash(c: TForumData, name, epoch, ident: string): bool = const query = @@ -576,284 +269,6 @@ proc verifyIdentHash(c: TForumData, name, epoch, ident: string): bool = if row[2].parseInt > (epoch.parseInt + 60): return false result = newIdent == ident -proc deleteAll(c: TForumData, nick: string): bool = - const query = - sql("delete from post where author = (select id from person where name = ?)") - result = tryExec(db, query, nick) - result = result and updateThreads(c) >= 0 - -proc setStatus(c: TForumData, nick: string, status: Rank; - reason: string): bool = - const query = - sql("update person set status = ?, ban = ? where name = ?") - result = tryExec(db, query, $status, reason, nick) - when false: - # for now we filter Spammers in forms.tmpl, so that a moderator - # cannot accidentically delete all of a user's posts. We go even - # further than that and show spammers their own spam postings. - if status == Spammer and result: - result = deleteAll(c, nick) - -proc setPassword(c: TForumData, nick, pass: string): bool = - const query = - sql("update person set password = ?, salt = ? where name = ?") - var salt = makeSalt() - result = tryExec(db, query, makePassword(pass, salt), salt, nick) - -proc hasReplyBtn(c: TForumData): bool = - result = c.req.pathInfo != "/donewthread" and c.req.pathInfo != "/doreply" - result = result and c.req.params.getOrDefault("action") notin ["reply", "edit"] - # If the user is not logged in and there are no page numbers then we shouldn't - # generate the div. - let pages = ceil(c.totalPosts / PostsPerPage).int - result = result and (pages > 1 or c.loggedIn) - return c.threadId >= 0 and result - -proc getStats(c: TForumData, simple: bool): TForumStats = - const totalUsersQuery = - sql"select count(*) from person" - result.totalUsers = getValue(db, totalUsersQuery).parseInt - const totalPostsQuery = - sql"select count(*) from post" - result.totalPosts = getValue(db, totalPostsQuery).parseInt - const totalThreadsQuery = - sql"select count(*) from thread" - result.totalThreads = getValue(db, totalThreadsQuery).parseInt - if not simple: - var newestMemberCreation = 0 - result.activeUsers = @[] - result.newestMember = ("", -1) - const getUsersQuery = - sql"select id, name, strftime('%s', lastOnline), strftime('%s', creation) from person" - for row in fastRows(db, getUsersQuery): - let secs = if row[3] == "": 0 else: row[3].parseint - when false: - let lastOnlineSeconds = getTime() - Time(secs) - if lastOnlineSeconds < (60 * 5): # 5 minutes - result.activeUsers.add((row[1], row[0].parseInt)) - if row[3].parseInt > newestMemberCreation: - result.newestMember = (row[1], row[0].parseInt) - newestMemberCreation = row[3].parseInt - -proc genPagenumNav(c: TForumData, stats: TForumStats): string = - result = "" - var - firstUrl = "" - prevUrl = "" - totalPages = 0 - lastUrl = "" - nextUrl = "" - - if c.isThreadsList: - firstUrl = c.req.makeUri("/") - prevUrl = c.req.makeUri(if c.pageNum == 1: "/" else: "/page/" & $(c.pageNum-1)) - totalPages = ceil(stats.totalThreads / ThreadsPerPage).int - lastUrl = c.req.makeUri("/page/" & $(totalPages)) - nextUrl = c.req.makeUri("/page/" & $(c.pageNum+1)) - else: - firstUrl = c.makeThreadURL() - if c.pageNum == 1: - prevUrl = firstUrl - else: - prevUrl = c.req.makeUri(firstUrl & "/" & $(c.pageNum-1)) - totalPages = ceil(c.totalPosts / PostsPerPage).int - lastUrl = c.req.makeUri(firstUrl & "/" & $(totalPages)) - nextUrl = c.req.makeUri(firstUrl & "/" & $(c.pageNum+1)) - - if totalPages <= 1: - return "" - - var firstTag = "" - var prevTag = "" - if c.pageNum == 1: - firstTag = span("<<") - prevTag = span("<••") - else: - firstTag = htmlgen.a(href=firstUrl, "<<") - prevTag = htmlgen.a(href=prevUrl, "<••") - prevTag.add(htmlgen.link(rel="previous", href=prevUrl)) - result.add(firstTag) - result.add(prevTag) - - # Numbers - var pages = "" # Tags - # cutting numbers to the left and to the right tp MaxPagesFromCurrent - let firstToShow = max(1, c.pageNum - MaxPagesFromCurrent) - let lastToShow = min(totalPages, c.pageNum + MaxPagesFromCurrent) - if firstToShow > 1: pages.add(span("...")) - for i in firstToShow .. lastToShow: - if i == c.pageNum: - pages.add(span($(i))) - else: - var pageUrl = "" - if c.isThreadsList: - pageUrl = c.req.makeUri("/page/" & $(i)) - else: - pageUrl = c.req.makeUri(firstUrl & "/" & $(i)) - - pages.add(htmlgen.a(href = pageUrl, $(i))) - if lastToShow < totalPages: pages.add(span("...")) - result.add(pages) - - # Right - var lastTag = "" - var nextTag = "" - if c.pageNum == totalPages: - lastTag = span(">>") - nextTag = span("••>") - else: - lastTag = htmlgen.a(href=lastUrl, ">>") - nextTag = htmlgen.a(href=nextUrl, "••>") - nextTag.add(htmlgen.link(rel="next",href=nextUrl)) - result.add(nextTag) - result.add(lastTag) - -proc gatherTotalPostsByID(c: TForumData, thrid: int): int = - ## Gets the total post count of a thread. - result = getValue(db, sql"select count(*) from post where thread = ?", $thrid).parseInt - -proc gatherTotalPosts(c: TForumData) = - if c.totalPosts > 0: return - # Gather some data. - const totalPostsQuery = - sql"select count(*) from post p, person u where u.id = p.author and p.thread = ?" - c.totalPosts = getValue(db, totalPostsQuery, $c.threadId).parseInt - -proc getPagesInThread(c: TForumData): int = - c.gatherTotalPosts() # Get total post count - result = ceil(c.totalPosts / PostsPerPage).int-1 - -proc getPagesInThreadByID(c: TForumData, thrid: int): int = - result = ceil(c.gatherTotalPostsByID(thrid) / PostsPerPage).int - -proc getThreadTitle(thrid: int, pageNum: int): string = - echo thrid - result = getValue(db, sql"select name from thread where id = ?", $thrid) - if pageNum notin {0,1}: - result.add(" - Page " & $pageNum) - -proc genPagenumLocalNav(c: TForumData, thrid: int): string = - result = "" - const maxPostPages = 6 # Maximum links to pages shown. - const hmpp = maxPostPages div 2 - # 1 2 3 ... 10 11 12 - var currentThrURL = "/t/" & $thrid & "/" - let totalPagesInThread = c.getPagesInThreadByID(thrid) - if totalPagesInThread <= 1: return - var i = 1 - while i <= totalPagesInThread: - result.add(htmlgen.a(href=c.req.makeUri(currentThrURL & $i), $i)) - if i == hmpp and totalPagesInThread-i > hmpp: - result.add(span("...")) - # skip to the last 3 - i = totalPagesInThread-(hmpp-1) - else: - inc(i) - - result = htmlgen.span(class = "pages", result) - -proc gatherUserInfo(c: TForumData, nick: string, ui: var TUserInfo): bool = - ui.nick = nick - const getUIDQuery = sql"select id from person where name = ?" - var uid = getValue(db, getUIDQuery, nick) - if uid == "": return false - result = true - const totalPostsQuery = - sql"select count(*) from post where author = ?" - ui.posts = getValue(db, totalPostsQuery, uid).parseInt - const totalThreadsQuery = - sql("select count(*) from thread where id in (select thread from post where" & - " author = ? and post.id in (select min(id) from post group by thread))") - - ui.threads = getValue(db, totalThreadsQuery, uid).parseInt - const lastOnlineQuery = - sql"""select strftime('%s', lastOnline), email, ban, status - from person where id = ?""" - let row = db.getRow(lastOnlineQuery, $uid) - ui.lastOnline = if row[0].len > 0: row[0].parseInt else: -1 - ui.email = row[1] - ui.ban = row[2] - ui.rank = parseEnum[Rank](row[3]) - - const lastIpQuery = sql"select `ip` from `session` where `userid` = ? order by `id` desc limit 1;" - let ipRow = db.getRow(lastIpQuery, $uid) - ui.lastIp = ipRow[0] - -include "forms.tmpl" -include "main.tmpl" - -proc genProfile(c: TForumData, ui: TUserInfo): string = - result = "" - - result.add(htmlgen.`div`(id = "talk-head", - htmlgen.`div`(class="info-post", - htmlgen.`div`( - htmlgen.a(href = c.req.makeUri("/"), - span(style = "font-weight: bold;", "forum index") - ), - " > " & ui.nick & "'s profile" - ) - ) - ) - ) - result.add(htmlgen.`div`(id = "avatar", genGravatar(ui.email, 250))) - let t2 = if ui.lastOnline != -1: getGMTime(fromUnix(ui.lastOnline)) - else: getGMTime(getTime()) - - result.add(htmlgen.`div`(id = "info", - htmlgen.table( - tr( - th("Nickname"), - td(ui.nick) - ), - tr( - th("Threads"), - td($ui.threads) - ), - tr( - th("Posts"), - td($ui.posts) - ), - tr( - th("Last Online"), - td(if ui.lastOnline != -1: t2.format("dd/MM/yy HH':'mm 'UTC'") - else: "Never") - ), - tr( - th("Status"), - td($ui.rank) - ), - tr( - th(if c.rank >= Moderator: "Last IP" else: ""), - td(if c.rank >= Moderator: - htmlgen.a(href="http://whatismyipaddress.com/ip/" & encodeUrl(ui.lastIp), ui.lastIp) - else: "") - ), - tr( - th(""), - td(if c.rank >= Moderator and c.rank > ui.rank: - c.genFormSetRank(ui) - else: "") - ), - tr( - th(""), - td(if c.rank >= Moderator: - htmlgen.a(href=c.req.makeUri("/deleteAll?nick=$1" % ui.nick), - "Delete all user's posts and threads") - else: "") - ), - ) - )) - - result = htmlgen.`div`(id = "profile", - htmlgen.`div`(id = "left", result)) - -proc prependRe(s: string): string = - result = if s.len == 0: - "" - elif s.startswith("Re:"): s - else: "Re: " & s - proc initialise() = randomize() @@ -1319,6 +734,8 @@ proc updateProfile( $rank, email, username ) +include "main.tmpl" + initialise() routes: @@ -1869,64 +1286,6 @@ routes: createTFD() resp genPostsRSS(c), "application/atom+xml" - get "/deleteAll/?": - createTFD() - cond(@"nick" != "") - var formBody = "" - var del = false - var content = "" - formBody.add "" - content = htmlgen.p("Are you sure you wish to delete all " & - "the posts and threads created by ", htmlgen.b(@"nick"), "?") - content = content & htmlgen.form(action = c.req.makeUri("/dodeleteall"), - `method` = "POST", formBody) - resp genMain(c, content, "Delete all user's posts & threads - Nim Forum") - - post "/dodeleteall/?": - createTFD() - cond(@"nick" != "") - if c.rank < Moderator: - resp genMain(c, "You cannot delete this user's data.", "Error - Nim Forum") - let res = deleteAll(c, @"nick") - if res: - redirect(c.req.makeUri("/profile/" & @"nick")) - else: - resp genMain(c, "Failed to delete all user's posts and threads.", - "Error - NimForum") - - post "/dosetrank/?@nick?/?": - createTFD() - cond(@"nick" != "") - - if c.rank < Moderator: - resp genMain(c, "You cannot change this user's rank.", "Error - Nim Forum") - - var ui: TUserInfo - if not gatherUserInfo(c, @"nick", ui): - resp genMain(c, "User " & @"nick" & " does not exist.", "Error - Nim Forum") - let newRank = parseEnum[Rank](@"rank") - if newRank > c.rank: - resp genMain(c, "You cannot change this user's rank to this value.", "Error - Nim Forum") - - if setStatus(c, @"nick", newRank, @"reason"): - redirect(c.req.makeUri("/profile/" & @"nick")) - else: - resp genMain(c, "Failed to change the ban status of user.", - "Error - Nim Forum") - - get "/setpassword/?": - createTFD() - cond(@"nick" != "") - cond(@"pass" != "") - if c.rank < Moderator: - resp genMain(c, "You cannot change this user's pass.", "Error - Nim Forum") - let res = setPassword(c, @"nick", @"pass") - if res: - resp genMain(c, "Success", "Nim Forum") - else: - resp genMain(c, "Failure", "Nim Forum") - get "/activateEmail/?": createTFD() cond(@"nick" != "") @@ -1937,13 +1296,9 @@ routes: var success = false if verifyIdentHash(c, @"nick", $epoch, @"ident"): let ban = parseEnum[Rank](db.getValue(sql"select status from person where name = ?", @"nick")) - if ban == EmailUnconfirmed: - success = setStatus(c, @"nick", Moderated, "") + # if ban == EmailUnconfirmed: + # success = setStatus(c, @"nick", Moderated, "") - if success: - resp genMain(c, "Account activated", "Nim Forum") - else: - resp genMain(c, "Account activation failed", "Nim Forum") get "/emailResetPassword/?": createTFD() @@ -1965,9 +1320,10 @@ routes: let content = htmlgen.form(action=c.req.makeUri("/doemailresetpassword"), `method`="POST", message & formBody) - resp genMain(c, content, "Reset password - Nim Forum") + # resp genMain(c, content, "Reset password - Nim Forum") else: - resp genMain(c, "Invalid ident hash", "Error - Nim Forum") + discard + # resp genMain(c, "Invalid ident hash", "Error - Nim Forum") post "/doemailresetpassword": createTFD() @@ -1977,29 +1333,24 @@ routes: cond(@"password" != "") var epoch: BiggestInt = 0 cond(parseBiggestInt(@"epoch", epoch) > 0) - if verifyIdentHash(c, @"nick", $epoch, @"ident"): - let res = setPassword(c, @"nick", @"password") - if res: - resp genMain(c, "Password reset successfully!", "Nim Forum") - else: - resp genMain(c, "Password reset failure", "Nim Forum") - else: - resp genMain(c, "Invalid ident hash", "Nim Forum") - - get "/resetPassword/?": - createTFD() - - resp genMain(c, genFormResetPassword(c), "Reset Password - Nim Forum") + # if verifyIdentHash(c, @"nick", $epoch, @"ident"): + # let res = setPassword(c, @"nick", @"password") + # if res: + # resp genMain(c, "Password reset successfully!", "Nim Forum") + # else: + # resp genMain(c, "Password reset failure", "Nim Forum") + # else: + # resp genMain(c, "Invalid ident hash", "Nim Forum") post "/doresetpassword": createTFD() echo(request.params) cond(@"nick" != "") - if await resetPassword(c, @"nick", @"g-recaptcha-response", request.host): - resp genMain(c, "Email sent!", "Reset Password - Nim Forum") - else: - resp genMain(c, genFormResetPassword(c), "Reset Password - Nim Forum") + # if await resetPassword(c, @"nick", @"g-recaptcha-response", request.host): + # resp genMain(c, "Email sent!", "Reset Password - Nim Forum") + # else: + # resp genMain(c, genFormResetPassword(c), "Reset Password - Nim Forum") post "/search/?@page?": cond isFTSAvailable @@ -2021,28 +1372,5 @@ routes: [q,q,$ThreadsPerPage,$c.pageNum,$ThreadsPerPage,q, q,q,$ThreadsPerPage,$c.pageNum,$ThreadsPerPage,q]): yield rowFT - resp genMain(c, genSearchResults(c, searchResults, count), - additionalHeaders = genRSSHeaders(c), showRssLinks = true) - - # tries first to read html, then to read rst, convert ot html, cache and return - template textPage(path: string) = - createTFD() - #c.isThreadsList = true - var page = "" - if existsFile(path): - page = readFile(path) - else: - let basePath = - if path[path.high] == '/': path & "index" - elif path.endsWith(".html"): path[^5 .. ^1] - else: path - if existsFile(basePath & ".html"): - page = readFile(basePath & ".html") - elif existsFile(basePath & ".rst"): - page = readFile(basePath & ".rst").rstToHtml - writeFile(basePath & ".html", page) - resp genMain(c, page) - get "/search-help": - textPage "static/search-help" - get "/rst": - textPage "static/rst" + # resp genMain(c, genSearchResults(c, searchResults, count), + # additionalHeaders = genRSSHeaders(c), showRssLinks = true) \ No newline at end of file diff --git a/src/main.tmpl b/src/main.tmpl index 55ea1de..b9b9069 100644 --- a/src/main.tmpl +++ b/src/main.tmpl @@ -1,175 +1,7 @@ #? stdtmpl | standard -#proc genMain(c: TForumData, content: string, title = "Nim Forum", -# additional_headers = "", showRssLinks = false): string = -# result = "" -# var stats: TForumStats -# if c.isThreadsList: stats = c.getStats(false) -# else: -# stats = c.getStats(true) -# end if - - - - ${xmlEncode(title)} - - ${additional_headers} - - - - - #let frontQuery = c.req.makeUri("/") - - - -
-
-
-
-
-
-
-
-
- ${content} - #if c.isThreadsList: -
-
-
- ${c.genListOnline(stats)} -
-
-
- #if not c.noPagenumumNav: -
- ${genPagenumNav(c, stats)} -
- #end if - #elif hasReplyBtn(c): -
-
-
- ${genPagenumNav(c, stats)} -
-
-
- #if c.loggedIn(): - #let replyUri = c.req.makeUri(c.req.path & "?action=reply#reply") - -
- Reply -
-
- #end if -
-
- #end if - -
- -
-
- - - - - - - -#end proc -# +#template `!`(idx: untyped): untyped = +# row[idx] +#end template #proc genRSSHeaders(c: TForumData): string = # result = ""