gosora/plugin_markdown.go
Azareal f7942b42ac Continued the work on Cosora.
Trying to add support for Travis CI.

Added the NullUserStore for tests.
Moved more text out of the templates.
Removed an unnecessary dependency for Cosora.
Fixed a few more bugs in the MSSQL Adapter.
Disabled upserts for now, as it isn't working on MySQL.
Refactored the scheduled group logic to reduce the number of potential bugs and to stop using upserts.
Fixed many bugs in the Markdown Plugin.
Added the ability to escape Markdown.
Fixed a number of tests, commented unneccesary ones, and added new ones to probe for new problems.
Added the wink smiley.
Cleaned up some of the JavaScript.
Refactored the permissions logic to use transactions and avoid duplicating logic.
Added the ChangeRank method to the Group struct.
Added the ChangeGroup method to the User struct.
Added the currently unused LogWarning function.
Added the transaction versions of the builder methods. Better ones coming up soon!
The IsBanned flag is always set to false on mod and admin groups now.
Refactored the group creation logic to use transactions.
Fixed a bug in the group creator where new groups aren't visible to Exists().
The installer now drops tables for MySQL Databases, if they already exist to make it easier for us to run automated tests.
Added more ARIA Attributes.
2017-10-21 01:27:47 +01:00

365 lines
9.9 KiB
Go

package main
//import "fmt"
import (
"log"
"strings"
)
var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
var markdownUnclosedElement []byte
var markdownBoldTagOpen []byte
var markdownBoldTagClose []byte
var markdownItalicTagOpen []byte
var markdownItalicTagClose []byte
var markdownUnderlineTagOpen []byte
var markdownUnderlineTagClose []byte
var markdownStrikeTagOpen []byte
var markdownStrikeTagClose []byte
func init() {
plugins["markdown"] = NewPlugin("markdown", "Markdown", "Azareal", "http://github.com/Azareal", "", "", "", initMarkdown, nil, deactivateMarkdown, nil, nil)
}
func initMarkdown() error {
plugins["markdown"].AddHook("parse_assign", markdownParse)
markdownUnclosedElement = []byte("<span style='color: red;'>[Unclosed Element]</span>")
markdownBoldTagOpen = []byte("<b>")
markdownBoldTagClose = []byte("</b>")
markdownItalicTagOpen = []byte("<i>")
markdownItalicTagClose = []byte("</i>")
markdownUnderlineTagOpen = []byte("<u>")
markdownUnderlineTagClose = []byte("</u>")
markdownStrikeTagOpen = []byte("<s>")
markdownStrikeTagClose = []byte("</s>")
return nil
}
func deactivateMarkdown() {
plugins["markdown"].RemoveHook("parse_assign", markdownParse)
}
// An adapter for the parser, so that the parser can call itself recursively.
// This is less for the simple Markdown elements like bold and italics and more for the really complicated ones I plan on adding at some point.
func markdownParse(msg string) string {
msg = strings.TrimSpace(_markdownParse(msg+" ", 0))
log.Print("final msg: ", msg)
return msg
}
// Under Construction!
func _markdownParse(msg string, n int) string {
if n > markdownMaxDepth {
return "<span style='color: red;'>[Markdown Error: Overflowed the max depth of 20]</span>"
}
var outbytes []byte
var lastElement int
log.Printf("Initial Message: %+v\n", strings.Replace(msg, "\r", "\\r", -1))
for index := 0; index < len(msg); index++ {
//log.Print("--OUTER MARKDOWN LOOP START--")
//log.Print("index: ", index)
//log.Print("msg[index]: ", msg[index])
//log.Print("string(msg[index]): ", string(msg[index]))
//log.Printf("--OUTER MARKDOWN LOOP END--\n\n")
switch msg[index] {
// TODO: Do something slightly less hacky for skipping URLs
case '/':
if len(msg) > (index+2) && msg[index+1] == '/' {
for ; index < len(msg) && msg[index] != ' '; index++ {
}
index--
continue
}
case '_':
var startIndex = index
if (index + 1) >= len(msg) {
break
}
index++
index = markdownSkipUntilChar(msg, index, '_')
if (index-(startIndex+1)) < 1 || index >= len(msg) {
break
}
sIndex := startIndex + 1
lIndex := index
index++
outbytes = append(outbytes, msg[lastElement:startIndex]...)
outbytes = append(outbytes, markdownUnderlineTagOpen...)
// TODO: Implement this without as many type conversions
outbytes = append(outbytes, []byte(_markdownParse(msg[sIndex:lIndex], n+1))...)
outbytes = append(outbytes, markdownUnderlineTagClose...)
lastElement = index
index--
case '~':
var startIndex = index
if (index + 1) >= len(msg) {
break
}
index++
index = markdownSkipUntilChar(msg, index, '~')
if (index-(startIndex+1)) < 1 || index >= len(msg) {
break
}
sIndex := startIndex + 1
lIndex := index
index++
outbytes = append(outbytes, msg[lastElement:startIndex]...)
outbytes = append(outbytes, markdownStrikeTagOpen...)
// TODO: Implement this without as many type conversions
outbytes = append(outbytes, []byte(_markdownParse(msg[sIndex:lIndex], n+1))...)
outbytes = append(outbytes, markdownStrikeTagClose...)
lastElement = index
index--
case '*':
//log.Print("------")
//log.Print("[]byte(msg): ", []byte(msg))
//log.Print("len(msg): ", len(msg))
//log.Print("start index: ", index)
//log.Print("start msg[index]: ", msg[index])
//log.Print("start string(msg[index]): ", string(msg[index]))
//log.Print("start []byte(msg[:index]): ", []byte(msg[:index]))
var startIndex = index
var italic = true
var bold = false
if (index + 2) < len(msg) {
//log.Print("start index + 1: ", index + 1)
//log.Print("start msg[index]: ", msg[index + 1])
//log.Print("start string(msg[index]): ", string(msg[index + 1]))
if msg[index+1] == '*' {
//log.Print("two asterisks")
bold = true
index++
if msg[index+1] != '*' {
italic = false
} else {
//log.Print("three asterisks")
index++
}
}
}
//log.Print("lastElement: ", lastElement)
//log.Print("startIndex: ", startIndex)
//log.Print("msg[startIndex]: ", msg[startIndex])
//log.Print("string(msg[startIndex]): ", string(msg[startIndex]))
//log.Print("preabrupt index: ", index)
//log.Print("preabrupt msg[index]: ", msg[index])
//log.Print("preabrupt string(msg[index]): ", string(msg[index]))
//log.Print("preabrupt []byte(msg[:index]): ", []byte(msg[:index]))
//log.Print("preabrupt msg[:index]: ", msg[:index])
// Does the string terminate abruptly?
if (index + 1) >= len(msg) {
break
}
index++
//log.Print("preskip index",index)
//log.Print("preskip msg[index]",msg[index])
//log.Print("preskip string(msg[index])",string(msg[index]))
index = markdownSkipUntilAsterisk(msg, index)
if index >= len(msg) {
break
}
//log.Print("index: ", index)
//log.Print("[]byte(msg[:index]): ", []byte(msg[:index]))
//log.Print("msg[index]: ", msg[index])
sIndex := startIndex
lIndex := index
if bold && italic {
//log.Print("bold & italic final code")
if (index + 3) >= len(msg) {
//log.Print("unclosed markdown element @ exit element")
outbytes = append(outbytes, msg[lastElement:startIndex]...)
//outbytes = append(outbytes, markdownUnclosedElement...)
lastElement = startIndex
break
}
index += 3
sIndex += 3
} else if bold {
//log.Print("bold final code")
if (index + 2) >= len(msg) {
//log.Print("true unclosed markdown element @ exit element")
outbytes = append(outbytes, msg[lastElement:startIndex]...)
//outbytes = append(outbytes, markdownUnclosedElement...)
lastElement = startIndex
break
}
index += 2
sIndex += 2
} else {
//log.Print("italic final code")
if (index + 1) >= len(msg) {
//log.Print("true unclosed markdown element @ exit element")
outbytes = append(outbytes, msg[lastElement:startIndex]...)
//outbytes = append(outbytes, markdownUnclosedElement...)
lastElement = startIndex
break
}
index++
sIndex++
}
//log.Print("sIndex",sIndex)
//log.Print("lIndex",lIndex)
if lIndex <= sIndex {
//log.Print("unclosed markdown element @ lIndex <= sIndex")
outbytes = append(outbytes, msg[lastElement:startIndex]...)
//outbytes = append(outbytes, markdownUnclosedElement...)
lastElement = startIndex
break
}
if sIndex < 0 || lIndex < 0 {
//log.Print("unclosed markdown element @ sIndex < 0 || lIndex < 0")
outbytes = append(outbytes, msg[lastElement:startIndex]...)
//outbytes = append(outbytes, markdownUnclosedElement...)
lastElement = startIndex
break
}
//log.Print("final sIndex",sIndex)
//log.Print("final lIndex",lIndex)
//log.Print("final index",index)
//log.Print("final msg[index]",msg[index])
//log.Print("final string(msg[index])",string(msg[index]))
//log.Print("final msg[sIndex]",msg[sIndex])
//log.Print("final string(msg[sIndex])",string(msg[sIndex]))
//log.Print("final msg[lIndex]",msg[lIndex])
//log.Print("final string(msg[lIndex])",string(msg[lIndex]))
//log.Print("[]byte(msg[:sIndex])",[]byte(msg[:sIndex]))
//log.Print("[]byte(msg[:lIndex])",[]byte(msg[:lIndex]))
outbytes = append(outbytes, msg[lastElement:startIndex]...)
if bold {
outbytes = append(outbytes, markdownBoldTagOpen...)
}
if italic {
outbytes = append(outbytes, markdownItalicTagOpen...)
}
// TODO: Implement this without as many type conversions
outbytes = append(outbytes, []byte(_markdownParse(msg[sIndex:lIndex], n+1))...)
if italic {
outbytes = append(outbytes, markdownItalicTagClose...)
}
if bold {
outbytes = append(outbytes, markdownBoldTagClose...)
}
lastElement = index
index--
case '\\':
if (index + 1) < len(msg) {
outbytes = append(outbytes, msg[lastElement:index]...)
index++
lastElement = index
}
//case '`':
//case '_':
//case '~':
//case 10: // newline
}
}
//log.Print("exit message loop")
if len(outbytes) == 0 {
return msg
} else if lastElement < (len(msg) - 1) {
return string(outbytes) + msg[lastElement:]
}
return string(outbytes)
}
func markdownFindChar(data string, index int, char byte) bool {
for ; index < len(data); index++ {
item := data[index]
if item > 32 {
return (item == char)
}
}
return false
}
func markdownSkipUntilChar(data string, index int, char byte) int {
for ; index < len(data); index++ {
if data[index] == char {
break
}
}
return index
}
func markdownSkipUntilAsterisk(data string, index int) int {
SwitchLoop:
for ; index < len(data); index++ {
switch data[index] {
case 10:
if ((index + 1) < len(data)) && markdownFindChar(data, index, '*') {
index = markdownSkipList(data, index)
}
case '*':
break SwitchLoop
}
}
return index
}
// plugin_markdown doesn't support lists yet, but I want it to be easy to have nested lists when we do have them
func markdownSkipList(data string, index int) int {
var lastNewline int
var datalen = len(data)
for ; index < datalen; index++ {
SkipListInnerLoop:
if data[index] == 10 {
lastNewline = index
for ; index < datalen; index++ {
if data[index] > 32 {
break
} else if data[index] == 10 {
goto SkipListInnerLoop
}
}
if index >= datalen {
if data[index] != '*' && data[index] != '-' {
if (lastNewline + 1) < datalen {
return lastNewline + 1
}
return lastNewline
}
}
}
}
return index
}