Variables which are computed multiple times in templates should only be computed once now.
Enabled the Watchdog Goroutine. Moved a couple of parser tests into their own file. Eliminated the hard-coded URLs in TestParser() Refactored the alert system so that a specific actor is only loaded once rather than once for each alert. This isn't a problem in practice due to the user cache, but it might be on high traffic sites. Don't run HandleServerSync() on single server setups. Don't load the user in GetTopicUser(), if the topic creator is the current user. Fixed a bug in the template generator where endloop nodes were being pushed instead of endif ones.
This commit is contained in:
parent
87b3f9107d
commit
52c8be4173
100
common/alerts.go
100
common/alerts.go
|
@ -12,10 +12,21 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
|
||||||
"github.com/Azareal/Gosora/common/phrases"
|
"github.com/Azareal/Gosora/common/phrases"
|
||||||
|
"github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Alert struct {
|
||||||
|
ASID int
|
||||||
|
ActorID int
|
||||||
|
TargetUserID int
|
||||||
|
Event string
|
||||||
|
ElementType string
|
||||||
|
ElementID int
|
||||||
|
|
||||||
|
Actor *User
|
||||||
|
}
|
||||||
|
|
||||||
type AlertStmts struct {
|
type AlertStmts struct {
|
||||||
addActivity *sql.Stmt
|
addActivity *sql.Stmt
|
||||||
notifyWatchers *sql.Stmt
|
notifyWatchers *sql.Stmt
|
||||||
|
@ -50,81 +61,82 @@ func escapeTextInJson(in string) string {
|
||||||
return strings.Replace(in, "/", "\\/", -1)
|
return strings.Replace(in, "/", "\\/", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildAlert(asid int, event string, elementType string, actorID int, targetUserID int, elementID int, user User /* The current user */) (string, error) {
|
func BuildAlert(alert Alert, user User /* The current user */) (out string, err error) {
|
||||||
var targetUser *User
|
var targetUser *User
|
||||||
|
if alert.Actor == nil {
|
||||||
actor, err := Users.Get(actorID)
|
alert.Actor, err = Users.Get(alert.ActorID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_no_actor"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_no_actor"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if elementType != "forum" {
|
/*if alert.ElementType != "forum" {
|
||||||
targetUser, err = users.Get(targetUserID)
|
targetUser, err = users.Get(alert.TargetUserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LocalErrorJS("Unable to find the target user",w,r)
|
LocalErrorJS("Unable to find the target user",w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if event == "friend_invite" {
|
if alert.Event == "friend_invite" {
|
||||||
return buildAlertString(phrases.GetTmplPhrase("alerts.new_friend_invite"), []string{actor.Name}, actor.Link, actor.Avatar, asid), nil
|
return buildAlertString(phrases.GetTmplPhrase("alerts.new_friend_invite"), []string{alert.Actor.Name}, alert.Actor.Link, alert.Actor.Avatar, alert.ASID), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not that many events for us to handle in a forum
|
// Not that many events for us to handle in a forum
|
||||||
if elementType == "forum" {
|
if alert.ElementType == "forum" {
|
||||||
if event == "reply" {
|
if alert.Event == "reply" {
|
||||||
topic, err := Topics.Get(elementID)
|
topic, err := Topics.Get(alert.ElementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
DebugLogf("Unable to find linked topic %d", elementID)
|
DebugLogf("Unable to find linked topic %d", alert.ElementID)
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
|
||||||
}
|
}
|
||||||
// Store the forum ID in the targetUser column instead of making a new one? o.O
|
// Store the forum ID in the targetUser column instead of making a new one? o.O
|
||||||
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
|
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
|
||||||
return buildAlertString(phrases.GetTmplPhrase("alerts.forum_new_topic"), []string{actor.Name, topic.Title}, topic.Link, actor.Avatar, asid), nil
|
return buildAlertString(phrases.GetTmplPhrase("alerts.forum_new_topic"), []string{alert.Actor.Name, topic.Title}, topic.Link, alert.Actor.Avatar, alert.ASID), nil
|
||||||
}
|
}
|
||||||
return buildAlertString(phrases.GetTmplPhrase("alerts.forum_unknown_action"), []string{actor.Name}, "", actor.Avatar, asid), nil
|
return buildAlertString(phrases.GetTmplPhrase("alerts.forum_unknown_action"), []string{alert.Actor.Name}, "", alert.Actor.Avatar, alert.ASID), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var url, area string
|
var url, area string
|
||||||
var phraseName = "alerts." + elementType
|
var phraseName = "alerts." + alert.ElementType
|
||||||
switch elementType {
|
switch alert.ElementType {
|
||||||
case "topic":
|
case "topic":
|
||||||
topic, err := Topics.Get(elementID)
|
topic, err := Topics.Get(alert.ElementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
DebugLogf("Unable to find linked topic %d", elementID)
|
DebugLogf("Unable to find linked topic %d", alert.ElementID)
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic"))
|
||||||
}
|
}
|
||||||
url = topic.Link
|
url = topic.Link
|
||||||
area = topic.Title
|
area = topic.Title
|
||||||
if targetUserID == user.ID {
|
if alert.TargetUserID == user.ID {
|
||||||
phraseName += "_own"
|
phraseName += "_own"
|
||||||
}
|
}
|
||||||
case "user":
|
case "user":
|
||||||
targetUser, err = Users.Get(elementID)
|
targetUser, err = Users.Get(alert.ElementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
DebugLogf("Unable to find target user %d", elementID)
|
DebugLogf("Unable to find target user %d", alert.ElementID)
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_no_target_user"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_no_target_user"))
|
||||||
}
|
}
|
||||||
area = targetUser.Name
|
area = targetUser.Name
|
||||||
url = targetUser.Link
|
url = targetUser.Link
|
||||||
if targetUserID == user.ID {
|
if alert.TargetUserID == user.ID {
|
||||||
phraseName += "_own"
|
phraseName += "_own"
|
||||||
}
|
}
|
||||||
case "post":
|
case "post":
|
||||||
topic, err := TopicByReplyID(elementID)
|
topic, err := TopicByReplyID(alert.ElementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic_by_reply"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic_by_reply"))
|
||||||
}
|
}
|
||||||
url = topic.Link
|
url = topic.Link
|
||||||
area = topic.Title
|
area = topic.Title
|
||||||
if targetUserID == user.ID {
|
if alert.TargetUserID == user.ID {
|
||||||
phraseName += "_own"
|
phraseName += "_own"
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_invalid_elementtype"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_invalid_elementtype"))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch event {
|
switch alert.Event {
|
||||||
case "like":
|
case "like":
|
||||||
phraseName += "_like"
|
phraseName += "_like"
|
||||||
case "mention":
|
case "mention":
|
||||||
|
@ -133,7 +145,7 @@ func BuildAlert(asid int, event string, elementType string, actorID int, targetU
|
||||||
phraseName += "_reply"
|
phraseName += "_reply"
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildAlertString(phrases.GetTmplPhrase(phraseName), []string{actor.Name, area}, url, actor.Avatar, asid), nil
|
return buildAlertString(phrases.GetTmplPhrase(phraseName), []string{alert.Actor.Name, area}, url, alert.Actor.Avatar, alert.ASID), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildAlertString(msg string, sub []string, path string, avatar string, asid int) string {
|
func buildAlertString(msg string, sub []string, path string, avatar string, asid int) string {
|
||||||
|
@ -160,22 +172,25 @@ func AddActivityAndNotifyAll(actor int, targetUser int, event string, elementTyp
|
||||||
return NotifyWatchers(lastID)
|
return NotifyWatchers(lastID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddActivityAndNotifyTarget(actor int, targetUser int, event string, elementType string, elementID int) error {
|
func AddActivityAndNotifyTarget(alert Alert) error {
|
||||||
res, err := alertStmts.addActivity.Exec(actor, targetUser, event, elementType, elementID)
|
res, err := alertStmts.addActivity.Exec(alert.ActorID, alert.TargetUserID, alert.Event, alert.ElementType, alert.ElementID)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lastID, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = NotifyOne(targetUser, lastID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastID, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = NotifyOne(alert.TargetUserID, lastID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
alert.ASID = int(lastID)
|
||||||
|
|
||||||
// Live alerts, if the target is online and WebSockets is enabled
|
// Live alerts, if the target is online and WebSockets is enabled
|
||||||
_ = WsHub.pushAlert(targetUser, int(lastID), event, elementType, actor, targetUser, elementID)
|
_ = WsHub.pushAlert(alert.TargetUserID, alert)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,13 +236,12 @@ func notifyWatchers(asid int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var actorID, targetUserID, elementID int
|
var alert = Alert{ASID: int(asid)}
|
||||||
var event, elementType string
|
err = alertStmts.getActivityEntry.QueryRow(asid).Scan(&alert.ActorID, &alert.TargetUserID, &alert.Event, &alert.ElementType, &alert.ElementID)
|
||||||
err = alertStmts.getActivityEntry.QueryRow(asid).Scan(&actorID, &targetUserID, &event, &elementType, &elementID)
|
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = WsHub.pushAlerts(uids, int(asid), event, elementType, actorID, targetUserID, elementID)
|
_ = WsHub.pushAlerts(uids, alert)
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,13 @@ func (list SFileList) JSTmplInit() error {
|
||||||
data[braceAt] = ' ' // Blank this one too
|
data[braceAt] = ' ' // Blank this one too
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
each(" = []byte(", func(index int) {
|
||||||
|
braceAt, hasEndBrace := skipUntilIfExists(data, index, ')')
|
||||||
|
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
|
||||||
|
if hasEndBrace {
|
||||||
|
data[braceAt] = ' ' // Blank it
|
||||||
|
}
|
||||||
|
})
|
||||||
each("w.Write(", func(index int) {
|
each("w.Write(", func(index int) {
|
||||||
braceAt, hasEndBrace := skipUntilIfExists(data, index, ')')
|
braceAt, hasEndBrace := skipUntilIfExists(data, index, ')')
|
||||||
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
|
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
|
||||||
|
@ -185,6 +192,8 @@ func (list SFileList) JSTmplInit() error {
|
||||||
data = replace(data, shortName+"_tmpl_phrase_id = RegisterTmplPhraseNames([]string{", "[")
|
data = replace(data, shortName+"_tmpl_phrase_id = RegisterTmplPhraseNames([]string{", "[")
|
||||||
data = replace(data, "var plist = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "let plist = tmplPhrases[\""+tmplName+"\"];")
|
data = replace(data, "var plist = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "let plist = tmplPhrases[\""+tmplName+"\"];")
|
||||||
//data = replace(data, "var phrases = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "let phrases = tmplPhrases[\""+tmplName+"\"];\nconsole.log('tmplName:','"+tmplName+"')\nconsole.log('phrases:', phrases);")
|
//data = replace(data, "var phrases = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "let phrases = tmplPhrases[\""+tmplName+"\"];\nconsole.log('tmplName:','"+tmplName+"')\nconsole.log('phrases:', phrases);")
|
||||||
|
data = replace(data, "var cached_var_", "let cached_var_")
|
||||||
|
data = replace(data, " = []byte(", " = ")
|
||||||
data = replace(data, "if ", "if(")
|
data = replace(data, "if ", "if(")
|
||||||
data = replace(data, "return nil", "return out")
|
data = replace(data, "return nil", "return out")
|
||||||
data = replace(data, " )", ")")
|
data = replace(data, " )", ")")
|
||||||
|
|
|
@ -96,9 +96,9 @@ func HandleExpiredScheduledGroups() error {
|
||||||
// TODO: Does this even work?
|
// TODO: Does this even work?
|
||||||
func HandleServerSync() error {
|
func HandleServerSync() error {
|
||||||
// We don't want to run any unnecessary queries when there is nothing to synchronise
|
// We don't want to run any unnecessary queries when there is nothing to synchronise
|
||||||
/*if Config.ServerCount > 1 {
|
if Config.ServerCount == 1 {
|
||||||
return nil
|
return nil
|
||||||
}*/
|
}
|
||||||
|
|
||||||
var lastUpdate time.Time
|
var lastUpdate time.Time
|
||||||
err := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
err := taskStmts.getSync.QueryRow().Scan(&lastUpdate)
|
||||||
|
|
|
@ -4,6 +4,11 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// For use in generated code
|
||||||
|
type FragLite struct {
|
||||||
|
Body string
|
||||||
|
}
|
||||||
|
|
||||||
type Fragment struct {
|
type Fragment struct {
|
||||||
Body string
|
Body string
|
||||||
TemplateName string
|
TemplateName string
|
||||||
|
@ -23,6 +28,7 @@ type CContext struct {
|
||||||
VarHolder string
|
VarHolder string
|
||||||
HoldReflect reflect.Value
|
HoldReflect reflect.Value
|
||||||
TemplateName string
|
TemplateName string
|
||||||
|
LoopDepth int
|
||||||
OutBuf *[]OutBufferFrame
|
OutBuf *[]OutBufferFrame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,3 +41,36 @@ func (con *CContext) PushText(body string, fragIndex int, fragOutIndex int) (ind
|
||||||
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, "text", con.TemplateName, fragIndex, fragOutIndex})
|
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, "text", con.TemplateName, fragIndex, fragOutIndex})
|
||||||
return len(*con.OutBuf) - 1
|
return len(*con.OutBuf) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (con *CContext) PushPhrase(body string, langIndex int) (index int) {
|
||||||
|
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, "lang", con.TemplateName, langIndex, nil})
|
||||||
|
return len(*con.OutBuf) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (con *CContext) StartLoop(body string) (index int) {
|
||||||
|
con.LoopDepth++
|
||||||
|
return con.Push("startloop", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (con *CContext) EndLoop(body string) (index int) {
|
||||||
|
return con.Push("endloop", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (con *CContext) StartTemplate(body string) (index int) {
|
||||||
|
*con.OutBuf = append(*con.OutBuf, OutBufferFrame{body, "starttemplate", con.TemplateName, nil, nil})
|
||||||
|
return len(*con.OutBuf) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (con *CContext) EndTemplate(body string) (index int) {
|
||||||
|
return con.Push("endtemplate", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (con *CContext) AttachVars(vars string, index int) {
|
||||||
|
outBuf := *con.OutBuf
|
||||||
|
node := outBuf[index]
|
||||||
|
if node.Type != "starttemplate" && node.Type != "startloop" {
|
||||||
|
panic("not a starttemplate node")
|
||||||
|
}
|
||||||
|
node.Body += vars
|
||||||
|
outBuf[index] = node
|
||||||
|
}
|
||||||
|
|
|
@ -189,7 +189,10 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
||||||
c.TemplateFragmentCount = make(map[string]int)
|
c.TemplateFragmentCount = make(map[string]int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startIndex := con.StartTemplate("")
|
||||||
c.rootIterate(c.templateList[fname], con)
|
c.rootIterate(c.templateList[fname], con)
|
||||||
|
con.EndTemplate("")
|
||||||
|
c.afterTemplate(con, startIndex)
|
||||||
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
||||||
|
|
||||||
_, ok := c.FragOnce[fname]
|
_, ok := c.FragOnce[fname]
|
||||||
|
@ -280,6 +283,9 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
||||||
skipBlock.Frags[frame.Extra.(int)] = skipBlock.LastCount
|
skipBlock.Frags[frame.Extra.(int)] = skipBlock.LastCount
|
||||||
}
|
}
|
||||||
writeTextFrame(frame.TemplateName, frame.Extra.(int)-skip)
|
writeTextFrame(frame.TemplateName, frame.Extra.(int)-skip)
|
||||||
|
} else if frame.Type == "varsub" || frame.Type == "cvarsub" {
|
||||||
|
c.detail(frame.Type + " frame")
|
||||||
|
fout += "w.Write(" + frame.Body + ")\n"
|
||||||
} else {
|
} else {
|
||||||
c.detail(frame.Type + " frame")
|
c.detail(frame.Type + " frame")
|
||||||
fout += frame.Body
|
fout += frame.Body
|
||||||
|
@ -425,12 +431,24 @@ func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.RangeNode) {
|
||||||
|
|
||||||
var startIf = func(item reflect.Value, useCopy bool) {
|
var startIf = func(item reflect.Value, useCopy bool) {
|
||||||
con.Push("startif", "if len("+expr+") != 0 {\n")
|
con.Push("startif", "if len("+expr+") != 0 {\n")
|
||||||
con.Push("startloop", "for _, item := range "+expr+" {\n")
|
startIndex := con.StartLoop("for _, item := range " + expr + " {\n")
|
||||||
ccon := con
|
ccon := con
|
||||||
ccon.VarHolder = "item"
|
var depth string
|
||||||
|
if ccon.VarHolder == "item" {
|
||||||
|
depth = strings.TrimPrefix(ccon.VarHolder, "item")
|
||||||
|
if depth != "" {
|
||||||
|
idepth, err := strconv.Atoi(depth)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
depth = strconv.Itoa(idepth + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ccon.VarHolder = "item" + depth
|
||||||
ccon.HoldReflect = item
|
ccon.HoldReflect = item
|
||||||
c.compileSwitch(ccon, node.List)
|
c.compileSwitch(ccon, node.List)
|
||||||
con.Push("endloop", "}\n")
|
con.EndLoop("}\n")
|
||||||
|
c.afterTemplate(con, startIndex)
|
||||||
if node.ElseList != nil {
|
if node.ElseList != nil {
|
||||||
con.Push("endif", "}")
|
con.Push("endif", "}")
|
||||||
con.Push("startelse", " else {\n")
|
con.Push("startelse", " else {\n")
|
||||||
|
@ -440,7 +458,7 @@ func (c *CTemplateSet) compileRangeNode(con CContext, node *parse.RangeNode) {
|
||||||
c.compileSwitch(ccon, node.ElseList)
|
c.compileSwitch(ccon, node.ElseList)
|
||||||
con.Push("endelse", "}\n")
|
con.Push("endelse", "}\n")
|
||||||
} else {
|
} else {
|
||||||
con.Push("endloop", "}\n")
|
con.Push("endif", "}\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,10 +1116,10 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
||||||
base = "[]byte(strconv.Itoa(" + varname + "))"
|
base = "[]byte(strconv.Itoa(" + varname + "))"
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
con.Push("startif", "if "+varname+" {\n")
|
con.Push("startif", "if "+varname+" {\n")
|
||||||
con.Push("varsub", "w.Write([]byte(\"true\"))")
|
con.Push("varsub", "[]byte(\"true\")")
|
||||||
con.Push("endif", "} ")
|
con.Push("endif", "} ")
|
||||||
con.Push("startelse", "else {\n")
|
con.Push("startelse", "else {\n")
|
||||||
con.Push("varsub", "w.Write([]byte(\"false\"))")
|
con.Push("varsub", "[]byte(\"false\")")
|
||||||
con.Push("endelse", "}\n")
|
con.Push("endelse", "}\n")
|
||||||
return
|
return
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
@ -1121,7 +1139,6 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
||||||
fmt.Println("Unknown Type:", val.Type().Name())
|
fmt.Println("Unknown Type:", val.Type().Name())
|
||||||
panic("-- I don't know what this variable's type is o.o\n")
|
panic("-- I don't know what this variable's type is o.o\n")
|
||||||
}
|
}
|
||||||
base = "w.Write(" + base + ")\n"
|
|
||||||
c.detail("base: ", base)
|
c.detail("base: ", base)
|
||||||
if assLines == "" {
|
if assLines == "" {
|
||||||
con.Push("varsub", base)
|
con.Push("varsub", base)
|
||||||
|
@ -1179,13 +1196,18 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
||||||
c.templateList[fname] = tree
|
c.templateList[fname] = tree
|
||||||
subtree := c.templateList[fname]
|
subtree := c.templateList[fname]
|
||||||
c.detail("subtree.Root", subtree.Root)
|
c.detail("subtree.Root", subtree.Root)
|
||||||
|
|
||||||
c.localVars[fname] = make(map[string]VarItemReflect)
|
c.localVars[fname] = make(map[string]VarItemReflect)
|
||||||
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
|
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
|
||||||
c.fragmentCursor[fname] = 0
|
c.fragmentCursor[fname] = 0
|
||||||
con.Push("starttemplate", "{\n")
|
|
||||||
|
var startBit, endBit string
|
||||||
|
if con.LoopDepth != 0 {
|
||||||
|
startBit = "{\n"
|
||||||
|
endBit = "}\n"
|
||||||
|
}
|
||||||
|
con.StartTemplate(startBit)
|
||||||
c.rootIterate(subtree, con)
|
c.rootIterate(subtree, con)
|
||||||
con.Push("endtemplate", "}\n")
|
con.EndTemplate(endBit)
|
||||||
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
||||||
|
|
||||||
_, ok := c.FragOnce[fname]
|
_, ok := c.FragOnce[fname]
|
||||||
|
@ -1194,6 +1216,75 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CTemplateSet) afterTemplate(con CContext, startIndex int) {
|
||||||
|
c.dumpCall("afterTemplate", con, startIndex)
|
||||||
|
defer c.retCall("afterTemplate")
|
||||||
|
|
||||||
|
var loopDepth = 0
|
||||||
|
var outBuf = *con.OutBuf
|
||||||
|
var varcounts = make(map[string]int)
|
||||||
|
var loopStart = startIndex
|
||||||
|
|
||||||
|
if outBuf[startIndex].Type == "startloop" && (len(outBuf) > startIndex+1) {
|
||||||
|
loopStart++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude varsubs within loops for now
|
||||||
|
for i := loopStart; i < len(outBuf); i++ {
|
||||||
|
item := outBuf[i]
|
||||||
|
c.detail("item:", item)
|
||||||
|
if item.Type == "startloop" {
|
||||||
|
loopDepth++
|
||||||
|
c.detail("loopDepth:", loopDepth)
|
||||||
|
} else if item.Type == "endloop" {
|
||||||
|
loopDepth--
|
||||||
|
c.detail("loopDepth:", loopDepth)
|
||||||
|
if loopDepth == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if item.Type == "varsub" && loopDepth == 0 {
|
||||||
|
count := varcounts[item.Body]
|
||||||
|
varcounts[item.Body] = count + 1
|
||||||
|
c.detail("count " + strconv.Itoa(count) + " for " + item.Body)
|
||||||
|
c.detail("loopDepth:", loopDepth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var varstr string
|
||||||
|
var i int
|
||||||
|
var varmap = make(map[string]int)
|
||||||
|
for name, count := range varcounts {
|
||||||
|
if count > 1 {
|
||||||
|
varstr += "var cached_var_" + strconv.Itoa(i) + " = " + name + "\n"
|
||||||
|
varmap[name] = i
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude varsubs within loops for now
|
||||||
|
loopDepth = 0
|
||||||
|
for i := loopStart; i < len(outBuf); i++ {
|
||||||
|
item := outBuf[i]
|
||||||
|
if item.Type == "startloop" {
|
||||||
|
loopDepth++
|
||||||
|
} else if item.Type == "endloop" {
|
||||||
|
loopDepth--
|
||||||
|
if loopDepth == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if item.Type == "varsub" && loopDepth == 0 {
|
||||||
|
index, ok := varmap[item.Body]
|
||||||
|
if ok {
|
||||||
|
item.Body = "cached_var_" + strconv.Itoa(index)
|
||||||
|
item.Type = "cvarsub"
|
||||||
|
outBuf[i] = item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
con.AttachVars(varstr, startIndex)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Should we rethink the way the log methods work or their names?
|
// TODO: Should we rethink the way the log methods work or their names?
|
||||||
|
|
||||||
func (c *CTemplateSet) detail(args ...interface{}) {
|
func (c *CTemplateSet) detail(args ...interface{}) {
|
||||||
|
|
|
@ -345,17 +345,18 @@ func TopicByReplyID(rid int) (*Topic, error) {
|
||||||
|
|
||||||
// TODO: Refactor the caller to take a Topic and a User rather than a combined TopicUser
|
// TODO: Refactor the caller to take a Topic and a User rather than a combined TopicUser
|
||||||
// TODO: Load LastReplyAt everywhere in here?
|
// TODO: Load LastReplyAt everywhere in here?
|
||||||
func GetTopicUser(tid int) (TopicUser, error) {
|
func GetTopicUser(user *User, tid int) (tu TopicUser, err error) {
|
||||||
tcache := Topics.GetCache()
|
tcache := Topics.GetCache()
|
||||||
ucache := Users.GetCache()
|
ucache := Users.GetCache()
|
||||||
if tcache != nil && ucache != nil {
|
if tcache != nil && ucache != nil {
|
||||||
topic, err := tcache.Get(tid)
|
topic, err := tcache.Get(tid)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
user, err := Users.Get(topic.CreatedBy)
|
if topic.CreatedBy != user.ID {
|
||||||
if err != nil {
|
user, err = Users.Get(topic.CreatedBy)
|
||||||
return TopicUser{ID: tid}, err
|
if err != nil {
|
||||||
|
return TopicUser{ID: tid}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We might be better off just passing separate topic and user structs to the caller?
|
// We might be better off just passing separate topic and user structs to the caller?
|
||||||
return copyTopicToTopicUser(topic, user), nil
|
return copyTopicToTopicUser(topic, user), nil
|
||||||
} else if ucache.Length() < ucache.GetCapacity() {
|
} else if ucache.Length() < ucache.GetCapacity() {
|
||||||
|
@ -363,16 +364,18 @@ func GetTopicUser(tid int) (TopicUser, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TopicUser{ID: tid}, err
|
return TopicUser{ID: tid}, err
|
||||||
}
|
}
|
||||||
user, err := Users.Get(topic.CreatedBy)
|
if topic.CreatedBy != user.ID {
|
||||||
if err != nil {
|
user, err = Users.Get(topic.CreatedBy)
|
||||||
return TopicUser{ID: tid}, err
|
if err != nil {
|
||||||
|
return TopicUser{ID: tid}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return copyTopicToTopicUser(topic, user), nil
|
return copyTopicToTopicUser(topic, user), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tu := TopicUser{ID: tid}
|
tu = TopicUser{ID: tid}
|
||||||
err := topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.Poll, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
|
err = topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.Poll, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
|
||||||
tu.Avatar, tu.MicroAvatar = BuildAvatar(tu.CreatedBy, tu.Avatar)
|
tu.Avatar, tu.MicroAvatar = BuildAvatar(tu.CreatedBy, tu.Avatar)
|
||||||
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
|
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
|
||||||
tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy)
|
tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy)
|
||||||
|
|
|
@ -340,21 +340,19 @@ func (hub *WsHubImpl) PushMessage(targetUser int, msg string) error {
|
||||||
return wsUser.WriteAll(msg)
|
return wsUser.WriteAll(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WsHubImpl) pushAlert(targetUser int, asid int, event string, elementType string, actorID int, targetUserID int, elementID int) error {
|
func (hub *WsHubImpl) pushAlert(targetUser int, alert Alert) error {
|
||||||
wsUser, err := hub.getUser(targetUser)
|
wsUser, err := hub.getUser(targetUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
astr, err := BuildAlert(alert, *wsUser.User)
|
||||||
alert, err := BuildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return wsUser.WriteAll(astr)
|
||||||
return wsUser.WriteAll(alert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WsHubImpl) pushAlerts(users []int, asid int, event string, elementType string, actorID int, targetUserID int, elementID int) error {
|
func (hub *WsHubImpl) pushAlerts(users []int, alert Alert) error {
|
||||||
wsUsers, err := hub.getUsers(users)
|
wsUsers, err := hub.getUsers(users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -365,8 +363,7 @@ func (hub *WsHubImpl) pushAlerts(users []int, asid int, event string, elementTyp
|
||||||
if wsUser == nil {
|
if wsUser == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
alert, err := BuildAlert(alert, *wsUser.User)
|
||||||
alert, err := BuildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
12
main.go
12
main.go
|
@ -377,7 +377,7 @@ func main() {
|
||||||
go tickLoop(thumbChan, halfSecondTicker, secondTicker, fifteenMinuteTicker, hourTicker)
|
go tickLoop(thumbChan, halfSecondTicker, secondTicker, fifteenMinuteTicker, hourTicker)
|
||||||
|
|
||||||
// Resource Management Goroutine
|
// Resource Management Goroutine
|
||||||
/*go func() {
|
go func() {
|
||||||
ucache := common.Users.GetCache()
|
ucache := common.Users.GetCache()
|
||||||
tcache := common.Topics.GetCache()
|
tcache := common.Topics.GetCache()
|
||||||
if ucache == nil && tcache == nil {
|
if ucache == nil && tcache == nil {
|
||||||
|
@ -393,17 +393,15 @@ func main() {
|
||||||
if ucache != nil {
|
if ucache != nil {
|
||||||
ucap := ucache.GetCapacity()
|
ucap := ucache.GetCapacity()
|
||||||
if ucache.Length() <= ucap || common.Users.GlobalCount() <= ucap {
|
if ucache.Length() <= ucap || common.Users.GlobalCount() <= ucap {
|
||||||
countNotDealloc = false
|
couldNotDealloc = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
lastEvictedCount = ucache.DeallocOverflow(countNotDealloc)
|
lastEvictedCount = ucache.DeallocOverflow(couldNotDealloc)
|
||||||
countNotDealloc = (lastEvictedCount == 0)
|
couldNotDealloc = (lastEvictedCount == 0)
|
||||||
} else {
|
|
||||||
countNotDealloc = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()*/
|
}()
|
||||||
|
|
||||||
log.Print("Initialising the router")
|
log.Print("Initialising the router")
|
||||||
router, err = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
router, err = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||||
|
|
134
misc_test.go
134
misc_test.go
|
@ -1213,137 +1213,3 @@ func TestWordCount(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPreparser(t *testing.T) {
|
|
||||||
var msgList = &METriList{nil}
|
|
||||||
|
|
||||||
// Note: The open tag is evaluated without knowledge of the close tag for efficiency and simplicity, so the parser autofills the associated close tag when it finds an open tag without a partner
|
|
||||||
msgList.Add("", "")
|
|
||||||
msgList.Add(" ", "")
|
|
||||||
msgList.Add(" hi", "hi")
|
|
||||||
msgList.Add("hi ", "hi")
|
|
||||||
msgList.Add("hi", "hi")
|
|
||||||
msgList.Add(":grinning:", "😀")
|
|
||||||
msgList.Add("😀", "😀")
|
|
||||||
msgList.Add(" ", "")
|
|
||||||
msgList.Add("<p>", "")
|
|
||||||
msgList.Add("</p>", "")
|
|
||||||
msgList.Add("<p></p>", "")
|
|
||||||
|
|
||||||
msgList.Add("<", "<")
|
|
||||||
msgList.Add(">", ">")
|
|
||||||
msgList.Add("<meow>", "<meow>")
|
|
||||||
msgList.Add("<", "&lt;")
|
|
||||||
msgList.Add("&", "&")
|
|
||||||
|
|
||||||
// Note: strings.TrimSpace strips newlines, if there's nothing before or after them
|
|
||||||
msgList.Add("<br>", "")
|
|
||||||
msgList.Add("<br />", "")
|
|
||||||
msgList.Add("\\n", "\n", "")
|
|
||||||
msgList.Add("\\n\\n", "\n\n", "")
|
|
||||||
msgList.Add("\\n\\n\\n", "\n\n\n", "")
|
|
||||||
msgList.Add("\\r\\n", "\r\n", "") // Windows style line ending
|
|
||||||
msgList.Add("\\n\\r", "\n\r", "")
|
|
||||||
|
|
||||||
msgList.Add("ho<br>ho", "ho\n\nho")
|
|
||||||
msgList.Add("ho<br />ho", "ho\n\nho")
|
|
||||||
msgList.Add("ho\\nho", "ho\nho", "ho\nho")
|
|
||||||
msgList.Add("ho\\n\\nho", "ho\n\nho", "ho\n\nho")
|
|
||||||
//msgList.Add("ho\\n\\n\\n\\nho", "ho\n\n\n\nho", "ho\n\n\nho")
|
|
||||||
msgList.Add("ho\\r\\nho", "ho\r\nho", "ho\nho") // Windows style line ending
|
|
||||||
msgList.Add("ho\\n\\rho", "ho\n\rho", "ho\nho")
|
|
||||||
|
|
||||||
msgList.Add("<b></b>", "<strong></strong>")
|
|
||||||
msgList.Add("<b>hi</b>", "<strong>hi</strong>")
|
|
||||||
msgList.Add("<s>hi</s>", "<del>hi</del>")
|
|
||||||
msgList.Add("<del>hi</del>", "<del>hi</del>")
|
|
||||||
msgList.Add("<u>hi</u>", "<u>hi</u>")
|
|
||||||
msgList.Add("<em>hi</em>", "<em>hi</em>")
|
|
||||||
msgList.Add("<i>hi</i>", "<em>hi</em>")
|
|
||||||
msgList.Add("<strong>hi</strong>", "<strong>hi</strong>")
|
|
||||||
msgList.Add("<b><i>hi</i></b>", "<strong><em>hi</em></strong>")
|
|
||||||
msgList.Add("<strong><em>hi</em></strong>", "<strong><em>hi</em></strong>")
|
|
||||||
msgList.Add("<b><i><b>hi</b></i></b>", "<strong><em><strong>hi</strong></em></strong>")
|
|
||||||
msgList.Add("<strong><em><strong>hi</strong></em></strong>", "<strong><em><strong>hi</strong></em></strong>")
|
|
||||||
msgList.Add("<div>hi</div>", "<div>hi</div>")
|
|
||||||
msgList.Add("<span>hi</span>", "hi") // This is stripped since the editor (Trumbowyg) likes blasting useless spans
|
|
||||||
msgList.Add("<span >hi</span>", "hi")
|
|
||||||
msgList.Add("<span style='background-color: yellow;'>hi</span>", "hi")
|
|
||||||
msgList.Add("<span style='background-color: yellow;'>>hi</span>", ">hi")
|
|
||||||
msgList.Add("<b>hi", "<strong>hi</strong>")
|
|
||||||
msgList.Add("hi</b>", "hi</b>")
|
|
||||||
msgList.Add("</b>", "</b>")
|
|
||||||
msgList.Add("</del>", "</del>")
|
|
||||||
msgList.Add("</strong>", "</strong>")
|
|
||||||
msgList.Add("<b>", "<strong></strong>")
|
|
||||||
msgList.Add("<span style='background-color: yellow;'>hi", "hi")
|
|
||||||
msgList.Add("hi</span>", "hi")
|
|
||||||
msgList.Add("</span>", "")
|
|
||||||
msgList.Add("<span></span>", "")
|
|
||||||
msgList.Add("<span ></span>", "")
|
|
||||||
msgList.Add("<></>", "<></>")
|
|
||||||
msgList.Add("</><>", "</><>")
|
|
||||||
msgList.Add("<>", "<>")
|
|
||||||
msgList.Add("</>", "</>")
|
|
||||||
msgList.Add("@", "@")
|
|
||||||
msgList.Add("@Admin", "@1")
|
|
||||||
msgList.Add("@Bah", "@Bah")
|
|
||||||
msgList.Add(" @Admin", "@1")
|
|
||||||
msgList.Add("\n@Admin", "@1")
|
|
||||||
msgList.Add("@Admin\n", "@1")
|
|
||||||
msgList.Add("@Admin\ndd", "@1\ndd")
|
|
||||||
msgList.Add("d@Admin", "d@Admin")
|
|
||||||
//msgList.Add("byte 0", string([]byte{0}), "")
|
|
||||||
msgList.Add("byte 'a'", string([]byte{'a'}), "a")
|
|
||||||
//msgList.Add("byte 255", string([]byte{255}), "")
|
|
||||||
//msgList.Add("rune 0", string([]rune{0}), "")
|
|
||||||
// TODO: Do a test with invalid UTF-8 input
|
|
||||||
|
|
||||||
for _, item := range msgList.Items {
|
|
||||||
res := common.PreparseMessage(item.Msg)
|
|
||||||
if res != item.Expects {
|
|
||||||
if item.Name != "" {
|
|
||||||
t.Error("Name: ", item.Name)
|
|
||||||
}
|
|
||||||
t.Error("Testing string '" + item.Msg + "'")
|
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
|
||||||
//t.Error("Ouput in bytes:", []byte(res))
|
|
||||||
t.Error("Expected:", "'"+item.Expects+"'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParser(t *testing.T) {
|
|
||||||
var msgList = &METriList{nil}
|
|
||||||
|
|
||||||
msgList.Add("//github.com/Azareal/Gosora", "<a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a>")
|
|
||||||
msgList.Add("https://github.com/Azareal/Gosora", "<a href='https://github.com/Azareal/Gosora'>https://github.com/Azareal/Gosora</a>")
|
|
||||||
msgList.Add("http://github.com/Azareal/Gosora", "<a href='http://github.com/Azareal/Gosora'>http://github.com/Azareal/Gosora</a>")
|
|
||||||
msgList.Add("//github.com/Azareal/Gosora\n", "<a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a><br>")
|
|
||||||
msgList.Add("\n//github.com/Azareal/Gosora", "<br><a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a>")
|
|
||||||
msgList.Add("\n//github.com/Azareal/Gosora\n", "<br><a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a><br>")
|
|
||||||
msgList.Add("//github.com/Azareal/Gosora\n//github.com/Azareal/Gosora", "<a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a><br><a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a>")
|
|
||||||
msgList.Add("//github.com/Azareal/Gosora\n\n//github.com/Azareal/Gosora", "<a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a><br><br><a href='//github.com/Azareal/Gosora'>//github.com/Azareal/Gosora</a>")
|
|
||||||
msgList.Add("//"+common.Site.URL, "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a>")
|
|
||||||
msgList.Add("//"+common.Site.URL+"\n", "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a><br>")
|
|
||||||
msgList.Add("//"+common.Site.URL+"\n//"+common.Site.URL, "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a><br><a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a>")
|
|
||||||
|
|
||||||
msgList.Add("#tid-1", "<a href='/topic/1'>#tid-1</a>")
|
|
||||||
msgList.Add("https://github.com/Azareal/Gosora/#tid-1", "<a href='https://github.com/Azareal/Gosora/#tid-1'>https://github.com/Azareal/Gosora/#tid-1</a>")
|
|
||||||
msgList.Add("#fid-1", "<a href='/forum/1'>#fid-1</a>")
|
|
||||||
msgList.Add("@1", "<a href='/user/admin.1' class='mention'>@Admin</a>")
|
|
||||||
msgList.Add("@0", "<span style='color: red;'>[Invalid Profile]</span>")
|
|
||||||
msgList.Add("@-1", "<span style='color: red;'>[Invalid Profile]</span>1")
|
|
||||||
|
|
||||||
for _, item := range msgList.Items {
|
|
||||||
res := common.ParseMessage(item.Msg, 1, "forums")
|
|
||||||
if res != item.Expects {
|
|
||||||
if item.Name != "" {
|
|
||||||
t.Error("Name: ", item.Name)
|
|
||||||
}
|
|
||||||
t.Error("Testing string '" + item.Msg + "'")
|
|
||||||
t.Error("Bad output:", "'"+res+"'")
|
|
||||||
t.Error("Expected:", "'"+item.Expects+"'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azareal/Gosora/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPreparser(t *testing.T) {
|
||||||
|
var msgList = &METriList{nil}
|
||||||
|
|
||||||
|
// Note: The open tag is evaluated without knowledge of the close tag for efficiency and simplicity, so the parser autofills the associated close tag when it finds an open tag without a partner
|
||||||
|
msgList.Add("", "")
|
||||||
|
msgList.Add(" ", "")
|
||||||
|
msgList.Add(" hi", "hi")
|
||||||
|
msgList.Add("hi ", "hi")
|
||||||
|
msgList.Add("hi", "hi")
|
||||||
|
msgList.Add(":grinning:", "😀")
|
||||||
|
msgList.Add("😀", "😀")
|
||||||
|
msgList.Add(" ", "")
|
||||||
|
msgList.Add("<p>", "")
|
||||||
|
msgList.Add("</p>", "")
|
||||||
|
msgList.Add("<p></p>", "")
|
||||||
|
|
||||||
|
msgList.Add("<", "<")
|
||||||
|
msgList.Add(">", ">")
|
||||||
|
msgList.Add("<meow>", "<meow>")
|
||||||
|
msgList.Add("<", "&lt;")
|
||||||
|
msgList.Add("&", "&")
|
||||||
|
|
||||||
|
// Note: strings.TrimSpace strips newlines, if there's nothing before or after them
|
||||||
|
msgList.Add("<br>", "")
|
||||||
|
msgList.Add("<br />", "")
|
||||||
|
msgList.Add("\\n", "\n", "")
|
||||||
|
msgList.Add("\\n\\n", "\n\n", "")
|
||||||
|
msgList.Add("\\n\\n\\n", "\n\n\n", "")
|
||||||
|
msgList.Add("\\r\\n", "\r\n", "") // Windows style line ending
|
||||||
|
msgList.Add("\\n\\r", "\n\r", "")
|
||||||
|
|
||||||
|
msgList.Add("ho<br>ho", "ho\n\nho")
|
||||||
|
msgList.Add("ho<br />ho", "ho\n\nho")
|
||||||
|
msgList.Add("ho\\nho", "ho\nho", "ho\nho")
|
||||||
|
msgList.Add("ho\\n\\nho", "ho\n\nho", "ho\n\nho")
|
||||||
|
//msgList.Add("ho\\n\\n\\n\\nho", "ho\n\n\n\nho", "ho\n\n\nho")
|
||||||
|
msgList.Add("ho\\r\\nho", "ho\r\nho", "ho\nho") // Windows style line ending
|
||||||
|
msgList.Add("ho\\n\\rho", "ho\n\rho", "ho\nho")
|
||||||
|
|
||||||
|
msgList.Add("<b></b>", "<strong></strong>")
|
||||||
|
msgList.Add("<b>hi</b>", "<strong>hi</strong>")
|
||||||
|
msgList.Add("<s>hi</s>", "<del>hi</del>")
|
||||||
|
msgList.Add("<del>hi</del>", "<del>hi</del>")
|
||||||
|
msgList.Add("<u>hi</u>", "<u>hi</u>")
|
||||||
|
msgList.Add("<em>hi</em>", "<em>hi</em>")
|
||||||
|
msgList.Add("<i>hi</i>", "<em>hi</em>")
|
||||||
|
msgList.Add("<strong>hi</strong>", "<strong>hi</strong>")
|
||||||
|
msgList.Add("<b><i>hi</i></b>", "<strong><em>hi</em></strong>")
|
||||||
|
msgList.Add("<strong><em>hi</em></strong>", "<strong><em>hi</em></strong>")
|
||||||
|
msgList.Add("<b><i><b>hi</b></i></b>", "<strong><em><strong>hi</strong></em></strong>")
|
||||||
|
msgList.Add("<strong><em><strong>hi</strong></em></strong>", "<strong><em><strong>hi</strong></em></strong>")
|
||||||
|
msgList.Add("<div>hi</div>", "<div>hi</div>")
|
||||||
|
msgList.Add("<span>hi</span>", "hi") // This is stripped since the editor (Trumbowyg) likes blasting useless spans
|
||||||
|
msgList.Add("<span >hi</span>", "hi")
|
||||||
|
msgList.Add("<span style='background-color: yellow;'>hi</span>", "hi")
|
||||||
|
msgList.Add("<span style='background-color: yellow;'>>hi</span>", ">hi")
|
||||||
|
msgList.Add("<b>hi", "<strong>hi</strong>")
|
||||||
|
msgList.Add("hi</b>", "hi</b>")
|
||||||
|
msgList.Add("</b>", "</b>")
|
||||||
|
msgList.Add("</del>", "</del>")
|
||||||
|
msgList.Add("</strong>", "</strong>")
|
||||||
|
msgList.Add("<b>", "<strong></strong>")
|
||||||
|
msgList.Add("<span style='background-color: yellow;'>hi", "hi")
|
||||||
|
msgList.Add("hi</span>", "hi")
|
||||||
|
msgList.Add("</span>", "")
|
||||||
|
msgList.Add("<span></span>", "")
|
||||||
|
msgList.Add("<span ></span>", "")
|
||||||
|
msgList.Add("<></>", "<></>")
|
||||||
|
msgList.Add("</><>", "</><>")
|
||||||
|
msgList.Add("<>", "<>")
|
||||||
|
msgList.Add("</>", "</>")
|
||||||
|
msgList.Add("@", "@")
|
||||||
|
msgList.Add("@Admin", "@1")
|
||||||
|
msgList.Add("@Bah", "@Bah")
|
||||||
|
msgList.Add(" @Admin", "@1")
|
||||||
|
msgList.Add("\n@Admin", "@1")
|
||||||
|
msgList.Add("@Admin\n", "@1")
|
||||||
|
msgList.Add("@Admin\ndd", "@1\ndd")
|
||||||
|
msgList.Add("d@Admin", "d@Admin")
|
||||||
|
//msgList.Add("byte 0", string([]byte{0}), "")
|
||||||
|
msgList.Add("byte 'a'", string([]byte{'a'}), "a")
|
||||||
|
//msgList.Add("byte 255", string([]byte{255}), "")
|
||||||
|
//msgList.Add("rune 0", string([]rune{0}), "")
|
||||||
|
// TODO: Do a test with invalid UTF-8 input
|
||||||
|
|
||||||
|
for _, item := range msgList.Items {
|
||||||
|
res := common.PreparseMessage(item.Msg)
|
||||||
|
if res != item.Expects {
|
||||||
|
if item.Name != "" {
|
||||||
|
t.Error("Name: ", item.Name)
|
||||||
|
}
|
||||||
|
t.Error("Testing string '" + item.Msg + "'")
|
||||||
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
|
//t.Error("Ouput in bytes:", []byte(res))
|
||||||
|
t.Error("Expected:", "'"+item.Expects+"'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParser(t *testing.T) {
|
||||||
|
var msgList = &METriList{nil}
|
||||||
|
|
||||||
|
url := "github.com/Azareal/Gosora"
|
||||||
|
msgList.Add("//"+url, "<a href='//"+url+"'>//"+url+"</a>")
|
||||||
|
msgList.Add("https://"+url, "<a href='https://"+url+"'>https://"+url+"</a>")
|
||||||
|
msgList.Add("http://"+url, "<a href='http://"+url+"'>http://"+url+"</a>")
|
||||||
|
msgList.Add("//"+url+"\n", "<a href='//"+url+"'>//"+url+"</a><br>")
|
||||||
|
msgList.Add("\n//"+url, "<br><a href='//"+url+"'>//"+url+"</a>")
|
||||||
|
msgList.Add("\n//"+url+"\n", "<br><a href='//"+url+"'>//"+url+"</a><br>")
|
||||||
|
msgList.Add("//"+url+"\n//"+url, "<a href='//"+url+"'>//"+url+"</a><br><a href='//"+url+"'>//"+url+"</a>")
|
||||||
|
msgList.Add("//"+url+"\n\n//"+url, "<a href='//"+url+"'>//"+url+"</a><br><br><a href='//"+url+"'>//"+url+"</a>")
|
||||||
|
msgList.Add("//"+common.Site.URL, "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a>")
|
||||||
|
msgList.Add("//"+common.Site.URL+"\n", "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a><br>")
|
||||||
|
msgList.Add("//"+common.Site.URL+"\n//"+common.Site.URL, "<a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a><br><a href='//"+common.Site.URL+"'>//"+common.Site.URL+"</a>")
|
||||||
|
|
||||||
|
msgList.Add("#tid-1", "<a href='/topic/1'>#tid-1</a>")
|
||||||
|
msgList.Add("https://"+url+"/#tid-1", "<a href='https://"+url+"/#tid-1'>https://"+url+"/#tid-1</a>")
|
||||||
|
msgList.Add("#fid-1", "<a href='/forum/1'>#fid-1</a>")
|
||||||
|
msgList.Add("@1", "<a href='/user/admin.1' class='mention'>@Admin</a>")
|
||||||
|
msgList.Add("@0", "<span style='color: red;'>[Invalid Profile]</span>")
|
||||||
|
msgList.Add("@-1", "<span style='color: red;'>[Invalid Profile]</span>1")
|
||||||
|
|
||||||
|
for _, item := range msgList.Items {
|
||||||
|
res := common.ParseMessage(item.Msg, 1, "forums")
|
||||||
|
if res != item.Expects {
|
||||||
|
if item.Name != "" {
|
||||||
|
t.Error("Name: ", item.Name)
|
||||||
|
}
|
||||||
|
t.Error("Testing string '" + item.Msg + "'")
|
||||||
|
t.Error("Bad output:", "'"+res+"'")
|
||||||
|
t.Error("Expected:", "'"+item.Expects+"'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
routes.go
38
routes.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -65,10 +66,8 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var msglist, event, elementType string
|
var msglist string
|
||||||
var asid, actorID, targetUserID, elementID int
|
|
||||||
var msgCount int
|
var msgCount int
|
||||||
|
|
||||||
err = stmts.getActivityCountByWatcher.QueryRow(user.ID).Scan(&msgCount)
|
err = stmts.getActivityCountByWatcher.QueryRow(user.ID).Scan(&msgCount)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.PreErrorJS("Couldn't find the parent topic", w, r)
|
return common.PreErrorJS("Couldn't find the parent topic", w, r)
|
||||||
|
@ -82,22 +81,43 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
|
var actors []int
|
||||||
|
var alerts []common.Alert
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err = rows.Scan(&asid, &actorID, &targetUserID, &event, &elementType, &elementID)
|
var alert common.Alert
|
||||||
|
err = rows.Scan(&alert.ASID, &alert.ActorID, &alert.TargetUserID, &alert.Event, &alert.ElementType, &alert.ElementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJS(err, w, r)
|
return common.InternalErrorJS(err, w, r)
|
||||||
}
|
}
|
||||||
res, err := common.BuildAlert(asid, event, elementType, actorID, targetUserID, elementID, user)
|
alerts = append(alerts, alert)
|
||||||
if err != nil {
|
actors = append(actors, alert.ActorID)
|
||||||
return common.LocalErrorJS(err.Error(), w, r)
|
|
||||||
}
|
|
||||||
msglist += res + ","
|
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJS(err, w, r)
|
return common.InternalErrorJS(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Might not want to error here, if the account was deleted properly, we might want to figure out how we should handle deletions in general
|
||||||
|
list, err := common.Users.BulkGetMap(actors)
|
||||||
|
if err != nil {
|
||||||
|
return common.InternalErrorJS(err, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
for _, alert := range alerts {
|
||||||
|
alert.Actor, ok = list[alert.ActorID]
|
||||||
|
if !ok {
|
||||||
|
return common.InternalErrorJS(errors.New("No such actor"), w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := common.BuildAlert(alert, user)
|
||||||
|
if err != nil {
|
||||||
|
return common.LocalErrorJS(err.Error(), w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
msglist += res + ","
|
||||||
|
}
|
||||||
|
|
||||||
if len(msglist) != 0 {
|
if len(msglist) != 0 {
|
||||||
msglist = msglist[0 : len(msglist)-1]
|
msglist = msglist[0 : len(msglist)-1]
|
||||||
}
|
}
|
||||||
|
|
|
@ -328,7 +328,9 @@ func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user commo
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddActivityAndNotifyTarget(user.ID, profileOwner.ID, "reply", "user", profileOwner.ID)
|
// ! Be careful about leaking per-route permission state with &user
|
||||||
|
alert := common.Alert{0, user.ID, profileOwner.ID, "reply", "user", profileOwner.ID, &user}
|
||||||
|
err = common.AddActivityAndNotifyTarget(alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -462,7 +464,9 @@ func ReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User, s
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddActivityAndNotifyTarget(user.ID, reply.CreatedBy, "like", "post", rid)
|
// ! Be careful about leaking per-route permission state with &user
|
||||||
|
alert := common.Alert{0, user.ID, reply.CreatedBy, "like", "post", rid, &user}
|
||||||
|
err = common.AddActivityAndNotifyTarget(alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, header
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the topic...
|
// Get the topic...
|
||||||
topic, err := common.GetTopicUser(tid)
|
topic, err := common.GetTopicUser(&user, tid)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return common.NotFound(w, r, nil) // TODO: Can we add a simplified invocation of headerVars here? This is likely to be an extremely common NotFound
|
return common.NotFound(w, r, nil) // TODO: Can we add a simplified invocation of headerVars here? This is likely to be an extremely common NotFound
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -822,7 +822,9 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, s
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddActivityAndNotifyTarget(user.ID, topic.CreatedBy, "like", "topic", tid)
|
// ! Be careful about leaking per-route permission state with &user
|
||||||
|
alert := common.Alert{0, user.ID, topic.CreatedBy, "like", "topic", tid, &user}
|
||||||
|
err = common.AddActivityAndNotifyTarget(alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue