Implements mention highlighting in posts.
This commit is contained in:
parent
53ed3717b8
commit
eeda927085
2 changed files with 90 additions and 36 deletions
|
|
@ -281,9 +281,6 @@ proc validThreadId(c: TForumData): bool =
|
||||||
result = getValue(db, sql"select id from thread where id = ?",
|
result = getValue(db, sql"select id from thread where id = ?",
|
||||||
$c.threadId).len > 0
|
$c.threadId).len > 0
|
||||||
|
|
||||||
const
|
|
||||||
SecureChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '\128'..'\255'}
|
|
||||||
|
|
||||||
proc setError(c: TForumData, field, msg: string): bool {.inline.} =
|
proc setError(c: TForumData, field, msg: string): bool {.inline.} =
|
||||||
c.invalidField = field
|
c.invalidField = field
|
||||||
c.errorMsg = "Error: " & msg
|
c.errorMsg = "Error: " & msg
|
||||||
|
|
@ -292,7 +289,7 @@ proc setError(c: TForumData, field, msg: string): bool {.inline.} =
|
||||||
proc register(c: TForumData, name, pass, antibot, userIp,
|
proc register(c: TForumData, name, pass, antibot, userIp,
|
||||||
email: string): Future[bool] {.async.} =
|
email: string): Future[bool] {.async.} =
|
||||||
# Username validation:
|
# Username validation:
|
||||||
if name.len == 0 or not allCharsInSet(name, SecureChars):
|
if name.len == 0 or not allCharsInSet(name, UsernameIdent):
|
||||||
return setError(c, "name", "Invalid username!")
|
return setError(c, "name", "Invalid username!")
|
||||||
if getValue(db, sql"select name from person where name = ?", name).len > 0:
|
if getValue(db, sql"select name from person where name = ?", name).len > 0:
|
||||||
return setError(c, "name", "Username already exists!")
|
return setError(c, "name", "Username already exists!")
|
||||||
|
|
|
||||||
87
utils.nim
87
utils.nim
|
|
@ -2,6 +2,12 @@ import asyncdispatch, smtp, strutils, json, os, rst, rstgen, xmltree, strtabs,
|
||||||
htmlparser, streams, parseutils, options
|
htmlparser, streams, parseutils, options
|
||||||
from times import getTime, getGMTime, format
|
from times import getTime, getGMTime, format
|
||||||
|
|
||||||
|
# Used to be:
|
||||||
|
# {'A'..'Z', 'a'..'z', '0'..'9', '_', '\128'..'\255'}
|
||||||
|
let
|
||||||
|
UsernameIdent* = IdentChars # TODO: Double check that everyone follows this.
|
||||||
|
|
||||||
|
|
||||||
proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
|
proc parseInt*(s: string, value: var int, validRange: Slice[int]) {.
|
||||||
noSideEffect.} =
|
noSideEffect.} =
|
||||||
## parses `s` into an integer in the range `validRange`. If successful,
|
## parses `s` into an integer in the range `validRange`. If successful,
|
||||||
|
|
@ -80,22 +86,17 @@ proc blockquoteFinish(currentBlockquote, newNode: var XmlNode, n: XmlNode) =
|
||||||
currentBlockquote = newElement("blockquote")
|
currentBlockquote = newElement("blockquote")
|
||||||
newNode.add(n)
|
newNode.add(n)
|
||||||
|
|
||||||
proc rstToHtml*(content: string): string =
|
proc processQuotes(node: XmlNode): XmlNode =
|
||||||
result = rstgen.rstToHtml(content, {roSupportMarkdown},
|
|
||||||
docConfig)
|
|
||||||
# Bolt on quotes.
|
# Bolt on quotes.
|
||||||
# TODO: Yes, this is ugly. I wrote it quickly. PRs welcome ;)
|
# TODO: Yes, this is ugly. I wrote it quickly. PRs welcome ;)
|
||||||
try:
|
result = newElement("div")
|
||||||
var node = parseHtml(newStringStream(result))
|
|
||||||
var newNode = newElement("div")
|
|
||||||
if node.kind == xnElement:
|
|
||||||
var currentBlockquote = newElement("blockquote")
|
var currentBlockquote = newElement("blockquote")
|
||||||
for n in items(node):
|
for n in items(node):
|
||||||
case n.kind
|
case n.kind
|
||||||
of xnElement:
|
of xnElement:
|
||||||
case n.tag
|
case n.tag
|
||||||
of "p":
|
of "p":
|
||||||
let (nesting, contentNode, tag) = processGT(n, "p")
|
let (nesting, contentNode, _) = processGT(n, "p")
|
||||||
if nesting > 0:
|
if nesting > 0:
|
||||||
var bq = currentBlockquote
|
var bq = currentBlockquote
|
||||||
for i in 1 ..< nesting:
|
for i in 1 ..< nesting:
|
||||||
|
|
@ -106,20 +107,76 @@ proc rstToHtml*(content: string): string =
|
||||||
bq = newBq
|
bq = newBq
|
||||||
bq.insert(contentNode, if bq.len == 0: 0 else: bq.len)
|
bq.insert(contentNode, if bq.len == 0: 0 else: bq.len)
|
||||||
else:
|
else:
|
||||||
blockquoteFinish(currentBlockquote, newNode, n)
|
blockquoteFinish(currentBlockquote, result, n)
|
||||||
else:
|
else:
|
||||||
blockquoteFinish(currentBlockquote, newNode, n)
|
blockquoteFinish(currentBlockquote, result, n)
|
||||||
of xnText:
|
of xnText:
|
||||||
if n.text[0] == '\10':
|
if n.text[0] == '\10':
|
||||||
newNode.add(n)
|
result.add(n)
|
||||||
else:
|
else:
|
||||||
blockquoteFinish(currentBlockquote, newNode, n)
|
blockquoteFinish(currentBlockquote, result, n)
|
||||||
else:
|
else:
|
||||||
blockquoteFinish(currentBlockquote, newNode, n)
|
blockquoteFinish(currentBlockquote, result, n)
|
||||||
|
|
||||||
|
proc replaceMentions(node: XmlNode): seq[XmlNode] =
|
||||||
|
assert node.kind == xnText
|
||||||
|
result = @[]
|
||||||
|
|
||||||
|
var current = ""
|
||||||
|
var i = 0
|
||||||
|
while i < len(node.text):
|
||||||
|
i += parseUntil(node.text, current, {'@'}, i)
|
||||||
|
if i >= len(node.text): break
|
||||||
|
if node.text[i] == '@':
|
||||||
|
i.inc # Skip @
|
||||||
|
var username = ""
|
||||||
|
i += parseWhile(node.text, username, UsernameIdent, i)
|
||||||
|
|
||||||
|
let el = <>span(
|
||||||
|
class="user-mention",
|
||||||
|
data-username=username,
|
||||||
|
newText("@" & username)
|
||||||
|
)
|
||||||
|
|
||||||
|
result.add(newText(current))
|
||||||
|
current = ""
|
||||||
|
result.add(el)
|
||||||
|
|
||||||
|
result.add(newText(current))
|
||||||
|
|
||||||
|
proc processMentions(node: XmlNode): XmlNode =
|
||||||
|
case node.kind
|
||||||
|
of xnText:
|
||||||
|
result = newElement("span")
|
||||||
|
for child in replaceMentions(node):
|
||||||
|
result.add(child)
|
||||||
|
of xnElement:
|
||||||
|
case node.tag
|
||||||
|
of "pre", "code", "tt":
|
||||||
|
return node
|
||||||
|
else:
|
||||||
|
result = newElement(node.tag)
|
||||||
|
for n in items(node):
|
||||||
|
result.add(processMentions(n))
|
||||||
|
else:
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc rstToHtml*(content: string): string =
|
||||||
|
result = rstgen.rstToHtml(content, {roSupportMarkdown},
|
||||||
|
docConfig)
|
||||||
|
try:
|
||||||
|
var node = parseHtml(newStringStream(result))
|
||||||
|
if node.kind == xnElement:
|
||||||
|
let quotedNode = processQuotes(node)
|
||||||
|
let mentionedNode = processMentions(quotedNode)
|
||||||
|
|
||||||
result = ""
|
result = ""
|
||||||
add(result, newNode, indWidth=0, addNewLines=false)
|
add(result, mentionedNode, indWidth=0, addNewLines=false)
|
||||||
except:
|
except:
|
||||||
echo("[WARNING] Could not parse rst html.")
|
raise
|
||||||
|
# echo("[WARNING] Could not parse rst html.")
|
||||||
|
|
||||||
proc sendMail(config: Config, subject, message, recipient: string, from_addr = "forum@nim-lang.org", otherHeaders:seq[(string, string)] = @[]) {.async.} =
|
proc sendMail(config: Config, subject, message, recipient: string, from_addr = "forum@nim-lang.org", otherHeaders:seq[(string, string)] = @[]) {.async.} =
|
||||||
if config.smtpAddress.len == 0:
|
if config.smtpAddress.len == 0:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue