Implemented paging.
This commit is contained in:
parent
961a3fc0e9
commit
9beee9848c
5 changed files with 224 additions and 47 deletions
24
forms.tmpl
24
forms.tmpl
|
|
@ -5,13 +5,14 @@
|
|||
#end template
|
||||
#
|
||||
#
|
||||
#proc genThreadsList(c: var TForumData): string =
|
||||
# const query = sql"select id, name, views, modified from thread order by modified desc"
|
||||
#proc genThreadsList(c: var TForumData, count: var int): string =
|
||||
# const query = sql"select id, name, views, modified from thread order by modified desc limit ?, ?"
|
||||
# const threadId = 0
|
||||
# const name = 1
|
||||
# const views = 2
|
||||
#
|
||||
# result = ""
|
||||
# count = 0
|
||||
<table id="threads">
|
||||
<tr>
|
||||
<th>Topics</th>
|
||||
|
|
@ -20,7 +21,8 @@
|
|||
<th>Views</th>
|
||||
<th>Last reply</th>
|
||||
</tr>
|
||||
# for row in Rows(db, query):
|
||||
# for row in Rows(db, query, $((c.pageNum-1) * ThreadsPerPage), $ThreadsPerPage):
|
||||
# inc(count)
|
||||
<tr>
|
||||
<td class="topic">${UrlButton(c, XMLencode(%name), c.genThreadUrl(threadid = %threadid))}</td>
|
||||
#let authorName = getValue(db, sql("select name from person where id = " &
|
||||
|
|
@ -48,6 +50,7 @@
|
|||
#proc genPostPreview(c: var TForumData,
|
||||
# title, content, author, date: string): string =
|
||||
# result = ""
|
||||
<a name="preview"></a>
|
||||
<table class="post">
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
|
|
@ -71,8 +74,9 @@
|
|||
#end proc
|
||||
#
|
||||
#
|
||||
#proc genPostsList(c: var TForumData, threadId: string): string =
|
||||
# const 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 = ? order by p.id"
|
||||
#proc genPostsList(c: var TForumData, threadId: string, count: var int): string =
|
||||
# const 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 = ? order by p.id limit ?, ?"""
|
||||
# const postId = 0
|
||||
# const userName = 1
|
||||
# const postHeader = 2
|
||||
|
|
@ -81,7 +85,10 @@
|
|||
# const postAuthor = 5
|
||||
# const userEmail = 6
|
||||
# result = ""
|
||||
# for row in FastRows(db, query, threadId):
|
||||
# count = 0
|
||||
# for row in FastRows(db, query, threadId, $((c.pageNum-1) * PostsPerPage), $PostsPerPage):
|
||||
# inc(count)
|
||||
<a name="${%postId}"></a>
|
||||
<table class="post">
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
|
|
@ -123,7 +130,7 @@
|
|||
<div id="replytop">
|
||||
<span>${topText}</span>
|
||||
</div>
|
||||
<form action="${c.req.makeUri(action, false)}" method="POST">
|
||||
<form action="${c.req.makeUri(action, false) & "#preview"}" method="POST">
|
||||
${FieldValid(c, "subject", "Subject:")}
|
||||
${TextWidget(c, "subject", title, maxlength=100)}
|
||||
<br />
|
||||
|
|
@ -189,9 +196,8 @@
|
|||
#end proc
|
||||
#
|
||||
#
|
||||
#proc genListOnline(c: var TForumData): string =
|
||||
#proc genListOnline(c: var TForumData, stats: TForumStats): string =
|
||||
# result = ""
|
||||
# let stats = c.getStats()
|
||||
<div id="whoisonline">
|
||||
<div class="wioHeader">
|
||||
<span>Who is online?<span>
|
||||
|
|
|
|||
186
forum.nim
186
forum.nim
|
|
@ -8,12 +8,17 @@
|
|||
|
||||
import
|
||||
os, strutils, times, md5, strtabs, cgi, math, db_sqlite, matchers,
|
||||
rst, rstgen, captchas, sockets, scgi, jester
|
||||
rst, rstgen, captchas, sockets, scgi, jester, htmlgen
|
||||
|
||||
const
|
||||
unselectedThread = -1
|
||||
transientThread = 0
|
||||
|
||||
ThreadsPerPage = 15
|
||||
PostsPerPage = 10
|
||||
noPageNums = ["/login", "/register", "/dologin", "/doregister"]
|
||||
noHomeBtn = ["/", "/login", "/register", "/dologin", "/doregister"]
|
||||
|
||||
type
|
||||
TCrud = enum crCreate, crRead, crUpdate, crDelete
|
||||
|
||||
|
|
@ -33,7 +38,10 @@ type
|
|||
invalidField: string
|
||||
currentPost: TPost
|
||||
startTime: float
|
||||
|
||||
isThreadsList: bool
|
||||
pageNum: int
|
||||
totalPosts: int
|
||||
|
||||
TStyledButton = tuple[text: string, link: string]
|
||||
|
||||
TForumStats = object
|
||||
|
|
@ -90,8 +98,10 @@ proc FieldValid(c: TForumData, name, text: string): string =
|
|||
else:
|
||||
result = text
|
||||
|
||||
proc genThreadUrl(c: TForumData, postId = "", action = "", threadid = ""): string =
|
||||
proc genThreadUrl(c: TForumData, postId = "", action = "", threadid = "", pageNum = ""): string =
|
||||
result = "/t/" & (if threadid == "": $c.threadId else: threadid)
|
||||
if pageNum != "":
|
||||
result.add("/" & pageNum)
|
||||
if action != "":
|
||||
result.add("?action=" & action)
|
||||
if postId != "":
|
||||
|
|
@ -328,9 +338,11 @@ template setPreviewData(c: expr) =
|
|||
c.currentPost.subject = subject
|
||||
c.currentPost.content = content
|
||||
|
||||
template writeToDb(c, cr, postId: expr) =
|
||||
exec(db, crud(cr, "post", "author", "ip", "header", "content", "thread"),
|
||||
c.userId, c.req.ip, subject, content, $c.threadId, postId)
|
||||
template writeToDb(c, cr, setPostId: expr) =
|
||||
let retID = insertID(db, crud(cr, "post", "author", "ip", "header", "content", "thread"),
|
||||
c.userId, c.req.ip, subject, content, $c.threadId, "")
|
||||
if setPostId:
|
||||
c.postId = retID.int
|
||||
|
||||
proc edit(c: var TForumData, postId: int): bool =
|
||||
checkLogin(c)
|
||||
|
|
@ -360,7 +372,8 @@ proc reply(c: var TForumData): bool =
|
|||
if c.isPreview:
|
||||
setPreviewData(c)
|
||||
else:
|
||||
writeToDb(c, crCreate, "")
|
||||
writeToDb(c, crCreate, true)
|
||||
|
||||
exec(db, sql"update thread set modified = DATETIME('now') where id = ?",
|
||||
$c.threadId)
|
||||
result = true
|
||||
|
|
@ -375,7 +388,7 @@ proc newThread(c: var TForumData): bool =
|
|||
else:
|
||||
c.threadID = TryInsertID(db, query, c.req.params["subject"]).int
|
||||
if c.threadID < 0: return setError(c, "subject", "Subject already exists")
|
||||
writeToDb(c, crCreate, "")
|
||||
writeToDb(c, crCreate, false)
|
||||
result = true
|
||||
|
||||
proc login(c: var TForumData, name, pass: string): bool =
|
||||
|
|
@ -407,17 +420,18 @@ proc genActionMenu(c: var TForumData): string =
|
|||
result = ""
|
||||
var btns: seq[TStyledButton] = @[]
|
||||
# TODO: Make this detection better?
|
||||
if c.req.pathInfo notin ["/", "/login", "/register", "/dologin", "/doregister"]:
|
||||
if c.req.pathInfo.normalizeUri notin noHomeBtn and not c.isThreadsList:
|
||||
btns.add(("Thread List", c.req.makeUri("/", false)))
|
||||
if c.loggedIn:
|
||||
let hasReplyBtn = c.req.pathInfo != "/donewthread" and c.req.pathInfo != "/doreply"
|
||||
if c.threadId >= 0 and hasReplyBtn:
|
||||
let replyUrl = c.genThreadUrl("", "reply") & "#reply"
|
||||
let replyUrl = c.genThreadUrl(action = "reply",
|
||||
pageNum = $(ceil(c.totalPosts / postsPerPage).int)) & "#reply"
|
||||
btns.add(("Reply", replyUrl))
|
||||
btns.add(("New Thread", c.req.makeUri("/newthread", false)))
|
||||
result = c.genButtons(btns)
|
||||
|
||||
proc getStats(c: var TForumData): TForumStats =
|
||||
proc getStats(c: var TForumData, simple: bool): TForumStats =
|
||||
const totalUsersQuery =
|
||||
sql"select count(*) from person"
|
||||
result.totalUsers = getValue(db, totalUsersQuery).parseInt
|
||||
|
|
@ -427,19 +441,89 @@ proc getStats(c: var TForumData): TForumStats =
|
|||
const totalThreadsQuery =
|
||||
sql"select count(*) from thread"
|
||||
result.totalThreads = getValue(db, totalThreadsQuery).parseInt
|
||||
if not simple:
|
||||
var newestMemberCreation = 0
|
||||
result.activeUsers = @[]
|
||||
const getUsersQuery =
|
||||
sql"select id, name, admin, strftime('%s', lastOnline), strftime('%s', creation) from person"
|
||||
for row in fastRows(db, getUsersQuery):
|
||||
let secs = if row[3] == "": 0 else: row[3].parseint
|
||||
let lastOnlineSeconds = getTime() - TTime(secs)
|
||||
if lastOnlineSeconds < (60 * 5): # 5 minutes
|
||||
result.activeUsers.add((row[1], row[0].parseInt, row[2].parseBool))
|
||||
if row[4].parseInt > newestMemberCreation:
|
||||
result.newestMember = (row[1], row[0].parseInt, row[2].parseBool)
|
||||
newestMemberCreation = row[4].parseInt
|
||||
|
||||
proc genPagenumNav(c: var TForumData, stats: TForumStats): string =
|
||||
result = ""
|
||||
var
|
||||
firstUrl = ""
|
||||
prevUrl = ""
|
||||
totalPages = 0
|
||||
lastUrl = ""
|
||||
nextUrl = ""
|
||||
|
||||
var newestMemberCreation = 0
|
||||
result.activeUsers = @[]
|
||||
const getUsersQuery =
|
||||
sql"select id, name, admin, strftime('%s', lastOnline), strftime('%s', creation) from person"
|
||||
for row in fastRows(db, getUsersQuery):
|
||||
let secs = if row[3] == "": 0 else: row[3].parseint
|
||||
let lastOnlineSeconds = getTime() - TTime(secs)
|
||||
if lastOnlineSeconds < (60 * 5): # 5 minutes
|
||||
result.activeUsers.add((row[1], row[0].parseInt, row[2].parseBool))
|
||||
if row[4].parseInt > newestMemberCreation:
|
||||
result.newestMember = (row[1], row[0].parseInt, row[2].parseBool)
|
||||
newestMemberCreation = row[4].parseInt
|
||||
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.req.makeUri("/t/" & $c.threadId)
|
||||
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("First")
|
||||
prevTag = span("Prev")
|
||||
else:
|
||||
firstTag = a(href=firstUrl, "First")
|
||||
prevTag = a(href=prevUrl, "Prev")
|
||||
result.add(htmlgen.`div`(class = "left",
|
||||
firstTag,
|
||||
prevTag))
|
||||
# Right
|
||||
var lastTag = ""
|
||||
var nextTag = ""
|
||||
if c.pageNum == totalPages:
|
||||
lastTag = span("Last")
|
||||
nextTag = span("Next")
|
||||
else:
|
||||
lastTag = a(href=lastUrl, "Last")
|
||||
nextTag = a(href=nextUrl, "Next")
|
||||
result.add(htmlgen.`div`(class = "right",
|
||||
nextTag,
|
||||
lastTag))
|
||||
|
||||
# Numbers
|
||||
var pages = "" # Tags
|
||||
for i in 1..totalPages:
|
||||
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(a(href = pageUrl, $(i)))
|
||||
result.add(htmlgen.`div`(class = "middle",
|
||||
pages))
|
||||
|
||||
result = htmlgen.`div`(id = "pagenumbers", result)
|
||||
|
||||
include "forms.tmpl"
|
||||
include "main.tmpl"
|
||||
|
|
@ -455,27 +539,43 @@ template createTFD(): stmt =
|
|||
init(c)
|
||||
c.req = request
|
||||
c.startTime = epochTime()
|
||||
c.isThreadsList = false
|
||||
c.pageNum = 1
|
||||
if request.cookies.len > 0:
|
||||
checkLoggedIn(c)
|
||||
|
||||
proc gatherData(c: var 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
|
||||
|
||||
get "/":
|
||||
createTFD()
|
||||
resp genMain(c, genThreadsList(c), true)
|
||||
c.isThreadsList = true
|
||||
var count = 0
|
||||
resp genMain(c, genThreadsList(c, count))
|
||||
|
||||
get "/t/@threadid/?":
|
||||
get "/t/@threadid/?@page?/?":
|
||||
createTFD()
|
||||
if @"page".len > 0:
|
||||
parseInt(@"page", c.pageNum, 0..1000_000)
|
||||
cond (c.pageNum > 0)
|
||||
parseInt(@"threadid", c.threadId, -1..1000_000)
|
||||
if (@"postid").len > 0:
|
||||
parseInt(@"postid", c.postId, -1..1000_000)
|
||||
|
||||
var count = 0
|
||||
cond validThreadId(c)
|
||||
gatherData(c)
|
||||
if (@"action").len > 0:
|
||||
case @"action"
|
||||
of "reply":
|
||||
let subject = GetValue(db,
|
||||
sql"select header from post where id = (select max(id) from post where thread = ?)",
|
||||
$c.threadId).prependRe
|
||||
body = genPostsList(c, $c.threadId)
|
||||
echo(c.threadId)
|
||||
body = genPostsList(c, $c.threadId, count)
|
||||
cond count != 0
|
||||
body.add genFormPost(c, "doreply", "Reply", subject, "", false)
|
||||
of "edit":
|
||||
cond c.postId != -1
|
||||
|
|
@ -486,9 +586,22 @@ get "/t/@threadid/?":
|
|||
body = genFormPost(c, "doedit", "Edit", header, content, true)
|
||||
resp c.genMain(body)
|
||||
else:
|
||||
cond validThreadId(c)
|
||||
incrementViews(c)
|
||||
resp genMain(c, genPostsList(c, $c.threadId))
|
||||
let posts = genPostsList(c, $c.threadId, count)
|
||||
cond count != 0
|
||||
resp genMain(c, posts)
|
||||
|
||||
get "/page/@page/?":
|
||||
createTFD()
|
||||
c.isThreadsList = true
|
||||
cond (@"page" != "")
|
||||
parseInt(@"page", c.pageNum, 0..1000_000)
|
||||
cond (c.pageNum > 0)
|
||||
var count = 0
|
||||
let list = genThreadsList(c, count)
|
||||
if count == 0:
|
||||
pass()
|
||||
resp genMain(c, list)
|
||||
|
||||
get "/login/?":
|
||||
createTFD()
|
||||
|
|
@ -504,12 +617,16 @@ get "/register/?":
|
|||
resp genMain(c, genFormRegister(c))
|
||||
|
||||
template readIDs(): stmt =
|
||||
# Retrieve the threadid and postid
|
||||
# Retrieve the threadid, postid and pagenum
|
||||
if (@"threadid").len > 0:
|
||||
parseInt(@"threadid", c.threadId, -1..1000_000)
|
||||
if (@"postid").len > 0:
|
||||
parseInt(@"postid", c.postId, -1..1000_000)
|
||||
|
||||
proc getTotalPosts(c: var TForumData): int =
|
||||
c.gatherData() # Get total post count
|
||||
result = ceil(c.totalPosts / postsPerPage).int-1
|
||||
|
||||
template finishLogin(): stmt =
|
||||
setCookie("sid", c.userpass, daysForward(7))
|
||||
redirect(uri("/"))
|
||||
|
|
@ -548,9 +665,12 @@ post "/doreply":
|
|||
createTFD()
|
||||
readIDs()
|
||||
if reply(c):
|
||||
redirect(c.genThreadUrl())
|
||||
redirect(c.genThreadUrl(pageNum = $(c.getTotalPosts+1)) & "#" & $c.postId)
|
||||
else:
|
||||
body = genPostsList(c, $c.threadId)
|
||||
var count = 0
|
||||
if c.isPreview:
|
||||
c.pageNum = c.getTotalPosts+1
|
||||
body = genPostsList(c, $c.threadId, count)
|
||||
handleError("doreply", "Reply", false)
|
||||
|
||||
post "/doedit":
|
||||
|
|
|
|||
16
main.tmpl
16
main.tmpl
|
|
@ -1,6 +1,11 @@
|
|||
#! stdtmpl
|
||||
#proc genMain(c: var TForumData, content: string, mainPage = false): string =
|
||||
#proc genMain(c: var TForumData, content: string): string =
|
||||
# result = ""
|
||||
# var stats: TForumStats
|
||||
# if c.isThreadsList: stats = c.getStats(false)
|
||||
# else:
|
||||
# stats = c.getStats(true)
|
||||
# end if
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
|
@ -35,12 +40,15 @@
|
|||
$content
|
||||
<span style="color:red">$c.errorMsg</span>
|
||||
</div>
|
||||
#if c.req.pathInfo.normalizeUri notin noPageNums:
|
||||
${c.genPagenumNav(stats)}
|
||||
#end if
|
||||
<div id="topbar">
|
||||
${c.genActionMenu}
|
||||
</div>
|
||||
|
||||
#if mainPage:
|
||||
${c.genListOnline}
|
||||
|
||||
#if c.isThreadsList:
|
||||
${c.genListOnline(stats)}
|
||||
#end if
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
--path:"$nimrod/packages/docutils"
|
||||
|
||||
--path:"$nimrod"
|
||||
--path:"/home/dominik/code/nimrod/jester"
|
||||
--path:"/home/dom/code/nimrod/jester"
|
||||
|
|
|
|||
|
|
@ -240,6 +240,49 @@ div#replywrapper form {
|
|||
padding: 8pt;
|
||||
}
|
||||
|
||||
div#pagenumbers {
|
||||
font-size: 11pt;
|
||||
height: 21px;
|
||||
margin: 5.9pt;
|
||||
padding: 2pt;
|
||||
padding-left: 4pt;
|
||||
padding-right: 4pt;
|
||||
border-top: 1px solid #9d9d9d;
|
||||
border-bottom: 1px solid #9d9d9d;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div#pagenumbers div.left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
div#pagenumbers div.middle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div#pagenumbers div.middle a, div#pagenumbers div.middle span {
|
||||
padding-right: 4pt;
|
||||
}
|
||||
|
||||
div#pagenumbers div.middle span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div#pagenumbers div.left span, div#pagenumbers div.left a {
|
||||
padding-right: 8pt;
|
||||
}
|
||||
|
||||
div#pagenumbers div.right span, div#pagenumbers div.right a {
|
||||
padding-left: 8pt;
|
||||
}
|
||||
|
||||
|
||||
div#pagenumbers div.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* For RST nimrod syntax highlighter */
|
||||
span.DecNumber {color: blue}
|
||||
span.BinNumber {color: blue}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue