From 300defd46091e21df67d502f2a72cf21302eea20 Mon Sep 17 00:00:00 2001 From: Azareal Date: Wed, 24 Mar 2021 22:07:46 +1000 Subject: [PATCH] Add TopicListIntTest interface. Use inqbuild2 in DefaultTopicList.Tick. Avoid doing a full paginator calculation in the topic list when the result is obvious. Add defaultPagi and setForumList methods to DefaultTopicList. Cache two more items in qcache. Use a string builder in inqbuild and inqbuildstr. Add the inqbuild2 function. Reduce boilerplate in topic.go --- common/common.go | 42 ++++++++++----- common/topic_list.go | 108 +++++++++++++++++++-------------------- routes/topic.go | 118 +++++++++++++++++++++---------------------- 3 files changed, 142 insertions(+), 126 deletions(-) diff --git a/common/common.go b/common/common.go index 7d3aa341..2bb8672f 100644 --- a/common/common.go +++ b/common/common.go @@ -12,7 +12,7 @@ import ( "log" "os" "strconv" - //"sync" + "strings" "sync/atomic" "time" @@ -223,10 +223,10 @@ func eachall(stmt *sql.Stmt, f func(r *sql.Rows) error) error { return rows.Err() } -var qcache = []string{0: "?", 1: "?,?", 2: "?,?,?", 3: "?,?,?,?", 4: "?,?,?,?,?", 5: "?,?,?,?,?,?"} +var qcache = []string{0: "?", 1: "?,?", 2: "?,?,?", 3: "?,?,?,?", 4: "?,?,?,?,?", 5: "?,?,?,?,?,?", 6: "?,?,?,?,?,?,?", 7: "?,?,?,?,?,?,?,?"} func inqbuild(ids []int) ([]interface{}, string) { - if len(ids) < 5 { + if len(ids) < 7 { idList := make([]interface{}, len(ids)) for i, id := range ids { idList[i] = strconv.Itoa(id) @@ -234,21 +234,38 @@ func inqbuild(ids []int) ([]interface{}, string) { return idList, qcache[len(ids)-1] } - var q string + var sb strings.Builder + sb.Grow((len(ids) * 2) - 1) idList := make([]interface{}, len(ids)) for i, id := range ids { idList[i] = strconv.Itoa(id) if i == 0 { - q = "?" + sb.WriteRune('?') } else { - q += ",?" + sb.WriteString(",?") } } - return idList, q + return idList, sb.String() +} + +func inqbuild2(count int) string { + if count <= 7 { + return qcache[count-1] + } + var sb strings.Builder + sb.Grow((count * 2) - 1) + for i := 0; i <= count; i++ { + if i == 0 { + sb.WriteRune('?') + } else { + sb.WriteString(",?") + } + } + return sb.String() } func inqbuildstr(strs []string) ([]interface{}, string) { - if len(strs) < 5 { + if len(strs) < 7 { idList := make([]interface{}, len(strs)) for i, id := range strs { idList[i] = id @@ -256,15 +273,16 @@ func inqbuildstr(strs []string) ([]interface{}, string) { return idList, qcache[len(strs)-1] } - var q string + var sb strings.Builder + sb.Grow((len(strs) * 2) - 1) idList := make([]interface{}, len(strs)) for i, id := range strs { idList[i] = id if i == 0 { - q = "?" + sb.WriteRune('?') } else { - q += ",?" + sb.WriteString(",?") } } - return idList, q + return idList, sb.String() } diff --git a/common/topic_list.go b/common/topic_list.go index 39f1ef04..b632735d 100644 --- a/common/topic_list.go +++ b/common/topic_list.go @@ -28,6 +28,7 @@ type ForumTopicListHolder struct { Paginator Paginator } +// TODO: Should we return no rows errors on empty pages? Is this likely to break something? type TopicListInt interface { GetListByCanSee(canSee []int, page, orderby int, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, pagi Paginator, err error) GetListByGroup(g *Group, page, orderby int, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, pagi Paginator, err error) @@ -35,6 +36,11 @@ type TopicListInt interface { GetList(page, orderby int, filterIDs []int) (topicList []*TopicsRow, forumList []Forum, pagi Paginator, err error) } +type TopicListIntTest interface { + RawGetListByForum(f *Forum, page, orderby int) (topicList []*TopicsRow, pagi Paginator, err error) + Tick() error +} + type DefaultTopicList struct { // TODO: Rewrite this to put permTree as the primary and put canSeeStr on each group? oddGroups map[int][2]*TopicListHolder @@ -73,9 +79,7 @@ func NewDefaultTopicList(acc *qgen.Accumulator) (*DefaultTopicList, error) { if err := acc.FirstError(); err != nil { return nil, err } - - err := tList.Tick() - if err != nil { + if err := tList.Tick(); err != nil { return nil, err } @@ -181,13 +185,7 @@ func (tList *DefaultTopicList) Tick() error { continue } - var qlist string - for i := 0; i < top; i++ { - if i != 0 { - qlist += "," - } - qlist += "?" - } + qlist := inqbuild2(top - 1) cols := "tid,title,content,createdBy,is_closed,sticky,createdAt,lastReplyAt,lastReplyBy,lastReplyID,parentID,views,postCount,likeCount,attachCount,poll,data" stmt, err := qgen.Builder.SimpleSelect("topics", cols, "parentID IN("+qlist+")", "views DESC,lastReplyAt DESC,createdBy DESC", "?,?") @@ -260,14 +258,9 @@ func (tList *DefaultTopicList) Tick() error { // TODO: Avoid rebuilding the entire list on every tick fList := make(map[int]*ForumTopicListHolder) for _, f := range fshort { - topicList, pagi := []*TopicsRow{}, Paginator{} - if f.TopicCount == 0 { - page := 1 - _, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage) - pageList := Paginate(page, lastPage, 5) - pagi = Paginator{pageList, page, lastPage} - } else { - topicList, pagi, err = tList.getListByForum(f, 1, 0) + topicList, pagi := []*TopicsRow{}, tList.defaultPagi() + if f.TopicCount != 0 { + topicList, pagi, err = tList.RawGetListByForum(f, 1, 0) if err != nil { return err } @@ -281,9 +274,8 @@ func (tList *DefaultTopicList) Tick() error { fList[f.ID] = &ForumTopicListHolder{topicList, pagi}*/ } - tList.forumLock.Lock() - tList.forums = fList - tList.forumLock.Unlock() + //fmt.Printf("fList: %+v\n", fList) + tList.setForumList(fList) hTbl := GetHookTable() _, _ = hTbl.VhookSkippable("tasks_tick_topic_list", tList) @@ -291,6 +283,19 @@ func (tList *DefaultTopicList) Tick() error { return nil } +func (tList *DefaultTopicList) defaultPagi() Paginator { + /*_, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage) + pageList := Paginate(page, lastPage, 5) + return topicList, Paginator{pageList, page, lastPage}, nil*/ + return Paginator{[]int{}, 1, 1} +} + +func (tList *DefaultTopicList) setForumList(forums map[int]*ForumTopicListHolder) { + tList.forumLock.Lock() + tList.forums = forums + tList.forumLock.Unlock() +} + /*var reloadForumMutex sync.Mutex // TODO: Avoid firing this multiple times per sec tick @@ -313,13 +318,8 @@ func (tList *DefaultTopicList) ReloadForum(id int) error { } tList.forumLock.Unlock() - topicList, pagi := []*TopicsRow{}, Paginator{} - if forum.TopicCount == 0 { - page := 1 - _, page, lastPage := PageOffset(forum.TopicCount, page, Config.ItemsPerPage) - pageList := Paginate(page, lastPage, 5) - pagi = Paginator{pageList, page, lastPage} - } else { + topicList, pagi := []*TopicsRow{}, tList.defaultPagi() + if forum.TopicCount != 0 { topicList, pagi, err = tList.getListByForum(forum, 1, 0) if err != nil { return err @@ -327,9 +327,7 @@ func (tList *DefaultTopicList) ReloadForum(id int) error { } fList[forum.ID] = &ForumTopicListHolder{topicList, pagi} - tList.forumLock.Lock() - tList.forums = fList - tList.forumLock.Unlock() + tList.setForumList(fList) return nil }*/ @@ -340,9 +338,7 @@ func (tList *DefaultTopicList) GetListByForum(f *Forum, page, orderby int) (topi page = 1 } if f.TopicCount == 0 { - _, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage) - pageList := Paginate(page, lastPage, 5) - return topicList, Paginator{pageList, page, lastPage}, nil + return topicList, tList.defaultPagi(), nil } if page == 1 && orderby == 0 { var h *ForumTopicListHolder @@ -354,16 +350,16 @@ func (tList *DefaultTopicList) GetListByForum(f *Forum, page, orderby int) (topi return h.List, h.Paginator, nil } } - return tList.getListByForum(f, page, orderby) + return tList.RawGetListByForum(f, page, orderby) } -func (tList *DefaultTopicList) getListByForum(f *Forum, page, orderby int) (topicList []*TopicsRow, pagi Paginator, err error) { +func (tList *DefaultTopicList) RawGetListByForum(f *Forum, page, orderby int) (topicList []*TopicsRow, pagi Paginator, err error) { // TODO: Does forum.TopicCount take the deleted items into consideration for guests? We don't have soft-delete yet, only hard-delete offset, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage) rows, err := tList.getTopicsByForum.Query(f.ID, offset, Config.ItemsPerPage) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } defer rows.Close() @@ -373,7 +369,7 @@ func (tList *DefaultTopicList) getListByForum(f *Forum, page, orderby int) (topi t := TopicsRow{Topic: Topic{ParentID: f.ID}} err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IsClosed, &t.Sticky, &t.CreatedAt, &t.LastReplyAt, &t.LastReplyBy, &t.LastReplyID, &t.ViewCount, &t.PostCount, &t.LikeCount) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } t.Link = BuildTopicURL(NameToSlug(t.Title), t.ID) @@ -387,7 +383,7 @@ func (tList *DefaultTopicList) getListByForum(f *Forum, page, orderby int) (topi reqUserList[t.LastReplyBy] = true } if err = rows.Err(); err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } // Convert the user ID map to a slice, then bulk load the users @@ -401,7 +397,7 @@ func (tList *DefaultTopicList) getListByForum(f *Forum, page, orderby int) (topi // TODO: What if a user is deleted via the Control Panel? userList, err := Users.BulkGetMap(idSlice) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } // Second pass to the add the user data @@ -411,6 +407,9 @@ func (tList *DefaultTopicList) getListByForum(f *Forum, page, orderby int) (topi t.LastUser = userList[t.LastReplyBy] } + if len(topicList) == 0 { + return topicList, tList.defaultPagi(), nil + } pageList := Paginate(page, lastPage, 5) return topicList, Paginator{pageList, page, lastPage}, nil } @@ -489,7 +488,7 @@ func (tList *DefaultTopicList) GetListByCanSee(canSee []int, page, orderby int, argList, qlist := ForumListToArgQ(filteredForums) if qlist == "" { // We don't want to kill the page, so pass an empty slice and nil error - return topicList, filteredForums, Paginator{[]int{}, 1, 1}, nil + return topicList, filteredForums, tList.defaultPagi(), nil } topicList, pagi, err = tList.getList(page, orderby, topicCount, argList, qlist) @@ -501,7 +500,7 @@ func (tList *DefaultTopicList) GetList(page, orderby int, filterIDs []int) (topi // TODO: Make CanSee a method on *Group with a canSee field? Have a CanSee method on *User to cover the case of superadmins? cCanSee, err := Forums.GetAllVisibleIDs() if err != nil { - return nil, nil, Paginator{nil, 1, 1}, err + return nil, nil, tList.defaultPagi(), err } //log.Printf("cCanSee: %+v\n", cCanSee) inSlice := func(haystack []int, needle int) bool { @@ -546,7 +545,7 @@ func (tList *DefaultTopicList) GetList(page, orderby int, filterIDs []int) (topi argList, qlist := ForumListToArgQ(forumList) if qlist == "" { // If the super admin can't see anything, then things have gone terribly wrong - return topicList, forumList, Paginator{[]int{}, 1, 1}, err + return topicList, forumList, tList.defaultPagi(), err } topicList, pagi, err = tList.getList(page, orderby, topicCount, argList, qlist) @@ -557,7 +556,7 @@ func (tList *DefaultTopicList) GetList(page, orderby int, filterIDs []int) (topi // TODO: Make orderby an enum of sorts func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList []interface{}, qlist string) (topicList []*TopicsRow, paginator Paginator, err error) { if topicCount == 0 { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } //log.Printf("argList: %+v\n",argList) //log.Printf("qlist: %+v\n",qlist) @@ -580,12 +579,12 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] } topicCount, err = ArgQToWeekViewTopicCount(argList, qlist) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } acc := qgen.NewAcc() stmt = acc.Select("topics").Columns(cols).Where("parentID IN(" + qlist + ") AND (weekEvenViews!=0 OR weekOddViews!=0)").Orderby(orderq).Limit("?,?").ComplexPrepare() if e := acc.FirstError(); e != nil { - return nil, Paginator{nil, 1, 1}, e + return nil, tList.defaultPagi(), e } defer stmt.Close() } @@ -612,7 +611,7 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] if stmt == nil { stmt, err = qgen.Builder.SimpleSelect("topics", cols, "parentID IN("+qlist+")", orderq, "?,?") if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } defer stmt.Close() } @@ -622,14 +621,13 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] rows, err := stmt.Query(argList...) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } defer rows.Close() - rc := Rstore.GetCache() + rc, tc := Rstore.GetCache(), Topics.GetCache() rcap := rc.GetCapacity() rlen := rc.Length() - tc := Topics.GetCache() reqUserList := make(map[int]bool) for rows.Next() { // TODO: Embed Topic structs in TopicsRow to make it easier for us to reuse this work in the topic cache @@ -637,7 +635,7 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] //var weekViews []uint8 err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IsClosed, &t.Sticky, &t.CreatedAt, &t.LastReplyAt, &t.LastReplyBy, &t.LastReplyID, &t.ParentID, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data, &t.WeekViews) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } //t.WeekViews = int(weekViews[0]) //log.Printf("t: %+v\n", t) @@ -675,7 +673,7 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] if t.PostCount == 2 && rlen < rcap && !hRids && page < 5 { rids, err := GetRidsForTopic(t.ID, 0) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } //log.Print("rids: ", rids) @@ -695,7 +693,7 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] } } if err = rows.Err(); err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } // TODO: specialcase for when reqUserList only has one or two items to avoid map alloc @@ -704,7 +702,7 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] for uid, _ := range reqUserList { u, err = Users.Get(uid) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } } for _, t := range topicList { @@ -723,7 +721,7 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList [] // TODO: What if a user is deleted via the Control Panel? userList, err := Users.BulkGetMap(idSlice) if err != nil { - return nil, Paginator{nil, 1, 1}, err + return nil, tList.defaultPagi(), err } // Second pass to the add the user data diff --git a/routes/topic.go b/routes/topic.go index da4a888b..4bf04ad8 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -8,7 +8,6 @@ import ( "errors" "io" - //"fmt" "image" "image/gif" "image/jpeg" @@ -24,7 +23,7 @@ import ( c "github.com/Azareal/Gosora/common" co "github.com/Azareal/Gosora/common/counters" - "github.com/Azareal/Gosora/common/phrases" + p "github.com/Azareal/Gosora/common/phrases" qgen "github.com/Azareal/Gosora/query_gen" ) @@ -48,7 +47,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header page, _ := strconv.Atoi(r.FormValue("page")) _, tid, err := ParseSEOURL(urlBit) if err != nil { - return c.SimpleError(phrases.GetErrorPhrase("url_id_must_be_integer"), w, r, h) + return c.SimpleError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, h) } // Get the topic... @@ -158,6 +157,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header } else if err != nil { return c.InternalError(err, w, r) } + //fmt.Printf("rlist: %+v\n",rlist) tpage.ItemList = rlist if externalHead { h.ExternalMedia = true @@ -206,7 +206,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header func AttachTopicActCommon(w http.ResponseWriter, r *http.Request, u *c.User, stid string) (t *c.Topic, ferr c.RouteError) { tid, err := strconv.Atoi(stid) if err != nil { - return t, c.LocalErrorJS(phrases.GetErrorPhrase("id_must_be_integer"), w, r) + return t, c.LocalErrorJS(p.GetErrorPhrase("id_must_be_integer"), w, r) } t, err = c.Topics.Get(tid) if err != nil { @@ -268,7 +268,7 @@ func RemoveAttachFromTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.Us for _, said := range strings.Split(r.PostFormValue("aids"), ",") { aid, err := strconv.Atoi(said) if err != nil { - return c.LocalErrorJS(phrases.GetErrorPhrase("id_must_be_integer"), w, r) + return c.LocalErrorJS(p.GetErrorPhrase("id_must_be_integer"), w, r) } rerr := deleteAttachment(w, r, u, aid, true) if rerr != nil { @@ -293,7 +293,7 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, if sfid != "" { fid, err = strconv.Atoi(sfid) if err != nil { - return c.LocalError(phrases.GetErrorPhrase("url_id_must_be_integer"), w, r, u) + return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, u) } } if fid == 0 { @@ -308,7 +308,7 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, return c.NoPermissions(w, r, u) } // TODO: Add a phrase for this - h.Title = phrases.GetTitlePhrase("create_topic") + h.Title = p.GetTitlePhrase("create_topic") h.Zone = "create_topic" // Lock this to the forum being linked? @@ -361,7 +361,7 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { fid, err := strconv.Atoi(r.PostFormValue("board")) if err != nil { - return c.LocalError(phrases.GetErrorPhrase("id_must_be_integer"), w, r, u) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u) } // TODO: Add hooks to make use of headerLite lite, ferr := c.SimpleForumUserCheck(w, r, u, fid) @@ -562,11 +562,11 @@ func uploadFilesWithHash(w http.ResponseWriter, r *http.Request, u *c.User, dir // TODO: Update the stats after edits so that we don't under or over decrement stats during deletes // TODO: Disable stat updates in posts handled by plugin_guilds -func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid string) c.RouteError { +func EditTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, stid string) c.RouteError { js := (r.PostFormValue("js") == "1") tid, err := strconv.Atoi(stid) if err != nil { - return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, js) + return c.PreErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, js) } topic, err := c.Topics.Get(tid) if err == sql.ErrNoRows { @@ -576,15 +576,15 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid } // TODO: Add hooks to make use of headerLite - lite, ferr := c.SimpleForumUserCheck(w, r, user, topic.ParentID) + lite, ferr := c.SimpleForumUserCheck(w, r, u, topic.ParentID) if ferr != nil { return ferr } - if !user.Perms.ViewTopic || !user.Perms.EditTopic { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ViewTopic || !u.Perms.EditTopic { + return c.NoPermissionsJSQ(w, r, u, js) } - if topic.IsClosed && !user.Perms.CloseTopic { - return c.NoPermissionsJSQ(w, r, user, js) + if topic.IsClosed && !u.Perms.CloseTopic { + return c.NoPermissionsJSQ(w, r, u, js) } err = topic.Update(r.PostFormValue("name"), r.PostFormValue("content")) @@ -592,16 +592,16 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid if err != nil { switch err { case c.ErrNoTitle: - return c.LocalErrorJSQ("This topic doesn't have a title", w, r, user, js) + return c.LocalErrorJSQ("This topic doesn't have a title", w, r, u, js) case c.ErrLongTitle: - return c.LocalErrorJSQ("The length of the title is too long, max: "+strconv.Itoa(c.Config.MaxTopicTitleLength), w, r, user, js) + return c.LocalErrorJSQ("The length of the title is too long, max: "+strconv.Itoa(c.Config.MaxTopicTitleLength), w, r, u, js) case c.ErrNoBody: - return c.LocalErrorJSQ("This topic doesn't have a body", w, r, user, js) + return c.LocalErrorJSQ("This topic doesn't have a body", w, r, u, js) } return c.InternalErrorJSQ(err, w, r, js) } - err = c.Forums.UpdateLastTopic(topic.ID, user.ID, topic.ParentID) + err = c.Forums.UpdateLastTopic(topic.ID, u.ID, topic.ParentID) if err != nil && err != sql.ErrNoRows { return c.InternalErrorJSQ(err, w, r, js) } @@ -614,7 +614,7 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid return c.InternalErrorJSQ(err, w, r, js) } - skip, rerr := lite.Hooks.VhookSkippable("action_end_edit_topic", topic.ID, user) + skip, rerr := lite.Hooks.VhookSkippable("action_end_edit_topic", topic.ID, u) if skip || rerr != nil { return rerr } @@ -622,7 +622,7 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid if !js { http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) } else { - outBytes, err := json.Marshal(JsonReply{c.ParseMessage(topic.Content, topic.ParentID, "forums", user.ParseSettings, user)}) + outBytes, err := json.Marshal(JsonReply{c.ParseMessage(topic.Content, topic.ParentID, "forums", u.ParseSettings, u)}) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -722,7 +722,7 @@ func StickTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, stid st func topicActionPre(stid, action string, w http.ResponseWriter, r *http.Request, u *c.User) (*c.Topic, *c.HeaderLite, c.RouteError) { tid, err := strconv.Atoi(stid) if err != nil { - return nil, nil, c.PreError(phrases.GetErrorPhrase("id_must_be_integer"), w, r) + return nil, nil, c.PreError(p.GetErrorPhrase("id_must_be_integer"), w, r) } t, err := c.Topics.Get(tid) if err == sql.ErrNoRows { @@ -766,7 +766,7 @@ func UnstickTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, stid return topicActionPost(t.Unstick(), "unstick", w, r, lite, t, u) } -func LockTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { +func LockTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { // TODO: Move this to some sort of middleware var tids []int js := c.ReqIsJson(r) @@ -786,7 +786,7 @@ func LockTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.Rou tids = append(tids, tid) } if len(tids) == 0 { - return c.LocalErrorJSQ("You haven't provided any IDs", w, r, user, js) + return c.LocalErrorJSQ("You haven't provided any IDs", w, r, u, js) } for _, tid := range tids { @@ -798,12 +798,12 @@ func LockTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.Rou } // TODO: Add hooks to make use of headerLite - lite, ferr := c.SimpleForumUserCheck(w, r, user, topic.ParentID) + lite, ferr := c.SimpleForumUserCheck(w, r, u, topic.ParentID) if ferr != nil { return ferr } - if !user.Perms.ViewTopic || !user.Perms.CloseTopic { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ViewTopic || !u.Perms.CloseTopic { + return c.NoPermissionsJSQ(w, r, u, js) } err = topic.Lock() @@ -811,13 +811,13 @@ func LockTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.Rou return c.InternalErrorJSQ(err, w, r, js) } - err = addTopicAction("lock", topic, user) + err = addTopicAction("lock", topic, u) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } // TODO: Do a bulk lock action hook? - skip, rerr := lite.Hooks.VhookSkippable("action_end_lock_topic", topic.ID, user) + skip, rerr := lite.Hooks.VhookSkippable("action_end_lock_topic", topic.ID, u) if skip || rerr != nil { return rerr } @@ -842,10 +842,10 @@ func UnlockTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, stid s // ! JS only route // TODO: Figure a way to get this route to work without JS -func MoveTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid string) c.RouteError { +func MoveTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sfid string) c.RouteError { fid, err := strconv.Atoi(sfid) if err != nil { - return c.PreErrorJS(phrases.GetErrorPhrase("id_must_be_integer"), w, r) + return c.PreErrorJS(p.GetErrorPhrase("id_must_be_integer"), w, r) } // TODO: Move this to some sort of middleware var tids []int @@ -869,19 +869,19 @@ func MoveTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid } // TODO: Add hooks to make use of headerLite - _, ferr := c.SimpleForumUserCheck(w, r, user, topic.ParentID) + _, ferr := c.SimpleForumUserCheck(w, r, u, topic.ParentID) if ferr != nil { return ferr } - if !user.Perms.ViewTopic || !user.Perms.MoveTopic { - return c.NoPermissionsJS(w, r, user) + if !u.Perms.ViewTopic || !u.Perms.MoveTopic { + return c.NoPermissionsJS(w, r, u) } - lite, ferr := c.SimpleForumUserCheck(w, r, user, fid) + lite, ferr := c.SimpleForumUserCheck(w, r, u, fid) if ferr != nil { return ferr } - if !user.Perms.ViewTopic || !user.Perms.MoveTopic { - return c.NoPermissionsJS(w, r, user) + if !u.Perms.ViewTopic || !u.Perms.MoveTopic { + return c.NoPermissionsJS(w, r, u) } err = topic.MoveTo(fid) @@ -889,13 +889,13 @@ func MoveTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sfid return c.InternalErrorJS(err, w, r) } // ? - Is there a better way of doing this? - err = addTopicAction("move-"+strconv.Itoa(fid), topic, user) + err = addTopicAction("move-"+strconv.Itoa(fid), topic, u) if err != nil { return c.InternalErrorJS(err, w, r) } // TODO: Do a bulk move action hook? - skip, rerr := lite.Hooks.VhookSkippable("action_end_move_topic", topic.ID, user) + skip, rerr := lite.Hooks.VhookSkippable("action_end_move_topic", topic.ID, u) if skip || rerr != nil { return rerr } @@ -916,11 +916,11 @@ func addTopicAction(action string, t *c.Topic, u *c.User) error { } // TODO: Refactor this -func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid string) c.RouteError { +func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, stid string) c.RouteError { js := r.PostFormValue("js") == "1" tid, err := strconv.Atoi(stid) if err != nil { - return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, js) + return c.PreErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, js) } topic, err := c.Topics.Get(tid) if err == sql.ErrNoRows { @@ -930,50 +930,50 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid } // TODO: Add hooks to make use of headerLite - lite, ferr := c.SimpleForumUserCheck(w, r, user, topic.ParentID) + lite, ferr := c.SimpleForumUserCheck(w, r, u, topic.ParentID) if ferr != nil { return ferr } - if !user.Perms.ViewTopic || !user.Perms.LikeItem { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ViewTopic || !u.Perms.LikeItem { + return c.NoPermissionsJSQ(w, r, u, js) } - if topic.CreatedBy == user.ID { - return c.LocalErrorJSQ("You can't like your own topics", w, r, user, js) + if topic.CreatedBy == u.ID { + return c.LocalErrorJSQ("You can't like your own topics", w, r, u, js) } _, err = c.Users.Get(topic.CreatedBy) if err != nil && err == sql.ErrNoRows { - return c.LocalErrorJSQ("The target user doesn't exist", w, r, user, js) + return c.LocalErrorJSQ("The target user doesn't exist", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } score := 1 - err = topic.Like(score, user.ID) + err = topic.Like(score, u.ID) if err == c.ErrAlreadyLiked { - return c.LocalErrorJSQ("You already liked this", w, r, user, js) + return c.LocalErrorJSQ("You already liked this", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } // ! Be careful about leaking per-route permission state with user ptr - alert := c.Alert{ActorID: user.ID, TargetUserID: topic.CreatedBy, Event: "like", ElementType: "topic", ElementID: tid, Actor: user} + alert := c.Alert{ActorID: u.ID, TargetUserID: topic.CreatedBy, Event: "like", ElementType: "topic", ElementID: tid, Actor: u} err = c.AddActivityAndNotifyTarget(alert) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - skip, rerr := lite.Hooks.VhookSkippable("action_end_like_topic", topic.ID, user) + skip, rerr := lite.Hooks.VhookSkippable("action_end_like_topic", topic.ID, u) if skip || rerr != nil { return rerr } return actionSuccess(w, r, "/topic/"+strconv.Itoa(tid), js) } -func UnlikeTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, stid string) c.RouteError { +func UnlikeTopicSubmit(w http.ResponseWriter, r *http.Request, u *c.User, stid string) c.RouteError { js := r.PostFormValue("js") == "1" tid, err := strconv.Atoi(stid) if err != nil { - return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, js) + return c.PreErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, js) } topic, err := c.Topics.Get(tid) if err == sql.ErrNoRows { @@ -983,21 +983,21 @@ func UnlikeTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sti } // TODO: Add hooks to make use of headerLite - lite, ferr := c.SimpleForumUserCheck(w, r, user, topic.ParentID) + lite, ferr := c.SimpleForumUserCheck(w, r, u, topic.ParentID) if ferr != nil { return ferr } - if !user.Perms.ViewTopic || !user.Perms.LikeItem { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ViewTopic || !u.Perms.LikeItem { + return c.NoPermissionsJSQ(w, r, u, js) } _, err = c.Users.Get(topic.CreatedBy) if err != nil && err == sql.ErrNoRows { - return c.LocalErrorJSQ("The target user doesn't exist", w, r, user, js) + return c.LocalErrorJSQ("The target user doesn't exist", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = topic.Unlike(user.ID) + err = topic.Unlike(u.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -1015,7 +1015,7 @@ func UnlikeTopicSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sti return c.InternalErrorJSQ(err, w, r, js) } - skip, rerr := lite.Hooks.VhookSkippable("action_end_unlike_topic", topic.ID, user) + skip, rerr := lite.Hooks.VhookSkippable("action_end_unlike_topic", topic.ID, u) if skip || rerr != nil { return rerr }