Per-topic views are now visible in the topic pages for the Nox Theme.

The parent forum of a topic is now visible on the topic pages for the Cosora and Nox Themes.
Added basic styling for polls to the Nox Theme.
Improved the styling for topic title editing in the Nox Theme.
This commit is contained in:
Azareal 2018-08-30 20:31:21 +10:00
parent 41c3a5bb4a
commit 39adc2e606
11 changed files with 122 additions and 26 deletions

View File

@ -94,6 +94,7 @@ type TopicPage struct {
*Header *Header
ItemList []ReplyUser ItemList []ReplyUser
Topic TopicUser Topic TopicUser
Forum *Forum
Poll Poll Poll Poll
Page int Page int
LastPage int LastPage int

View File

@ -189,7 +189,7 @@ func CompileTemplates() error {
PollOption{1, "Something"}, PollOption{1, "Something"},
}, VoteCount: 7} }, VoteCount: 7}
avatar, microAvatar := BuildAvatar(62, "") avatar, microAvatar := BuildAvatar(62, "")
topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, RelativeTime(now), now, RelativeTime(now), 0, "", "127.0.0.1", 0, 1, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false} topic := TopicUser{1, "blah", "Blah", "Hey there!", 0, false, false, now, RelativeTime(now), now, RelativeTime(now), 0, "", "127.0.0.1", 1, 0, 1, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false}
var replyList []ReplyUser var replyList []ReplyUser
// TODO: Do we want the UID on this to be 0? // TODO: Do we want the UID on this to be 0?
avatar, microAvatar = BuildAvatar(0, "") avatar, microAvatar = BuildAvatar(0, "")
@ -197,7 +197,8 @@ func CompileTemplates() error {
var varList = make(map[string]tmpl.VarItem) var varList = make(map[string]tmpl.VarItem)
header.Title = "Topic Name" header.Title = "Topic Name"
tpage := TopicPage{header, replyList, topic, poll, 1, 1} tpage := TopicPage{header, replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, 1, 1}
tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID)
topicIDTmpl, err := c.Compile("topic.html", "templates/", "common.TopicPage", tpage, varList) topicIDTmpl, err := c.Compile("topic.html", "templates/", "common.TopicPage", tpage, varList)
if err != nil { if err != nil {
return err return err
@ -234,7 +235,7 @@ func CompileTemplates() error {
} }
var topicsList []*TopicsRow var topicsList []*TopicsRow
topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, "Date", user3.ID, 1, "", "127.0.0.1", 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"}) topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, "Date", user3.ID, 1, "", "127.0.0.1", 1, 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"})
header2.Title = "Topic List" header2.Title = "Topic List"
topicListPage := TopicListPage{header, topicsList, forumList, Config.DefaultForum, Paginator{[]int{1}, 1, 1}} topicListPage := TopicListPage{header, topicsList, forumList, Config.DefaultForum, Paginator{[]int{1}, 1, 1}}
topicListTmpl, err := c.Compile("topics.html", "templates/", "common.TopicListPage", topicListPage, varList) topicListTmpl, err := c.Compile("topics.html", "templates/", "common.TopicListPage", topicListPage, varList)
@ -365,7 +366,7 @@ func CompileJSTemplates() error {
// TODO: Fix the import loop so we don't have to use this hack anymore // TODO: Fix the import loop so we don't have to use this hack anymore
c.SetBuildTags("!no_templategen,tmplgentopic") c.SetBuildTags("!no_templategen,tmplgentopic")
var topicsRow = &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, "Date", user3.ID, 1, "", "127.0.0.1", 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"} var topicsRow = &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, "Date", user3.ID, 1, "", "127.0.0.1", 1, 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"}
topicListItemTmpl, err := c.Compile("topics_topic.html", "templates/", "*common.TopicsRow", topicsRow, varList) topicListItemTmpl, err := c.Compile("topics_topic.html", "templates/", "*common.TopicsRow", topicsRow, varList)
if err != nil { if err != nil {
return err return err
@ -376,7 +377,7 @@ func CompileJSTemplates() error {
PollOption{1, "Something"}, PollOption{1, "Something"},
}, VoteCount: 7} }, VoteCount: 7}
avatar, microAvatar := BuildAvatar(62, "") avatar, microAvatar := BuildAvatar(62, "")
topic := TopicUser{1, "blah", "Blah", "Hey there!", 62, false, false, now, RelativeTime(now), now, RelativeTime(now), 0, "", "127.0.0.1", 0, 1, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false} topic := TopicUser{1, "blah", "Blah", "Hey there!", 62, false, false, now, RelativeTime(now), now, RelativeTime(now), 0, "", "127.0.0.1", 1, 0, 1, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false}
var replyList []ReplyUser var replyList []ReplyUser
// TODO: Do we really want the UID here to be zero? // TODO: Do we really want the UID here to be zero?
avatar, microAvatar = BuildAvatar(0, "") avatar, microAvatar = BuildAvatar(0, "")
@ -384,7 +385,8 @@ func CompileJSTemplates() error {
varList = make(map[string]tmpl.VarItem) varList = make(map[string]tmpl.VarItem)
header.Title = "Topic Name" header.Title = "Topic Name"
tpage := TopicPage{header, replyList, topic, poll, 1, 1} tpage := TopicPage{header, replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, 1, 1}
tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID)
topicIDTmpl, err := c.Compile("topic_posts.html", "templates/", "common.TopicPage", tpage, varList) topicIDTmpl, err := c.Compile("topic_posts.html", "templates/", "common.TopicPage", tpage, varList)
if err != nil { if err != nil {
return err return err

View File

@ -37,6 +37,7 @@ type Topic struct {
ParentID int ParentID int
Status string // Deprecated. Marked for removal. Status string // Deprecated. Marked for removal.
IPAddress string IPAddress string
ViewCount int
PostCount int PostCount int
LikeCount int LikeCount int
ClassName string // CSS Class Name ClassName string // CSS Class Name
@ -60,6 +61,7 @@ type TopicUser struct {
ParentID int ParentID int
Status string // Deprecated. Marked for removal. Status string // Deprecated. Marked for removal.
IPAddress string IPAddress string
ViewCount int
PostCount int PostCount int
LikeCount int LikeCount int
ClassName string ClassName string
@ -97,6 +99,7 @@ type TopicsRow struct {
ParentID int ParentID int
Status string // Deprecated. Marked for removal. -Is there anything we could use it for? Status string // Deprecated. Marked for removal. -Is there anything we could use it for?
IPAddress string IPAddress string
ViewCount int
PostCount int PostCount int
LikeCount int LikeCount int
ClassName string ClassName string
@ -123,6 +126,7 @@ type WsTopicsRow struct {
RelativeLastReplyAt string RelativeLastReplyAt string
LastReplyBy int LastReplyBy int
ParentID int ParentID int
ViewCount int
PostCount int PostCount int
LikeCount int LikeCount int
ClassName string ClassName string
@ -133,7 +137,7 @@ type WsTopicsRow struct {
} }
func (row *TopicsRow) WebSockets() *WsTopicsRow { func (row *TopicsRow) WebSockets() *WsTopicsRow {
return &WsTopicsRow{row.ID, row.Link, row.Title, row.CreatedBy, row.IsClosed, row.Sticky, row.CreatedAt, row.LastReplyAt, row.RelativeLastReplyAt, row.LastReplyBy, row.ParentID, row.PostCount, row.LikeCount, row.ClassName, row.Creator.WebSockets(), row.LastUser.WebSockets(), row.ForumName, row.ForumLink} return &WsTopicsRow{row.ID, row.Link, row.Title, row.CreatedBy, row.IsClosed, row.Sticky, row.CreatedAt, row.LastReplyAt, row.RelativeLastReplyAt, row.LastReplyBy, row.ParentID, row.ViewCount, row.PostCount, row.LikeCount, row.ClassName, row.Creator.WebSockets(), row.LastUser.WebSockets(), row.ForumName, row.ForumLink}
} }
type TopicStmts struct { type TopicStmts struct {
@ -174,8 +178,8 @@ func init() {
setPoll: acc.Update("topics").Set("content = '', parsed_content = '', poll = ?").Where("tid = ? AND poll = 0").Prepare(), setPoll: acc.Update("topics").Set("content = '', parsed_content = '', poll = ?").Where("tid = ? AND poll = 0").Prepare(),
createActionReply: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(), createActionReply: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
getTopicUser: acc.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.poll, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", ""), getTopicUser: acc.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.views, topics.postCount, topics.likeCount, topics.poll, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", ""),
getByReplyID: acc.SimpleLeftJoin("replies", "topics", "topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.poll, topics.data", "replies.tid = topics.tid", "rid = ?", "", ""), getByReplyID: acc.SimpleLeftJoin("replies", "topics", "topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.views, topics.postCount, topics.likeCount, topics.poll, topics.data", "replies.tid = topics.tid", "rid = ?", "", ""),
} }
return acc.FirstError() return acc.FirstError()
}) })
@ -332,7 +336,7 @@ func (topic *Topic) Copy() Topic {
// TODO: Load LastReplyAt? // TODO: Load LastReplyAt?
func TopicByReplyID(rid int) (*Topic, error) { func TopicByReplyID(rid int) (*Topic, error) {
topic := Topic{ID: 0} topic := Topic{ID: 0}
err := topicStmts.getByReplyID.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data) err := topicStmts.getByReplyID.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID) topic.Link = BuildTopicURL(NameToSlug(topic.Title), topic.ID)
return &topic, err return &topic, err
} }
@ -366,14 +370,14 @@ func GetTopicUser(tid int) (TopicUser, error) {
} }
tu := TopicUser{ID: tid} tu := TopicUser{ID: tid}
err := topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.PostCount, &tu.LikeCount, &tu.Poll, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) err := topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.Poll, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
tu.Avatar, tu.MicroAvatar = BuildAvatar(tu.CreatedBy, tu.Avatar) tu.Avatar, tu.MicroAvatar = BuildAvatar(tu.CreatedBy, tu.Avatar)
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID) tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy) tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy)
tu.Tag = Groups.DirtyGet(tu.Group).Tag tu.Tag = Groups.DirtyGet(tu.Group).Tag
if tcache != nil { if tcache != nil {
theTopic := Topic{ID: tu.ID, Link: tu.Link, Title: tu.Title, Content: tu.Content, CreatedBy: tu.CreatedBy, IsClosed: tu.IsClosed, Sticky: tu.Sticky, CreatedAt: tu.CreatedAt, LastReplyAt: tu.LastReplyAt, ParentID: tu.ParentID, IPAddress: tu.IPAddress, PostCount: tu.PostCount, LikeCount: tu.LikeCount, Poll: tu.Poll} theTopic := Topic{ID: tu.ID, Link: tu.Link, Title: tu.Title, Content: tu.Content, CreatedBy: tu.CreatedBy, IsClosed: tu.IsClosed, Sticky: tu.Sticky, CreatedAt: tu.CreatedAt, LastReplyAt: tu.LastReplyAt, ParentID: tu.ParentID, IPAddress: tu.IPAddress, ViewCount: tu.ViewCount, PostCount: tu.PostCount, LikeCount: tu.LikeCount, Poll: tu.Poll}
//log.Printf("theTopic: %+v\n", theTopic) //log.Printf("theTopic: %+v\n", theTopic)
_ = tcache.Add(&theTopic) _ = tcache.Add(&theTopic)
} }
@ -401,6 +405,7 @@ func copyTopicToTopicUser(topic *Topic, user *User) (tu TopicUser) {
tu.LastReplyAt = topic.LastReplyAt tu.LastReplyAt = topic.LastReplyAt
tu.ParentID = topic.ParentID tu.ParentID = topic.ParentID
tu.IPAddress = topic.IPAddress tu.IPAddress = topic.IPAddress
tu.ViewCount = topic.ViewCount
tu.PostCount = topic.PostCount tu.PostCount = topic.PostCount
tu.LikeCount = topic.LikeCount tu.LikeCount = topic.LikeCount
tu.Poll = topic.Poll tu.Poll = topic.Poll

View File

@ -216,7 +216,7 @@ func (tList *DefaultTopicList) getList(page int, argList []interface{}, qlist st
} }
offset, page, lastPage := PageOffset(topicCount, page, Config.ItemsPerPage) offset, page, lastPage := PageOffset(topicCount, page, Config.ItemsPerPage)
stmt, err := qgen.Builder.SimpleSelect("topics", "tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount", "parentID IN("+qlist+")", "sticky DESC, lastReplyAt DESC, createdBy DESC", "?,?") stmt, err := qgen.Builder.SimpleSelect("topics", "tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, views, postCount, likeCount", "parentID IN("+qlist+")", "sticky DESC, lastReplyAt DESC, createdBy DESC", "?,?")
if err != nil { if err != nil {
return nil, Paginator{nil, 1, 1}, err return nil, Paginator{nil, 1, 1}, err
} }
@ -234,7 +234,7 @@ func (tList *DefaultTopicList) getList(page int, argList []interface{}, qlist st
var reqUserList = make(map[int]bool) var reqUserList = make(map[int]bool)
for rows.Next() { for rows.Next() {
topicItem := TopicsRow{ID: 0} topicItem := TopicsRow{ID: 0}
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount) err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.ViewCount, &topicItem.PostCount, &topicItem.LikeCount)
if err != nil { if err != nil {
return nil, Paginator{nil, 1, 1}, err return nil, Paginator{nil, 1, 1}, err
} }

View File

@ -57,7 +57,7 @@ func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
} }
return &DefaultTopicStore{ return &DefaultTopicStore{
cache: cache, cache: cache,
get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, poll, data").Where("tid = ?").Prepare(), get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, views, postCount, likeCount, poll, data").Where("tid = ?").Prepare(),
exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(), exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(),
topicCount: acc.Count("topics").Prepare(), topicCount: acc.Count("topics").Prepare(),
create: acc.Insert("topics").Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(), create: acc.Insert("topics").Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(),
@ -71,7 +71,7 @@ func (mts *DefaultTopicStore) DirtyGet(id int) *Topic {
} }
topic = &Topic{ID: id} topic = &Topic{ID: id}
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data) err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
if err == nil { if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
_ = mts.cache.Add(topic) _ = mts.cache.Add(topic)
@ -88,7 +88,7 @@ func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
} }
topic = &Topic{ID: id} topic = &Topic{ID: id}
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data) err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
if err == nil { if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
_ = mts.cache.Add(topic) _ = mts.cache.Add(topic)
@ -99,14 +99,14 @@ func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
// BypassGet will always bypass the cache and pull the topic directly from the database // BypassGet will always bypass the cache and pull the topic directly from the database
func (mts *DefaultTopicStore) BypassGet(id int) (*Topic, error) { func (mts *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
topic := &Topic{ID: id} topic := &Topic{ID: id}
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data) err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
return topic, err return topic, err
} }
func (mts *DefaultTopicStore) Reload(id int) error { func (mts *DefaultTopicStore) Reload(id int) error {
topic := &Topic{ID: id} topic := &Topic{ID: id}
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data) err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.ViewCount, &topic.PostCount, &topic.LikeCount, &topic.Poll, &topic.Data)
if err == nil { if err == nil {
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id) topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
_ = mts.cache.Set(topic) _ = mts.cache.Set(topic)

View File

@ -762,7 +762,7 @@ func BenchmarkQueryTopicParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
var tu common.TopicUser var tu common.TopicUser
for pb.Next() { for pb.Next() {
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level) err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.views, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
if err == ErrNoRows { if err == ErrNoRows {
log.Fatal("No rows found!") log.Fatal("No rows found!")
return return

View File

@ -21,7 +21,7 @@ var forumStmts ForumStmts
func init() { func init() {
common.DbInits.Add(func(acc *qgen.Accumulator) error { common.DbInits.Add(func(acc *qgen.Accumulator) error {
forumStmts = ForumStmts{ forumStmts = ForumStmts{
getTopics: acc.Select("topics").Columns("tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount").Where("parentID = ?").Orderby("sticky DESC, lastReplyAt DESC, createdBy DESC").Limit("?,?").Prepare(), getTopics: acc.Select("topics").Columns("tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, views, postCount, likeCount").Where("parentID = ?").Orderby("sticky DESC, lastReplyAt DESC, createdBy DESC").Limit("?,?").Prepare(),
} }
return acc.FirstError() return acc.FirstError()
}) })
@ -73,7 +73,7 @@ func ViewForum(w http.ResponseWriter, r *http.Request, user common.User, sfid st
var reqUserList = make(map[int]bool) var reqUserList = make(map[int]bool)
for rows.Next() { for rows.Next() {
var topicItem = common.TopicsRow{ID: 0} var topicItem = common.TopicsRow{ID: 0}
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount) err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.ViewCount, &topicItem.PostCount, &topicItem.LikeCount)
if err != nil { if err != nil {
return common.InternalError(err, w, r) return common.InternalError(err, w, r)
} }

View File

@ -86,6 +86,11 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
} }
topic.RelativeCreatedAt = common.RelativeTime(topic.CreatedAt) topic.RelativeCreatedAt = common.RelativeTime(topic.CreatedAt)
forum, err := common.Forums.Get(topic.ParentID)
if err != nil {
return common.InternalError(err, w, r)
}
var poll common.Poll var poll common.Poll
if topic.Poll != 0 { if topic.Poll != 0 {
pPoll, err := common.Polls.Get(topic.Poll) pPoll, err := common.Polls.Get(topic.Poll)
@ -110,7 +115,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
// Calculate the offset // Calculate the offset
offset, page, lastPage := common.PageOffset(topic.PostCount, page, common.Config.ItemsPerPage) offset, page, lastPage := common.PageOffset(topic.PostCount, page, common.Config.ItemsPerPage)
tpage := common.TopicPage{header, []common.ReplyUser{}, topic, poll, page, lastPage} tpage := common.TopicPage{header, []common.ReplyUser{}, topic, forum, poll, page, lastPage}
// Get the replies if we have any... // Get the replies if we have any...
if topic.PostCount > 0 { if topic.PostCount > 0 {

View File

@ -12,8 +12,8 @@
<form action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"> <form action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post">
<div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}"> <div class="rowitem topic_item{{if .Topic.Sticky}} topic_sticky_head{{else if .Topic.IsClosed}} topic_closed_head{{end}}">
<h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1> <h1 class='topic_name hide_on_edit' title='{{.Topic.Title}}'>{{.Topic.Title}}</h1>
{{/** TODO: Inline this CSS **/}} <span class="topic_name_forum_sep hide_on_edit"> / </span>
{{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status.closed_tooltip"}}' aria-label='{{lang "topic.status_closed_aria"}}' style="font-weight:normal;float: right;position:relative;top:-5px;">&#x1F512;&#xFE0E</span>{{end}} <a href="{{.Forum.Link}}" class="topic_forum hide_on_edit">{{.Forum.Name}}</a>
{{/** TODO: Does this need to be guarded by a permission? It's only visible in edit mode anyway, which can't be triggered, if they don't have the permission **/}} {{/** TODO: Does this need to be guarded by a permission? It's only visible in edit mode anyway, which can't be triggered, if they don't have the permission **/}}
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}} {{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
{{if .CurrentUser.Perms.EditTopic}} {{if .CurrentUser.Perms.EditTopic}}
@ -21,6 +21,9 @@
<button name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button> <button name="topic-button" class="formbutton show_on_edit submit_edit">{{lang "topic.update_button"}}</button>
{{end}} {{end}}
{{end}} {{end}}
<span class="topic_view_count hide_on_edit">{{.Topic.ViewCount}}</span>
{{/** TODO: Inline this CSS **/}}
{{if .Topic.IsClosed}}<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='{{lang "status.closed_tooltip"}}' aria-label='{{lang "topic.status_closed_aria"}}' style="font-weight:normal;float: right;position:relative;top:-5px;">&#x1F512;&#xFE0E</span>{{end}}
</div> </div>
</form> </form>
</div> </div>

View File

@ -946,6 +946,21 @@ textarea {
margin-top: 0px; margin-top: 0px;
} }
.topic_block {
padding-bottom: 10px;
}
.topic_name_forum_sep {
margin-left: 6px;
margin-right: 6px;
line-height: 26px;
font-size: 18px;
}
.topic_forum {
line-height: 23px;
}
.topic_view_count {
display: none;
}
.postImage { .postImage {
width: 100%; width: 100%;
max-width: 240px; max-width: 240px;

View File

@ -516,10 +516,37 @@ button, .formbutton, .panel_right_button {
width: 36px; width: 36px;
} }
.colstack { .colstack, .topic_item {
display: flex; display: flex;
} }
.topic_item .topic_name_forum_sep {
font-size: 20px;
line-height: 30px;
margin-left: 7px;
margin-right: 7px;
}
.topic_item .topic_forum {
font-size: 19px;
line-height: 31px;
color: #dddddd;
}
.topic_view_count {
font-size: 17px;
margin-left: auto;
margin-right: 20px;
margin-top: 6px;
}
.topic_view_count:after {
content: " views";
}
.topic_name_input {
width: 100%;
margin-right: 12px;
}
.topic_item .submit_edit {
margin-right: 16px;
}
.postImage { .postImage {
width: 100%; width: 100%;
max-width: 320px; max-width: 320px;
@ -610,6 +637,44 @@ button, .formbutton, .panel_right_button {
margin-right: auto; margin-right: auto;
} }
input[type=checkbox] {
display: none;
}
input[type=checkbox] + label {
display: inline-flex;
width: 18px;
height: 18px;
margin-bottom: -2px;
margin-right: 8px;
border: 1px solid rgb(120,120,120);
background-color: rgb(90,90,90);
padding-top: 1px;
border-radius: 2px;
}
input[type=checkbox]:checked + label .sel {
width: 8px;
height: 8px;
background: rgb(160,160,160);
margin-left: auto;
margin-right: auto;
margin-top: auto;
margin-bottom: auto;
border-radius: 2px;
}
.poll_option {
display: flex;
margin-bottom: 10px;
}
.poll_option_text {
line-height: 14px;
}
.poll_buttons {
padding-top: 4px;
}
.poll_buttons button {
margin-right: 8px;
}
.add_like:before, .remove_like:before { .add_like:before, .remove_like:before {
content: "{{index .Phrases "topic.plus_one"}}"; content: "{{index .Phrases "topic.plus_one"}}";
} }