security: stop exempting video sites in frame-src on pages which don't have video embeds
perf: reduce allocs in action posts perf: avoid allocing a map and slice for single reply posts when it is liked and the post also has an attachment. perf: use hookgen for topic_reply_row_assign hook. perf: stop unnecessarily indirecting r in topic_reply_row_assign hook. perf: try optimising reqUserList logic in Topic.Replies()
This commit is contained in:
parent
019efc8c62
commit
304c246cb2
|
@ -44,6 +44,7 @@ type Header struct {
|
||||||
GoogSiteVerify string
|
GoogSiteVerify string
|
||||||
IsoCode string
|
IsoCode string
|
||||||
LooseCSP bool
|
LooseCSP bool
|
||||||
|
ExternalMedia bool
|
||||||
//StartedAt time.Time
|
//StartedAt time.Time
|
||||||
StartedAt int64
|
StartedAt int64
|
||||||
Elapsed1 string
|
Elapsed1 string
|
||||||
|
|
|
@ -483,10 +483,15 @@ func (ps *ParseSettings) CopyPtr() *ParseSettings {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseMessage(msg string, sectionID int, sectionType string, settings *ParseSettings, user *User) string {
|
||||||
|
msg, _ = ParseMessage2(msg,sectionID,sectionType,settings,user)
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
// TODO: We need a lot more hooks here. E.g. To add custom media types and handlers.
|
// TODO: We need a lot more hooks here. E.g. To add custom media types and handlers.
|
||||||
// TODO: Use templates to reduce the amount of boilerplate?
|
// TODO: Use templates to reduce the amount of boilerplate?
|
||||||
func ParseMessage(msg string, sectionID int, sectionType string, settings *ParseSettings, user *User) string {
|
func ParseMessage2(msg string, sectionID int, sectionType string, settings *ParseSettings, user *User) (string,bool) {
|
||||||
if settings == nil {
|
if settings == nil {
|
||||||
settings = DefaultParseSettings
|
settings = DefaultParseSettings
|
||||||
}
|
}
|
||||||
|
@ -510,7 +515,7 @@ func ParseMessage(msg string, sectionID int, sectionType string, settings *Parse
|
||||||
wordFilters, err := WordFilters.GetAll()
|
wordFilters, err := WordFilters.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return ""
|
return "", false
|
||||||
}
|
}
|
||||||
for _, f := range wordFilters {
|
for _, f := range wordFilters {
|
||||||
msg = strings.Replace(msg, f.Find, f.Replace, -1)
|
msg = strings.Replace(msg, f.Find, f.Replace, -1)
|
||||||
|
@ -519,13 +524,14 @@ func ParseMessage(msg string, sectionID int, sectionType string, settings *Parse
|
||||||
if len(msg) < 2 {
|
if len(msg) < 2 {
|
||||||
msg = strings.Replace(msg, "\n", "<br>", -1)
|
msg = strings.Replace(msg, "\n", "<br>", -1)
|
||||||
msg = GetHookTable().Sshook("parse_assign", msg)
|
msg = GetHookTable().Sshook("parse_assign", msg)
|
||||||
return msg
|
return msg, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for URLs, mentions and hashlinks in the messages...
|
// Search for URLs, mentions and hashlinks in the messages...
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
lastItem := 0
|
lastItem := 0
|
||||||
i := 0
|
i := 0
|
||||||
|
var externalHead bool
|
||||||
//var c bool
|
//var c bool
|
||||||
//fmt.Println("msg:", "'"+msg+"'")
|
//fmt.Println("msg:", "'"+msg+"'")
|
||||||
for ; len(msg) > i; i++ {
|
for ; len(msg) > i; i++ {
|
||||||
|
@ -775,6 +781,12 @@ func ParseMessage(msg string, sectionID int, sectionType string, settings *Parse
|
||||||
i += urlLen
|
i += urlLen
|
||||||
lastItem = i
|
lastItem = i
|
||||||
continue
|
continue
|
||||||
|
case ERawExternal:
|
||||||
|
sb.WriteString(media.Body)
|
||||||
|
i += urlLen
|
||||||
|
lastItem = i
|
||||||
|
externalHead = true
|
||||||
|
continue
|
||||||
case ENone:
|
case ENone:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
// TODO: Add support for media plugins
|
// TODO: Add support for media plugins
|
||||||
|
@ -820,7 +832,7 @@ func ParseMessage(msg string, sectionID int, sectionType string, settings *Parse
|
||||||
|
|
||||||
msg = strings.Replace(msg, "\n", "<br>", -1)
|
msg = strings.Replace(msg, "\n", "<br>", -1)
|
||||||
msg = GetHookTable().Sshook("parse_assign", msg)
|
msg = GetHookTable().Sshook("parse_assign", msg)
|
||||||
return msg
|
return msg, externalHead
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6, 7, 8, 6, 2, 7
|
// 6, 7, 8, 6, 2, 7
|
||||||
|
@ -993,6 +1005,7 @@ type MediaEmbed struct {
|
||||||
const (
|
const (
|
||||||
ENone = iota
|
ENone = iota
|
||||||
ERaw
|
ERaw
|
||||||
|
ERawExternal
|
||||||
EImage
|
EImage
|
||||||
AImage
|
AImage
|
||||||
AVideo
|
AVideo
|
||||||
|
@ -1073,7 +1086,7 @@ func parseMediaString(data string, settings *ParseSettings) (media MediaEmbed, o
|
||||||
if strings.HasSuffix(host, ".youtube.com") && path == "/watch" {
|
if strings.HasSuffix(host, ".youtube.com") && path == "/watch" {
|
||||||
video, ok := query["v"]
|
video, ok := query["v"]
|
||||||
if ok && len(video) >= 1 && video[0] != "" {
|
if ok && len(video) >= 1 && video[0] != "" {
|
||||||
media.Type = ERaw
|
media.Type = ERawExternal
|
||||||
// TODO: Filter the URL to make sure no nasties end up in there
|
// TODO: Filter the URL to make sure no nasties end up in there
|
||||||
media.Body = "<iframe class='postIframe'src='https://www.youtube-nocookie.com/embed/" + video[0] + "'frameborder=0 allowfullscreen></iframe>"
|
media.Body = "<iframe class='postIframe'src='https://www.youtube-nocookie.com/embed/" + video[0] + "'frameborder=0 allowfullscreen></iframe>"
|
||||||
return media, true
|
return media, true
|
||||||
|
@ -1081,7 +1094,7 @@ func parseMediaString(data string, settings *ParseSettings) (media MediaEmbed, o
|
||||||
} else if strings.HasPrefix(host, "www.nicovideo.jp") && strings.HasPrefix(path, "/watch/sm") {
|
} else if strings.HasPrefix(host, "www.nicovideo.jp") && strings.HasPrefix(path, "/watch/sm") {
|
||||||
vid, err := strconv.ParseInt(strings.TrimPrefix(path, "/watch/sm"), 10, 64)
|
vid, err := strconv.ParseInt(strings.TrimPrefix(path, "/watch/sm"), 10, 64)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
media.Type = ERaw
|
media.Type = ERawExternal
|
||||||
media.Body = "<iframe class='postIframe'src='https://embed.nicovideo.jp/watch/sm"+strconv.FormatInt(vid, 10)+"?jsapi=1&playerId=1'frameborder=0 allowfullscreen></iframe>"
|
media.Body = "<iframe class='postIframe'src='https://embed.nicovideo.jp/watch/sm"+strconv.FormatInt(vid, 10)+"?jsapi=1&playerId=1'frameborder=0 allowfullscreen></iframe>"
|
||||||
return media, true
|
return media, true
|
||||||
}
|
}
|
||||||
|
|
174
common/topic.go
174
common/topic.go
|
@ -415,8 +415,8 @@ func handleTopicReplies(umap map[int]struct{}, uid, tid int) error {
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
|
var createdBy int
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var createdBy int
|
|
||||||
err := rows.Scan(&createdBy)
|
err := rows.Scan(&createdBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -705,12 +705,16 @@ func (ru *ReplyUser) Init3(u *User, tu *TopicUser) (group *Group, err error) {
|
||||||
switch action {
|
switch action {
|
||||||
case "lock":
|
case "lock":
|
||||||
ru.ActionIcon = lockai
|
ru.ActionIcon = lockai
|
||||||
|
action = "topic.action_topic_lock"
|
||||||
case "unlock":
|
case "unlock":
|
||||||
ru.ActionIcon = unlockai
|
ru.ActionIcon = unlockai
|
||||||
|
action = "topic.action_topic_unlock"
|
||||||
case "stick":
|
case "stick":
|
||||||
ru.ActionIcon = stickai
|
ru.ActionIcon = stickai
|
||||||
|
action = "topic.action_topic_stick"
|
||||||
case "unstick":
|
case "unstick":
|
||||||
ru.ActionIcon = unstickai
|
ru.ActionIcon = unstickai
|
||||||
|
action = "topic.action_topic_unstick"
|
||||||
case "move":
|
case "move":
|
||||||
if len(aarr) == 2 {
|
if len(aarr) == 2 {
|
||||||
fid, _ := strconv.Atoi(aarr[1])
|
fid, _ := strconv.Atoi(aarr[1])
|
||||||
|
@ -725,14 +729,14 @@ func (ru *ReplyUser) Init3(u *User, tu *TopicUser) (group *Group, err error) {
|
||||||
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_default", ru.ActionType)
|
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_default", ru.ActionType)
|
||||||
return postGroup, nil
|
return postGroup, nil
|
||||||
}
|
}
|
||||||
ru.ActionType = p.GetTmplPhrasef("topic.action_topic_"+action, ru.UserLink, ru.CreatedByName)
|
ru.ActionType = p.GetTmplPhrasef(action, ru.UserLink, ru.CreatedByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return postGroup, nil
|
return postGroup, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Factor TopicUser into a *Topic and *User, as this starting to become overly complicated x.x
|
// TODO: Factor TopicUser into a *Topic and *User, as this starting to become overly complicated x.x
|
||||||
func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*ReplyUser /*, ogdesc string*/, err error) {
|
func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*ReplyUser /*, ogdesc string*/, externalHead bool, err error) {
|
||||||
var likedMap, attachMap map[int]int
|
var likedMap, attachMap map[int]int
|
||||||
var likedQueryList, attachQueryList []int
|
var likedQueryList, attachQueryList []int
|
||||||
|
|
||||||
|
@ -770,7 +774,7 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
parseSettings = user.ParseSettings
|
parseSettings = user.ParseSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ContentHtml = ParseMessage(r.Content, t.ParentID, "forums", parseSettings, user)
|
r.ContentHtml, externalHead = ParseMessage2(r.Content, t.ParentID, "forums", parseSettings, user)
|
||||||
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
|
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
|
||||||
if r.ContentHtml == r.Content {
|
if r.ContentHtml == r.Content {
|
||||||
r.ContentHtml = r.Content
|
r.ContentHtml = r.Content
|
||||||
|
@ -803,7 +807,7 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
parseSettings = user.ParseSettings
|
parseSettings = user.ParseSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ContentHtml = ParseMessage(r.Content, t.ParentID, "forums", parseSettings, user)
|
r.ContentHtml, externalHead = ParseMessage2(r.Content, t.ParentID, "forums", parseSettings, user)
|
||||||
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
|
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
|
||||||
if r.ContentHtml == r.Content {
|
if r.ContentHtml == r.Content {
|
||||||
r.ContentHtml = r.Content
|
r.ContentHtml = r.Content
|
||||||
|
@ -818,19 +822,24 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
//log.Print("reply cached serve")
|
//log.Print("reply cached serve")
|
||||||
r := &ReplyUser{ /*ClassName: "", */ Reply: *re, CreatedByName: ruser.Name, UserLink: ruser.Link, Avatar: ruser.Avatar, MicroAvatar: ruser.MicroAvatar /*URLPrefix: ruser.URLPrefix, URLName: ruser.URLName, */, Group: ruser.Group, Level: ruser.Level, Tag: ruser.Tag}
|
r := &ReplyUser{ /*ClassName: "", */ Reply: *re, CreatedByName: ruser.Name, UserLink: ruser.Link, Avatar: ruser.Avatar, MicroAvatar: ruser.MicroAvatar /*URLPrefix: ruser.URLPrefix, URLName: ruser.URLName, */, Group: ruser.Group, Level: ruser.Level, Tag: ruser.Tag}
|
||||||
if err = rf3(r); err != nil {
|
if err = rf3(r); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.LikeCount > 0 && user.Liked > 0 {
|
if r.LikeCount > 0 && user.Liked > 0 {
|
||||||
likedMap = map[int]int{r.ID: len(rlist)}
|
likedMap = map[int]int{r.ID: 0}
|
||||||
likedQueryList = []int{r.ID}
|
likedQueryList = []int{r.ID}
|
||||||
}
|
}
|
||||||
if user.Perms.EditReply && r.AttachCount > 0 {
|
if user.Perms.EditReply && r.AttachCount > 0 {
|
||||||
attachMap = map[int]int{r.ID: len(rlist)}
|
if likedMap == nil {
|
||||||
attachQueryList = []int{r.ID}
|
attachMap = map[int]int{r.ID: 0}
|
||||||
|
attachQueryList = []int{r.ID}
|
||||||
|
} else {
|
||||||
|
attachMap = likedMap
|
||||||
|
attachQueryList = likedQueryList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hTbl.VhookNoRet("topic_reply_row_assign", &r)
|
H_topic_reply_row_assign_hook(hTbl, r)
|
||||||
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
|
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
|
||||||
rlist = []*ReplyUser{r}
|
rlist = []*ReplyUser{r}
|
||||||
//log.Printf("r: %d-%d", r.ID, len(rlist)-1)
|
//log.Printf("r: %d-%d", r.ID, len(rlist)-1)
|
||||||
|
@ -856,7 +865,7 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hTbl.VhookNoRet("topic_reply_row_assign", &r)
|
H_topic_reply_row_assign_hook(hTbl, r)
|
||||||
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
|
// TODO: Use a pointer instead to make it easier to abstract this loop? What impact would this have on escape analysis?
|
||||||
rlist = append(rlist, r)
|
rlist = append(rlist, r)
|
||||||
//log.Printf("r: %d-%d", r.ID, len(rlist)-1)
|
//log.Printf("r: %d-%d", r.ID, len(rlist)-1)
|
||||||
|
@ -864,47 +873,47 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
if !user.Perms.ViewIPs && ruser != nil {
|
if !user.Perms.ViewIPs && ruser != nil {
|
||||||
rows, err := topicStmts.getReplies3.Query(t.ID, offset, Config.ItemsPerPage)
|
rows, err := topicStmts.getReplies3.Query(t.ID, offset, Config.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
r := &ReplyUser{Avatar: ruser.Avatar, MicroAvatar: ruser.MicroAvatar, UserLink: ruser.Link, CreatedByName: ruser.Name, Group: ruser.Group, Level: ruser.Level}
|
r := &ReplyUser{Avatar: ruser.Avatar, MicroAvatar: ruser.MicroAvatar, UserLink: ruser.Link, CreatedByName: ruser.Name, Group: ruser.Group, Level: ruser.Level}
|
||||||
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
if err = rf3(r); err != nil {
|
if err = rf3(r); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
rf2(r)
|
rf2(r)
|
||||||
}
|
}
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
} else if user.Perms.ViewIPs {
|
} else if user.Perms.ViewIPs {
|
||||||
rows, err := topicStmts.getReplies.Query(t.ID, offset, Config.ItemsPerPage)
|
rows, err := topicStmts.getReplies.Query(t.ID, offset, Config.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
r := &ReplyUser{}
|
r := &ReplyUser{}
|
||||||
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &r.Group /*&r.URLPrefix, &r.URLName,*/, &r.Level, &r.IP, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &r.Group /*&r.URLPrefix, &r.URLName,*/, &r.Level, &r.IP, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
if err = rf(r); err != nil {
|
if err = rf(r); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
rf2(r)
|
rf2(r)
|
||||||
}
|
}
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
} else if t.PostCount >= 20 {
|
} else if t.PostCount >= 20 {
|
||||||
rows, err := topicStmts.getReplies3.Query(t.ID, offset, Config.ItemsPerPage)
|
rows, err := topicStmts.getReplies3.Query(t.ID, offset, Config.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
reqUserList := make(map[int]bool)
|
reqUserList := make(map[int]bool)
|
||||||
|
@ -912,75 +921,110 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
r := &ReplyUser{}
|
r := &ReplyUser{}
|
||||||
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy /*&r.URLPrefix, &r.URLName,*/, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy /*&r.URLPrefix, &r.URLName,*/, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
if r.CreatedBy != t.CreatedBy && r.CreatedBy != user.ID {
|
if r.CreatedBy != t.CreatedBy && r.CreatedBy != user.ID {
|
||||||
reqUserList[r.CreatedBy] = true
|
reqUserList[r.CreatedBy] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var userList map[int]*User
|
if len(reqUserList) == 1 {
|
||||||
if len(reqUserList) > 0 {
|
var uitem *User
|
||||||
// Convert the user ID map to a slice, then bulk load the users
|
for uid, _ := range reqUserList {
|
||||||
idSlice := make([]int, len(reqUserList))
|
uitem, err = Users.Get(uid)
|
||||||
var i int
|
if err != nil {
|
||||||
for userID := range reqUserList {
|
return nil, externalHead, nil // TODO: Implement this!
|
||||||
idSlice[i] = userID
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
userList, err = Users.BulkGetMap(idSlice)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil // TODO: Implement this!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range rlist {
|
|
||||||
var u *User
|
|
||||||
if r.CreatedBy == t.CreatedBy {
|
|
||||||
r.CreatedByName = t.CreatedByName
|
|
||||||
r.Avatar = t.Avatar
|
|
||||||
r.MicroAvatar = t.MicroAvatar
|
|
||||||
r.Group = t.Group
|
|
||||||
r.Level = t.Level
|
|
||||||
} else {
|
|
||||||
if r.CreatedBy == user.ID {
|
|
||||||
u = user
|
|
||||||
} else {
|
|
||||||
u = userList[r.CreatedBy]
|
|
||||||
}
|
}
|
||||||
r.CreatedByName = u.Name
|
|
||||||
r.Avatar = u.Avatar
|
|
||||||
r.MicroAvatar = u.MicroAvatar
|
|
||||||
r.Group = u.Group
|
|
||||||
r.Level = u.Level
|
|
||||||
}
|
}
|
||||||
if err = rf(r); err != nil {
|
for _, r := range rlist {
|
||||||
return nil, err
|
if r.CreatedBy == t.CreatedBy {
|
||||||
|
r.CreatedByName = t.CreatedByName
|
||||||
|
r.Avatar = t.Avatar
|
||||||
|
r.MicroAvatar = t.MicroAvatar
|
||||||
|
r.Group = t.Group
|
||||||
|
r.Level = t.Level
|
||||||
|
} else {
|
||||||
|
var u *User
|
||||||
|
if r.CreatedBy == user.ID {
|
||||||
|
u = user
|
||||||
|
} else {
|
||||||
|
u = uitem
|
||||||
|
}
|
||||||
|
r.CreatedByName = u.Name
|
||||||
|
r.Avatar = u.Avatar
|
||||||
|
r.MicroAvatar = u.MicroAvatar
|
||||||
|
r.Group = u.Group
|
||||||
|
r.Level = u.Level
|
||||||
|
}
|
||||||
|
if err = rf(r); err != nil {
|
||||||
|
return nil, externalHead, err
|
||||||
|
}
|
||||||
|
rf2(r)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var userList map[int]*User
|
||||||
|
if len(reqUserList) > 0 {
|
||||||
|
// Convert the user ID map to a slice, then bulk load the users
|
||||||
|
idSlice := make([]int, len(reqUserList))
|
||||||
|
var i int
|
||||||
|
for userID := range reqUserList {
|
||||||
|
idSlice[i] = userID
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
userList, err = Users.BulkGetMap(idSlice)
|
||||||
|
if err != nil {
|
||||||
|
return nil, externalHead, nil // TODO: Implement this!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rlist {
|
||||||
|
if r.CreatedBy == t.CreatedBy {
|
||||||
|
r.CreatedByName = t.CreatedByName
|
||||||
|
r.Avatar = t.Avatar
|
||||||
|
r.MicroAvatar = t.MicroAvatar
|
||||||
|
r.Group = t.Group
|
||||||
|
r.Level = t.Level
|
||||||
|
} else {
|
||||||
|
var u *User
|
||||||
|
if r.CreatedBy == user.ID {
|
||||||
|
u = user
|
||||||
|
} else {
|
||||||
|
u = userList[r.CreatedBy]
|
||||||
|
}
|
||||||
|
r.CreatedByName = u.Name
|
||||||
|
r.Avatar = u.Avatar
|
||||||
|
r.MicroAvatar = u.MicroAvatar
|
||||||
|
r.Group = u.Group
|
||||||
|
r.Level = u.Level
|
||||||
|
}
|
||||||
|
if err = rf(r); err != nil {
|
||||||
|
return nil, externalHead, err
|
||||||
|
}
|
||||||
|
rf2(r)
|
||||||
}
|
}
|
||||||
rf2(r)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rows, err := topicStmts.getReplies2.Query(t.ID, offset, Config.ItemsPerPage)
|
rows, err := topicStmts.getReplies2.Query(t.ID, offset, Config.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
r := &ReplyUser{}
|
r := &ReplyUser{}
|
||||||
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &r.Group /*&r.URLPrefix, &r.URLName,*/, &r.Level, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
err := rows.Scan(&r.ID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.Avatar, &r.CreatedByName, &r.Group /*&r.URLPrefix, &r.URLName,*/, &r.Level, &r.LikeCount, &r.AttachCount, &r.ActionType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
if err = rf(r); err != nil {
|
if err = rf(r); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
rf2(r)
|
rf2(r)
|
||||||
}
|
}
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -989,7 +1033,7 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
if user.Liked > 0 && len(likedQueryList) > 0 /*&& user.LastLiked <= time.Now()*/ {
|
if user.Liked > 0 && len(likedQueryList) > 0 /*&& user.LastLiked <= time.Now()*/ {
|
||||||
eids, err := Likes.BulkExists(likedQueryList, user.ID, "replies")
|
eids, err := Likes.BulkExists(likedQueryList, user.ID, "replies")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
for _, eid := range eids {
|
for _, eid := range eids {
|
||||||
rlist[likedMap[eid]].Liked = true
|
rlist[likedMap[eid]].Liked = true
|
||||||
|
@ -1000,7 +1044,7 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
//log.Printf("attachQueryList: %+v\n", attachQueryList)
|
//log.Printf("attachQueryList: %+v\n", attachQueryList)
|
||||||
amap, err := Attachments.BulkMiniGetList("replies", attachQueryList)
|
amap, err := Attachments.BulkMiniGetList("replies", attachQueryList)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return nil, err
|
return nil, externalHead, err
|
||||||
}
|
}
|
||||||
//log.Printf("amap: %+v\n", amap)
|
//log.Printf("amap: %+v\n", amap)
|
||||||
//log.Printf("attachMap: %+v\n", attachMap)
|
//log.Printf("attachMap: %+v\n", attachMap)
|
||||||
|
@ -1015,7 +1059,7 @@ func (t *TopicUser) Replies(offset int /*pFrag int, */, user *User) (rlist []*Re
|
||||||
|
|
||||||
//hTbl.VhookNoRet("topic_reply_end", &rlist)
|
//hTbl.VhookNoRet("topic_reply_end", &rlist)
|
||||||
|
|
||||||
return rlist, nil
|
return rlist, externalHead, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
|
|
|
@ -94,7 +94,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Cache ContentHTML when possible?
|
// TODO: Cache ContentHTML when possible?
|
||||||
topic.ContentHTML = c.ParseMessage(topic.Content, topic.ParentID, "forums", parseSettings, user)
|
topic.ContentHTML, h.ExternalMedia = c.ParseMessage2(topic.Content, topic.ParentID, "forums", parseSettings, user)
|
||||||
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
|
// TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do.
|
||||||
if topic.ContentHTML == topic.Content {
|
if topic.ContentHTML == topic.Content {
|
||||||
topic.ContentHTML = topic.Content
|
topic.ContentHTML = topic.Content
|
||||||
|
@ -152,13 +152,16 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header
|
||||||
if strings.HasPrefix(r.URL.Fragment, "post-") {
|
if strings.HasPrefix(r.URL.Fragment, "post-") {
|
||||||
pFrag, _ = strconv.Atoi(strings.TrimPrefix(r.URL.Fragment, "post-"))
|
pFrag, _ = strconv.Atoi(strings.TrimPrefix(r.URL.Fragment, "post-"))
|
||||||
}*/
|
}*/
|
||||||
rlist, err := topic.Replies(offset /* pFrag,*/, user)
|
rlist, externalHead, err := topic.Replies(offset /* pFrag,*/, user)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return c.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user)
|
return c.LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.", w, r, user)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
tpage.ItemList = rlist
|
tpage.ItemList = rlist
|
||||||
|
if externalHead {
|
||||||
|
h.ExternalMedia = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Zone = "view_topic"
|
h.Zone = "view_topic"
|
||||||
|
|
Loading…
Reference in New Issue