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
This commit is contained in:
parent
26e8bf32a7
commit
300defd460
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
|
118
routes/topic.go
118
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user