Implements mention highlighting in posts.

This commit is contained in:
Dominik Picheta 2018-05-14 14:41:12 +01:00
commit eeda927085
2 changed files with 90 additions and 36 deletions

View file

@ -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!")

View file

@ -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: