gosora/routes/topic_list.go
Azareal b4ffaa2cd6 Initial work on Plugin Hyperdrive.
Reduced the amount of boilerplate in Plugin Markdown.
Reduced the amount of boilerplate in Plugin Sendmail.
Reduced the amount of boilerplate in sample plugins Heythere and Skeleton.
Fixed up Plugin GeoIP. It's not ready for use though.

Added the routes.FootHeaders function.

Added the route_topic_list_start plugin hook.
2019-04-20 14:55:22 +10:00

200 lines
6.0 KiB
Go

package routes
import (
"database/sql"
"log"
"net/http"
"strconv"
"strings"
c "github.com/Azareal/Gosora/common"
"github.com/Azareal/Gosora/common/phrases"
)
func wsTopicList(topicList []*c.TopicsRow, lastPage int) *c.WsTopicList {
wsTopicList := make([]*c.WsTopicsRow, len(topicList))
for i, topicRow := range topicList {
wsTopicList[i] = topicRow.WebSockets()
}
return &c.WsTopicList{wsTopicList, lastPage}
}
func TopicList(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
skip, rerr := header.Hooks.VhookSkippable("route_topic_list_start", w, r, &user, header)
if skip || rerr != nil {
return rerr
}
return TopicListCommon(w, r, user, header, "lastupdated", "")
}
func TopicListMostViewed(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
return TopicListCommon(w, r, user, header, "mostviewed", "most-viewed")
}
// TODO: Implement search
func TopicListCommon(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header, torder string, tsorder string) c.RouteError {
header.Title = phrases.GetTitlePhrase("topics")
header.Zone = "topics"
header.Path = "/topics/"
header.MetaDesc = header.Settings["meta_desc"].(string)
group, err := c.Groups.Get(user.Group)
if err != nil {
log.Printf("Group #%d doesn't exist despite being used by c.User #%d", user.Group, user.ID)
return c.LocalError("Something weird happened", w, r, user)
}
// Get the current page
page, _ := strconv.Atoi(r.FormValue("page"))
sfids := r.FormValue("fids")
var fids []int
if sfids != "" {
for _, sfid := range strings.Split(sfids, ",") {
fid, err := strconv.Atoi(sfid)
if err != nil {
return c.LocalError("Invalid fid", w, r, user)
}
fids = append(fids, fid)
}
if len(fids) == 1 {
forum, err := c.Forums.Get(fids[0])
if err != nil {
return c.LocalError("Invalid fid forum", w, r, user)
}
header.Title = forum.Name
header.ZoneID = forum.ID
}
}
// TODO: Allow multiple forums in searches
// TODO: Simplify this block after initially landing search
var topicList []*c.TopicsRow
var forumList []c.Forum
var paginator c.Paginator
q := r.FormValue("q")
if q != "" && c.RepliesSearch != nil {
var canSee []int
if user.IsSuperAdmin {
canSee, err = c.Forums.GetAllVisibleIDs()
if err != nil {
return c.InternalError(err, w, r)
}
} else {
canSee = group.CanSee
}
var cfids []int
if len(fids) > 0 {
var inSlice = func(haystack []int, needle int) bool {
for _, item := range haystack {
if needle == item {
return true
}
}
return false
}
for _, fid := range fids {
if inSlice(canSee, fid) {
forum := c.Forums.DirtyGet(fid)
if forum.Name != "" && forum.Active && (forum.ParentType == "" || forum.ParentType == "forum") {
// TODO: Add a hook here for plugin_guilds?
cfids = append(cfids, fid)
}
}
}
} else {
cfids = canSee
}
tids, err := c.RepliesSearch.Query(q, cfids)
if err != nil && err != sql.ErrNoRows {
return c.InternalError(err, w, r)
}
//log.Printf("tids %+v\n", tids)
// TODO: Handle the case where there aren't any items...
// TODO: Add a BulkGet method which returns a slice?
tMap, err := c.Topics.BulkGetMap(tids)
if err != nil {
return c.InternalError(err, w, r)
}
var reqUserList = make(map[int]bool)
for _, topic := range tMap {
reqUserList[topic.CreatedBy] = true
reqUserList[topic.LastReplyBy] = true
topicList = append(topicList, topic.TopicsRow())
}
//fmt.Printf("reqUserList %+v\n", reqUserList)
// Convert the user ID map to a slice, then bulk load the users
var idSlice = make([]int, len(reqUserList))
var i int
for userID := range reqUserList {
idSlice[i] = userID
i++
}
// TODO: What if a user is deleted via the Control Panel?
//fmt.Printf("idSlice %+v\n", idSlice)
userList, err := c.Users.BulkGetMap(idSlice)
if err != nil {
return nil // TODO: Implement this!
}
// TODO: De-dupe this logic in common/topic_list.go?
for _, topic := range topicList {
topic.Link = c.BuildTopicURL(c.NameToSlug(topic.Title), topic.ID)
// TODO: Pass forum to something like topic.Forum and use that instead of these two properties? Could be more flexible.
forum := c.Forums.DirtyGet(topic.ParentID)
topic.ForumName = forum.Name
topic.ForumLink = forum.Link
// TODO: Create a specialised function with a bit less overhead for getting the last page for a post count
_, _, lastPage := c.PageOffset(topic.PostCount, 1, c.Config.ItemsPerPage)
topic.LastPage = lastPage
topic.Creator = userList[topic.CreatedBy]
topic.LastUser = userList[topic.LastReplyBy]
}
// TODO: Reduce the amount of boilerplate here
if r.FormValue("js") == "1" {
outBytes, err := wsTopicList(topicList, paginator.LastPage).MarshalJSON()
if err != nil {
return c.InternalError(err, w, r)
}
w.Write(outBytes)
return nil
}
header.Title = phrases.GetTitlePhrase("topics_search")
pi := c.TopicListPage{header, topicList, forumList, c.Config.DefaultForum, c.TopicListSort{torder, false}, paginator}
return renderTemplate("topics", w, r, header, pi)
}
// TODO: Pass a struct back rather than passing back so many variables
if user.IsSuperAdmin {
topicList, forumList, paginator, err = c.TopicList.GetList(page, tsorder, fids)
} else {
topicList, forumList, paginator, err = c.TopicList.GetListByGroup(group, page, tsorder, fids)
}
if err != nil {
return c.InternalError(err, w, r)
}
// ! Need an inline error not a page level error
if len(topicList) == 0 {
return c.NotFound(w, r, header)
}
// TODO: Reduce the amount of boilerplate here
if r.FormValue("js") == "1" {
outBytes, err := wsTopicList(topicList, paginator.LastPage).MarshalJSON()
if err != nil {
return c.InternalError(err, w, r)
}
w.Write(outBytes)
return nil
}
pi := c.TopicListPage{header, topicList, forumList, c.Config.DefaultForum, c.TopicListSort{torder, false}, paginator}
return renderTemplate("topics", w, r, header, pi)
}