Added the post counter and the associated Control Panel graph.

Renamed the pre_render_panel_analytics hook to pre_render_panel_analytics_views.
Added a details table to the route graphs.
Moved the /topic/ and /reply/ routes to the router generator and hardened them.
Made some of the route names more consistent.
We now track IPs in odd situations in debug mode.
Fixed a bug where people could post on non-existent profiles.

Continued work on topic move.
Added the postchunks table.
This commit is contained in:
Azareal 2018-01-14 12:03:20 +00:00
parent 90a20d5b05
commit 9a3d8425e0
32 changed files with 1695 additions and 1068 deletions

View File

@ -8,21 +8,25 @@ import (
"../query_gen/lib"
)
var GlobalViewCounter *ChunkedViewCounter
// Global counters
var GlobalViewCounter *DefaultViewCounter
var AgentViewCounter *DefaultAgentViewCounter
var RouteViewCounter *DefaultRouteViewCounter
var PostCounter *DefaultPostCounter
// Local counters
var TopicViewCounter *DefaultTopicViewCounter
type ChunkedViewCounter struct {
type DefaultViewCounter struct {
buckets [2]int64
currentBucket int64
insert *sql.Stmt
}
func NewChunkedViewCounter() (*ChunkedViewCounter, error) {
func NewGlobalViewCounter() (*DefaultViewCounter, error) {
acc := qgen.Builder.Accumulator()
counter := &ChunkedViewCounter{
counter := &DefaultViewCounter{
currentBucket: 0,
insert: acc.Insert("viewchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
}
@ -32,7 +36,7 @@ func NewChunkedViewCounter() (*ChunkedViewCounter, error) {
return counter, acc.FirstError()
}
func (counter *ChunkedViewCounter) Tick() (err error) {
func (counter *DefaultViewCounter) Tick() (err error) {
var oldBucket = counter.currentBucket
var nextBucket int64 // 0
if counter.currentBucket == 0 {
@ -47,11 +51,11 @@ func (counter *ChunkedViewCounter) Tick() (err error) {
return counter.insertChunk(previousViewChunk)
}
func (counter *ChunkedViewCounter) Bump() {
func (counter *DefaultViewCounter) Bump() {
atomic.AddInt64(&counter.buckets[counter.currentBucket], 1)
}
func (counter *ChunkedViewCounter) insertChunk(count int64) error {
func (counter *DefaultViewCounter) insertChunk(count int64) error {
if count == 0 {
return nil
}
@ -60,6 +64,53 @@ func (counter *ChunkedViewCounter) insertChunk(count int64) error {
return err
}
type DefaultPostCounter struct {
buckets [2]int64
currentBucket int64
insert *sql.Stmt
}
func NewPostCounter() (*DefaultPostCounter, error) {
acc := qgen.Builder.Accumulator()
counter := &DefaultPostCounter{
currentBucket: 0,
insert: acc.Insert("postchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(),
}
AddScheduledFifteenMinuteTask(counter.Tick)
//AddScheduledSecondTask(counter.Tick)
AddShutdownTask(counter.Tick)
return counter, acc.FirstError()
}
func (counter *DefaultPostCounter) Tick() (err error) {
var oldBucket = counter.currentBucket
var nextBucket int64 // 0
if counter.currentBucket == 0 {
nextBucket = 1
}
atomic.AddInt64(&counter.buckets[oldBucket], counter.buckets[nextBucket])
atomic.StoreInt64(&counter.buckets[nextBucket], 0)
atomic.StoreInt64(&counter.currentBucket, nextBucket)
var previousViewChunk = counter.buckets[oldBucket]
atomic.AddInt64(&counter.buckets[oldBucket], -previousViewChunk)
return counter.insertChunk(previousViewChunk)
}
func (counter *DefaultPostCounter) Bump() {
atomic.AddInt64(&counter.buckets[counter.currentBucket], 1)
}
func (counter *DefaultPostCounter) insertChunk(count int64) error {
if count == 0 {
return nil
}
debugLogf("Inserting a postchunk with a count of %d", count)
_, err := counter.insert.Exec(count)
return err
}
type RWMutexCounterBucket struct {
counter int
sync.RWMutex
@ -114,7 +165,7 @@ func (counter *DefaultAgentViewCounter) insertChunk(count int, agent int) error
func (counter *DefaultAgentViewCounter) Bump(agent int) {
// TODO: Test this check
debugLog("counter.agentBuckets[", agent, "]: ", counter.agentBuckets[agent])
debugDetail("counter.agentBuckets[", agent, "]: ", counter.agentBuckets[agent])
if len(counter.agentBuckets) <= agent || agent < 0 {
return
}

View File

@ -96,7 +96,7 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
"pre_render_panel_forums": nil,
"pre_render_panel_delete_forum": nil,
"pre_render_panel_edit_forum": nil,
"pre_render_panel_analytics": nil,
"pre_render_panel_analytics_views": nil,
"pre_render_panel_analytics_routes": nil,
"pre_render_panel_analytics_agents": nil,
"pre_render_panel_analytics_route_views": nil,

View File

@ -206,6 +206,7 @@ type PanelAnalyticsRoutePage struct {
Zone string
Route string
PrimaryGraph PanelTimeGraph
ViewItems []PanelAnalyticsItem
TimeRange string
}

View File

@ -78,6 +78,7 @@ type Perms struct {
EditReply bool
//EditOwnReply bool
DeleteReply bool
//DeleteOwnReply bool
PinTopic bool
CloseTopic bool
//CloseOwnTopic bool

View File

@ -5,6 +5,7 @@ import (
"log"
"net"
"net/http"
"strconv"
"strings"
)
@ -346,3 +347,25 @@ func NoSessionMismatch(w http.ResponseWriter, r *http.Request, user User) RouteE
func ReqIsJson(r *http.Request) bool {
return r.Header.Get("Content-type") == "application/json"
}
func HandleUploadRoute(w http.ResponseWriter, r *http.Request, user User, maxFileSize int) RouteError {
// TODO: Reuse this code more
if r.ContentLength > int64(maxFileSize) {
size, unit := ConvertByteUnit(float64(maxFileSize))
return CustomError("Your upload is too big. Your files need to be smaller than "+strconv.Itoa(int(size))+unit+".", http.StatusExpectationFailed, "Error", w, r, user)
}
r.Body = http.MaxBytesReader(w, r.Body, int64(maxFileSize))
err := r.ParseMultipartForm(int64(Megabyte))
if err != nil {
return LocalError("Bad Form", w, r, user)
}
return nil
}
func NoUploadSessionMismatch(w http.ResponseWriter, r *http.Request, user User) RouteError {
if r.FormValue("session") != user.Session {
return SecurityError(w, r, user)
}
return nil
}

View File

@ -112,6 +112,7 @@ type TopicStmts struct {
addRepliesToTopic *sql.Stmt
lock *sql.Stmt
unlock *sql.Stmt
moveTo *sql.Stmt
stick *sql.Stmt
unstick *sql.Stmt
hasLikedTopic *sql.Stmt
@ -132,6 +133,7 @@ func init() {
addRepliesToTopic: acc.Update("topics").Set("postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()").Where("tid = ?").Prepare(),
lock: acc.Update("topics").Set("is_closed = 1").Where("tid = ?").Prepare(),
unlock: acc.Update("topics").Set("is_closed = 0").Where("tid = ?").Prepare(),
moveTo: acc.Update("topics").Set("parentID = ?").Where("tid = ?").Prepare(),
stick: acc.Update("topics").Set("sticky = 1").Where("tid = ?").Prepare(),
unstick: acc.Update("topics").Set("sticky = 0").Where("tid = ?").Prepare(),
hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType = 'topics'").Prepare(),
@ -175,6 +177,12 @@ func (topic *Topic) Unlock() (err error) {
return err
}
func (topic *Topic) MoveTo(destForum int) (err error) {
_, err = topicStmts.moveTo.Exec(destForum, topic.ID)
topic.cacheRemove()
return err
}
// TODO: We might want more consistent terminology rather than using stick in some places and pin in others. If you don't understand the difference, there is none, they are one and the same.
func (topic *Topic) Stick() (err error) {
_, err = topicStmts.stick.Exec(topic.ID)

View File

@ -56,6 +56,7 @@ var RouteMap = map[string]interface{}{
"routePanelAnalyticsAgents": routePanelAnalyticsAgents,
"routePanelAnalyticsRouteViews": routePanelAnalyticsRouteViews,
"routePanelAnalyticsAgentViews": routePanelAnalyticsAgentViews,
"routePanelAnalyticsPosts": routePanelAnalyticsPosts,
"routePanelGroups": routePanelGroups,
"routePanelGroupsEdit": routePanelGroupsEdit,
"routePanelGroupsEditPerms": routePanelGroupsEditPerms,
@ -79,6 +80,20 @@ var RouteMap = map[string]interface{}{
"routeUnban": routeUnban,
"routeActivate": routeActivate,
"routeIps": routeIps,
"routeTopicCreateSubmit": routeTopicCreateSubmit,
"routeEditTopicSubmit": routeEditTopicSubmit,
"routeDeleteTopicSubmit": routeDeleteTopicSubmit,
"routeStickTopicSubmit": routeStickTopicSubmit,
"routeUnstickTopicSubmit": routeUnstickTopicSubmit,
"routeLockTopicSubmit": routeLockTopicSubmit,
"routeUnlockTopicSubmit": routeUnlockTopicSubmit,
"routeMoveTopicSubmit": routeMoveTopicSubmit,
"routeLikeTopicSubmit": routeLikeTopicSubmit,
"routeTopicID": routeTopicID,
"routeCreateReplySubmit": routeCreateReplySubmit,
"routeReplyEditSubmit": routeReplyEditSubmit,
"routeReplyDeleteSubmit": routeReplyDeleteSubmit,
"routeReplyLikeSubmit": routeReplyLikeSubmit,
"routeDynamic": routeDynamic,
"routeUploads": routeUploads,
}
@ -126,31 +141,46 @@ var routeMapEnum = map[string]int{
"routePanelAnalyticsAgents": 38,
"routePanelAnalyticsRouteViews": 39,
"routePanelAnalyticsAgentViews": 40,
"routePanelGroups": 41,
"routePanelGroupsEdit": 42,
"routePanelGroupsEditPerms": 43,
"routePanelGroupsEditSubmit": 44,
"routePanelGroupsEditPermsSubmit": 45,
"routePanelGroupsCreateSubmit": 46,
"routePanelBackups": 47,
"routePanelLogsMod": 48,
"routePanelDebug": 49,
"routePanel": 50,
"routeAccountEditCritical": 51,
"routeAccountEditCriticalSubmit": 52,
"routeAccountEditAvatar": 53,
"routeAccountEditAvatarSubmit": 54,
"routeAccountEditUsername": 55,
"routeAccountEditUsernameSubmit": 56,
"routeAccountEditEmail": 57,
"routeAccountEditEmailTokenSubmit": 58,
"routeProfile": 59,
"routeBanSubmit": 60,
"routeUnban": 61,
"routeActivate": 62,
"routeIps": 63,
"routeDynamic": 64,
"routeUploads": 65,
"routePanelAnalyticsPosts": 41,
"routePanelGroups": 42,
"routePanelGroupsEdit": 43,
"routePanelGroupsEditPerms": 44,
"routePanelGroupsEditSubmit": 45,
"routePanelGroupsEditPermsSubmit": 46,
"routePanelGroupsCreateSubmit": 47,
"routePanelBackups": 48,
"routePanelLogsMod": 49,
"routePanelDebug": 50,
"routePanel": 51,
"routeAccountEditCritical": 52,
"routeAccountEditCriticalSubmit": 53,
"routeAccountEditAvatar": 54,
"routeAccountEditAvatarSubmit": 55,
"routeAccountEditUsername": 56,
"routeAccountEditUsernameSubmit": 57,
"routeAccountEditEmail": 58,
"routeAccountEditEmailTokenSubmit": 59,
"routeProfile": 60,
"routeBanSubmit": 61,
"routeUnban": 62,
"routeActivate": 63,
"routeIps": 64,
"routeTopicCreateSubmit": 65,
"routeEditTopicSubmit": 66,
"routeDeleteTopicSubmit": 67,
"routeStickTopicSubmit": 68,
"routeUnstickTopicSubmit": 69,
"routeLockTopicSubmit": 70,
"routeUnlockTopicSubmit": 71,
"routeMoveTopicSubmit": 72,
"routeLikeTopicSubmit": 73,
"routeTopicID": 74,
"routeCreateReplySubmit": 75,
"routeReplyEditSubmit": 76,
"routeReplyDeleteSubmit": 77,
"routeReplyLikeSubmit": 78,
"routeDynamic": 79,
"routeUploads": 80,
}
var reverseRouteMapEnum = map[int]string{
0: "routeAPI",
@ -194,31 +224,46 @@ var reverseRouteMapEnum = map[int]string{
38: "routePanelAnalyticsAgents",
39: "routePanelAnalyticsRouteViews",
40: "routePanelAnalyticsAgentViews",
41: "routePanelGroups",
42: "routePanelGroupsEdit",
43: "routePanelGroupsEditPerms",
44: "routePanelGroupsEditSubmit",
45: "routePanelGroupsEditPermsSubmit",
46: "routePanelGroupsCreateSubmit",
47: "routePanelBackups",
48: "routePanelLogsMod",
49: "routePanelDebug",
50: "routePanel",
51: "routeAccountEditCritical",
52: "routeAccountEditCriticalSubmit",
53: "routeAccountEditAvatar",
54: "routeAccountEditAvatarSubmit",
55: "routeAccountEditUsername",
56: "routeAccountEditUsernameSubmit",
57: "routeAccountEditEmail",
58: "routeAccountEditEmailTokenSubmit",
59: "routeProfile",
60: "routeBanSubmit",
61: "routeUnban",
62: "routeActivate",
63: "routeIps",
64: "routeDynamic",
65: "routeUploads",
41: "routePanelAnalyticsPosts",
42: "routePanelGroups",
43: "routePanelGroupsEdit",
44: "routePanelGroupsEditPerms",
45: "routePanelGroupsEditSubmit",
46: "routePanelGroupsEditPermsSubmit",
47: "routePanelGroupsCreateSubmit",
48: "routePanelBackups",
49: "routePanelLogsMod",
50: "routePanelDebug",
51: "routePanel",
52: "routeAccountEditCritical",
53: "routeAccountEditCriticalSubmit",
54: "routeAccountEditAvatar",
55: "routeAccountEditAvatarSubmit",
56: "routeAccountEditUsername",
57: "routeAccountEditUsernameSubmit",
58: "routeAccountEditEmail",
59: "routeAccountEditEmailTokenSubmit",
60: "routeProfile",
61: "routeBanSubmit",
62: "routeUnban",
63: "routeActivate",
64: "routeIps",
65: "routeTopicCreateSubmit",
66: "routeEditTopicSubmit",
67: "routeDeleteTopicSubmit",
68: "routeStickTopicSubmit",
69: "routeUnstickTopicSubmit",
70: "routeLockTopicSubmit",
71: "routeUnlockTopicSubmit",
72: "routeMoveTopicSubmit",
73: "routeLikeTopicSubmit",
74: "routeTopicID",
75: "routeCreateReplySubmit",
76: "routeReplyEditSubmit",
77: "routeReplyDeleteSubmit",
78: "routeReplyLikeSubmit",
79: "routeDynamic",
80: "routeUploads",
}
var agentMapEnum = map[string]int{
"unknown": 0,
@ -339,6 +384,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
if prefix == "/static" {
@ -389,6 +435,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if common.Dev.DebugMode {
log.Print("Blank UA: ", req.UserAgent())
log.Print("Method: ", req.Method)
for key, value := range req.Header {
for _, vvalue := range value {
log.Print("Header '" + key + "': " + vvalue + "!!")
@ -398,6 +445,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
default:
common.AgentViewCounter.Bump(0)
@ -413,6 +461,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
}
@ -724,14 +773,23 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
case "/panel/analytics/agent/":
common.RouteViewCounter.Bump(40)
err = routePanelAnalyticsAgentViews(w,req,user,extraData)
case "/panel/groups/":
case "/panel/analytics/posts/":
err = common.ParseForm(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(41)
err = routePanelAnalyticsPosts(w,req,user)
case "/panel/groups/":
common.RouteViewCounter.Bump(42)
err = routePanelGroups(w,req,user)
case "/panel/groups/edit/":
common.RouteViewCounter.Bump(42)
common.RouteViewCounter.Bump(43)
err = routePanelGroupsEdit(w,req,user,extraData)
case "/panel/groups/edit/perms/":
common.RouteViewCounter.Bump(43)
common.RouteViewCounter.Bump(44)
err = routePanelGroupsEditPerms(w,req,user,extraData)
case "/panel/groups/edit/submit/":
err = common.NoSessionMismatch(w,req,user)
@ -740,7 +798,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(44)
common.RouteViewCounter.Bump(45)
err = routePanelGroupsEditSubmit(w,req,user,extraData)
case "/panel/groups/edit/perms/submit/":
err = common.NoSessionMismatch(w,req,user)
@ -749,7 +807,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(45)
common.RouteViewCounter.Bump(46)
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
case "/panel/groups/create/":
err = common.NoSessionMismatch(w,req,user)
@ -758,7 +816,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(46)
common.RouteViewCounter.Bump(47)
err = routePanelGroupsCreateSubmit(w,req,user)
case "/panel/backups/":
err = common.SuperAdminOnly(w,req,user)
@ -767,10 +825,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(47)
common.RouteViewCounter.Bump(48)
err = routePanelBackups(w,req,user,extraData)
case "/panel/logs/mod/":
common.RouteViewCounter.Bump(48)
common.RouteViewCounter.Bump(49)
err = routePanelLogsMod(w,req,user)
case "/panel/debug/":
err = common.AdminOnly(w,req,user)
@ -779,10 +837,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(49)
common.RouteViewCounter.Bump(50)
err = routePanelDebug(w,req,user)
default:
common.RouteViewCounter.Bump(50)
common.RouteViewCounter.Bump(51)
err = routePanel(w,req,user)
}
if err != nil {
@ -797,7 +855,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(51)
common.RouteViewCounter.Bump(52)
err = routeAccountEditCritical(w,req,user)
case "/user/edit/critical/submit/":
err = common.NoSessionMismatch(w,req,user)
@ -812,7 +870,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(52)
common.RouteViewCounter.Bump(53)
err = routeAccountEditCriticalSubmit(w,req,user)
case "/user/edit/avatar/":
err = common.MemberOnly(w,req,user)
@ -821,7 +879,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(53)
common.RouteViewCounter.Bump(54)
err = routeAccountEditAvatar(w,req,user)
case "/user/edit/avatar/submit/":
err = common.MemberOnly(w,req,user)
@ -830,7 +888,18 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(54)
err = common.HandleUploadRoute(w,req,user,common.Config.MaxRequestSize)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.NoUploadSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(55)
err = routeAccountEditAvatarSubmit(w,req,user)
case "/user/edit/username/":
err = common.MemberOnly(w,req,user)
@ -839,7 +908,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(55)
common.RouteViewCounter.Bump(56)
err = routeAccountEditUsername(w,req,user)
case "/user/edit/username/submit/":
err = common.NoSessionMismatch(w,req,user)
@ -854,7 +923,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(56)
common.RouteViewCounter.Bump(57)
err = routeAccountEditUsernameSubmit(w,req,user)
case "/user/edit/email/":
err = common.MemberOnly(w,req,user)
@ -863,7 +932,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(57)
common.RouteViewCounter.Bump(58)
err = routeAccountEditEmail(w,req,user)
case "/user/edit/token/":
err = common.NoSessionMismatch(w,req,user)
@ -878,11 +947,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(58)
common.RouteViewCounter.Bump(59)
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
default:
req.URL.Path += extraData
common.RouteViewCounter.Bump(59)
common.RouteViewCounter.Bump(60)
err = routeProfile(w,req,user)
}
if err != nil {
@ -903,7 +972,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(60)
common.RouteViewCounter.Bump(61)
err = routeBanSubmit(w,req,user,extraData)
case "/users/unban/":
err = common.NoSessionMismatch(w,req,user)
@ -918,7 +987,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(61)
common.RouteViewCounter.Bump(62)
err = routeUnban(w,req,user,extraData)
case "/users/activate/":
err = common.NoSessionMismatch(w,req,user)
@ -933,7 +1002,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(62)
common.RouteViewCounter.Bump(63)
err = routeActivate(w,req,user,extraData)
case "/users/ips/":
err = common.MemberOnly(w,req,user)
@ -942,12 +1011,229 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
common.RouteViewCounter.Bump(63)
common.RouteViewCounter.Bump(64)
err = routeIps(w,req,user)
}
if err != nil {
router.handleError(err,w,req,user)
}
case "/topic":
switch(req.URL.Path) {
case "/topic/create/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(65)
err = routeTopicCreateSubmit(w,req,user)
case "/topic/edit/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(66)
err = routeEditTopicSubmit(w,req,user,extraData)
case "/topic/delete/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
req.URL.Path += extraData
common.RouteViewCounter.Bump(67)
err = routeDeleteTopicSubmit(w,req,user)
case "/topic/stick/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(68)
err = routeStickTopicSubmit(w,req,user,extraData)
case "/topic/unstick/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(69)
err = routeUnstickTopicSubmit(w,req,user,extraData)
case "/topic/lock/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
req.URL.Path += extraData
common.RouteViewCounter.Bump(70)
err = routeLockTopicSubmit(w,req,user)
case "/topic/unlock/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(71)
err = routeUnlockTopicSubmit(w,req,user,extraData)
case "/topic/move/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(72)
err = routeMoveTopicSubmit(w,req,user,extraData)
case "/topic/like/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(73)
err = routeLikeTopicSubmit(w,req,user,extraData)
default:
common.RouteViewCounter.Bump(74)
err = routeTopicID(w,req,user, extraData)
}
if err != nil {
router.handleError(err,w,req,user)
}
case "/reply":
switch(req.URL.Path) {
case "/reply/create/":
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.HandleUploadRoute(w,req,user,common.Config.MaxRequestSize)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.NoUploadSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(75)
err = routeCreateReplySubmit(w,req,user)
case "/reply/edit/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(76)
err = routeReplyEditSubmit(w,req,user,extraData)
case "/reply/delete/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(77)
err = routeReplyDeleteSubmit(w,req,user,extraData)
case "/reply/like/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
err = common.MemberOnly(w,req,user)
if err != nil {
router.handleError(err,w,req,user)
return
}
common.RouteViewCounter.Bump(78)
err = routeReplyLikeSubmit(w,req,user,extraData)
}
if err != nil {
router.handleError(err,w,req,user)
}
/*case "/sitemaps": // TODO: Count these views
req.URL.Path += extraData
err = sitemapSwitch(w,req)
@ -959,7 +1245,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
common.NotFound(w,req)
return
}
common.RouteViewCounter.Bump(65)
common.RouteViewCounter.Bump(80)
req.URL.Path += extraData
// TODO: Find a way to propagate errors up from this?
router.UploadHandler(w,req) // TODO: Count these views
@ -1003,7 +1289,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.RUnlock()
if ok {
common.RouteViewCounter.Bump(64) // TODO: Be more specific about *which* dynamic route it is
common.RouteViewCounter.Bump(79) // TODO: Be more specific about *which* dynamic route it is
req.URL.Path += extraData
err = handle(w,req,user)
if err != nil {

22
main.go
View File

@ -80,7 +80,7 @@ func afterDBInit() (err error) {
if err != nil {
return err
}
common.GlobalViewCounter, err = common.NewChunkedViewCounter()
common.GlobalViewCounter, err = common.NewGlobalViewCounter()
if err != nil {
return err
}
@ -92,6 +92,10 @@ func afterDBInit() (err error) {
if err != nil {
return err
}
common.PostCounter, err = common.NewPostCounter()
if err != nil {
return err
}
common.TopicViewCounter, err = common.NewDefaultTopicViewCounter()
if err != nil {
return err
@ -299,22 +303,6 @@ func main() {
// TODO: Move these routes into the new routes list
log.Print("Initialising the router")
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
router.HandleFunc("/topic/create/submit/", routeTopicCreateSubmit)
router.HandleFunc("/topic/", routeTopicID)
router.HandleFunc("/reply/create/", routeCreateReply)
//router.HandleFunc("/reply/edit/", routeReplyEdit) // No js fallback
//router.HandleFunc("/reply/delete/", routeReplyDelete) // No js confirmation page? We could have a confirmation modal for the JS case
router.HandleFunc("/reply/edit/submit/", routeReplyEditSubmit)
router.HandleFunc("/reply/delete/submit/", routeReplyDeleteSubmit)
router.HandleFunc("/reply/like/submit/", routeReplyLikeSubmit)
router.HandleFunc("/topic/edit/submit/", routeEditTopic)
router.HandleFunc("/topic/delete/submit/", routeDeleteTopic)
router.HandleFunc("/topic/stick/submit/", routeStickTopic)
router.HandleFunc("/topic/unstick/submit/", routeUnstickTopic)
router.HandleFunc("/topic/lock/submit/", routeLockTopic)
router.HandleFunc("/topic/unlock/submit/", routeUnlockTopic)
router.HandleFunc("/topic/move/submit/", routeMoveTopic)
router.HandleFunc("/topic/like/submit/", routeLikeTopic)
// Accounts
router.HandleFunc("/accounts/login/", routeLogin)

View File

@ -228,24 +228,12 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user common.
}
}
common.PostCounter.Bump()
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func routeCreateReply(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Reduce this to 1MB for attachments for each file?
// TODO: Reuse this code more
if r.ContentLength > int64(common.Config.MaxRequestSize) {
size, unit := common.ConvertByteUnit(float64(common.Config.MaxRequestSize))
return common.CustomError("Your attachments are too big. Your files need to be smaller than "+strconv.Itoa(int(size))+unit+".", http.StatusExpectationFailed, "Error", w, r, user)
}
r.Body = http.MaxBytesReader(w, r.Body, int64(common.Config.MaxRequestSize))
err := r.ParseMultipartForm(int64(common.Megabyte))
if err != nil {
return common.LocalError("Unable to parse the form", w, r, user)
}
func routeCreateReplySubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.PostFormValue("tid"))
if err != nil {
return common.PreError("Failed to convert the Topic ID", w, r)
@ -372,17 +360,14 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user common.User)
if err != nil {
return common.InternalError(err, w, r)
}
common.PostCounter.Bump()
return nil
}
// TODO: Refactor this
func routeLikeTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
tid, err := strconv.Atoi(r.URL.Path[len("/topic/like/submit/"):])
func routeLikeTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("Topic IDs can only ever be numbers.", w, r)
}
@ -442,13 +427,8 @@ func routeLikeTopic(w http.ResponseWriter, r *http.Request, user common.User) co
return nil
}
func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
rid, err := strconv.Atoi(r.URL.Path[len("/reply/like/submit/"):])
func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
rid, err := strconv.Atoi(srid)
if err != nil {
return common.PreError("The provided Reply ID is not a valid number.", w, r)
}
@ -524,10 +504,14 @@ func routeProfileReplyCreate(w http.ResponseWriter, r *http.Request, user common
if err != nil {
return common.LocalError("Bad Form", w, r, user)
}
uid, err := strconv.Atoi(r.PostFormValue("uid"))
if err != nil {
return common.LocalError("Invalid UID", w, r, user)
}
if !common.Users.Exists(uid) {
return common.LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
}
content := common.PreparseMessage(r.PostFormValue("reply-content"))
// TODO: Fully parse the post and store it in the parsed column
@ -536,10 +520,7 @@ func routeProfileReplyCreate(w http.ResponseWriter, r *http.Request, user common
return common.InternalError(err, w, r)
}
if !common.Users.Exists(uid) {
return common.LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
}
common.PostCounter.Bump()
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
return nil
}
@ -629,6 +610,7 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user common.User,
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
common.PostCounter.Bump()
http.Redirect(w, r, "/topic/"+strconv.FormatInt(lastID, 10), http.StatusSeeOther)
return nil
@ -719,20 +701,10 @@ func routeAccountEditAvatar(w http.ResponseWriter, r *http.Request, user common.
}
func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
if r.ContentLength > int64(common.Config.MaxRequestSize) {
size, unit := common.ConvertByteUnit(float64(common.Config.MaxRequestSize))
return common.CustomError("Your avatar's too big. Avatars must be smaller than "+strconv.Itoa(int(size))+unit, http.StatusExpectationFailed, "Error", w, r, user)
}
r.Body = http.MaxBytesReader(w, r.Body, int64(common.Config.MaxRequestSize))
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
err := r.ParseMultipartForm(int64(common.Megabyte))
if err != nil {
return common.LocalError("Upload failed", w, r, user)
}
var filename, ext string
for _, fheaders := range r.MultipartForm.File {
@ -783,7 +755,7 @@ func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c
}
}
err = user.ChangeAvatar("." + ext)
err := user.ChangeAvatar("." + ext)
if err != nil {
return common.InternalError(err, w, r)
}

View File

@ -15,15 +15,10 @@ import (
// 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
// TODO: Make sure this route is member only
func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
func routeEditTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
isJs := (r.PostFormValue("js") == "1")
tid, err := strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreErrorJSQ("The provided TopicID is not a valid number.", w, r, isJs)
}
@ -64,8 +59,7 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) co
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Make sure this route is member only
func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
func routeDeleteTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
@ -131,8 +125,8 @@ func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user common.User)
return nil
}
func routeStickTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/stick/submit/"):])
func routeStickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
@ -170,8 +164,8 @@ func routeStickTopic(w http.ResponseWriter, r *http.Request, user common.User) c
return nil
}
func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unstick/submit/"):])
func routeUnstickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
@ -210,7 +204,7 @@ func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user common.User)
return nil
}
func routeLockTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
func routeLockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
@ -272,8 +266,8 @@ func routeLockTopic(w http.ResponseWriter, r *http.Request, user common.User) co
return nil
}
func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unlock/submit/"):])
func routeUnlockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
tid, err := strconv.Atoi(stid)
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
@ -312,19 +306,32 @@ func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user common.User)
return nil
}
func routeMoveTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
return common.NoPermissions(w, r, user)
// ! JS only route
// TODO: Figure a way to get this route to work without JS
func routeMoveTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
// Not fully implemented
return common.NoPermissionsJS(w, r, user)
tid, err := strconv.Atoi(r.URL.Path[len("/topic/move/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
// TODO: Move this to some sort of middleware
var tids []int
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
if len(tids) == 0 {
return common.LocalErrorJS("You haven't provided any IDs", w, r)
}
fid := 0
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to move doesn't exist.", w, r)
return common.PreErrorJS("The topic you tried to move doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
return common.InternalErrorJS(err, w, r)
}
// TODO: Add hooks to make use of headerLite
@ -332,38 +339,37 @@ func routeMoveTopic(w http.ResponseWriter, r *http.Request, user common.User) co
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic { // TODO: MoveTopic permission?
return common.NoPermissions(w, r, user)
if !user.Perms.ViewTopic || !user.IsSuperMod { // TODO: Add a MoveTo permission
return common.NoPermissionsJS(w, r, user)
}
err = topic.Unlock()
err = topic.MoveTo(fid)
if err != nil {
return common.InternalError(err, w, r)
return common.InternalErrorJS(err, w, r)
}
err = common.ModLogs.Create("move", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
return common.InternalErrorJS(err, w, r)
}
err = topic.CreateActionReply("move", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
return common.InternalErrorJS(err, w, r)
}
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
if len(tids) == 1 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
}
return nil
}
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("js") == "1")
rid, err := strconv.Atoi(r.URL.Path[len("/reply/edit/submit/"):])
rid, err := strconv.Atoi(srid)
if err != nil {
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
}
@ -408,14 +414,10 @@ func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.Us
// TODO: Refactor this
// TODO: Disable stat updates in posts handled by plugin_guilds
func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User, srid string) common.RouteError {
isJs := (r.PostFormValue("isJs") == "1")
rid, err := strconv.Atoi(r.URL.Path[len("/reply/delete/submit/"):])
rid, err := strconv.Atoi(srid)
if err != nil {
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
}

View File

@ -559,6 +559,52 @@ func routePanelForumsEditPermsAdvanceSubmit(w http.ResponseWriter, r *http.Reque
return panelSuccessRedirect("/panel/forums/edit/perms/"+strconv.Itoa(fid)+"-"+strconv.Itoa(gid), w, r, isJs)
}
type AnalyticsTimeRange struct {
Quantity int
Unit string
Slices int
SliceWidth int
Range string
}
func panelAnalyticsTimeRange(rawTimeRange string) (timeRange AnalyticsTimeRange, err error) {
timeRange.Quantity = 6
timeRange.Unit = "hour"
timeRange.Slices = 12
timeRange.SliceWidth = 60 * 30
timeRange.Range = "six-hours"
switch rawTimeRange {
case "one-month":
timeRange.Quantity = 30
timeRange.Unit = "day"
timeRange.Slices = 30
timeRange.SliceWidth = 60 * 60 * 24
timeRange.Range = "one-month"
case "two-days": // Two days is experimental
timeRange.Quantity = 2
timeRange.Unit = "day"
timeRange.Slices = 24
timeRange.SliceWidth = 60 * 60 * 2
timeRange.Range = "two-days"
case "one-day":
timeRange.Quantity = 1
timeRange.Unit = "day"
timeRange.Slices = 24
timeRange.SliceWidth = 60 * 60
timeRange.Range = "one-day"
case "twelve-hours":
timeRange.Quantity = 12
timeRange.Slices = 24
timeRange.Range = "twelve-hours"
case "six-hours", "":
timeRange.Range = "six-hours"
default:
return timeRange, errors.New("Unknown time range")
}
return timeRange, nil
}
func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
@ -567,6 +613,225 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
if err != nil {
return common.LocalError(err.Error(), w, r, user)
}
var revLabelList []int64
var labelList []int64
var viewMap = make(map[int64]int64)
var currentTime = time.Now().Unix()
for i := 1; i <= timeRange.Slices; i++ {
var label = currentTime - int64(i*timeRange.SliceWidth)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
for _, value := range revLabelList {
labelList = append(labelList, value)
}
var viewList []int64
log.Print("in routePanelAnalyticsViews")
acc := qgen.Builder.Accumulator()
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
err := rows.Scan(&count, &createdAt)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt)
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
var viewItems []common.PanelAnalyticsItem
for _, value := range revLabelList {
viewList = append(viewList, viewMap[value])
viewItems = append(viewItems, common.PanelAnalyticsItem{Time: value, Count: viewMap[value]})
}
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange.Range}
return panelRenderTemplate("panel_analytics_views", w, r, user, &pi)
}
func routePanelAnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
if err != nil {
return common.LocalError(err.Error(), w, r, user)
}
var revLabelList []int64
var labelList []int64
var viewMap = make(map[int64]int64)
var currentTime = time.Now().Unix()
for i := 1; i <= timeRange.Slices; i++ {
var label = currentTime - int64(i*timeRange.SliceWidth)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
for _, value := range revLabelList {
labelList = append(labelList, value)
}
var viewList []int64
log.Print("in routePanelAnalyticsRouteViews")
acc := qgen.Builder.Accumulator()
// TODO: Validate the route is valid
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(route)
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
err := rows.Scan(&count, &createdAt)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt)
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
var viewItems []common.PanelAnalyticsItem
for _, value := range revLabelList {
viewList = append(viewList, viewMap[value])
viewItems = append(viewItems, common.PanelAnalyticsItem{Time: value, Count: viewMap[value]})
}
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(route), graph, viewItems, timeRange.Range}
return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi)
}
func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
if err != nil {
return common.LocalError(err.Error(), w, r, user)
}
var revLabelList []int64
var labelList []int64
var viewMap = make(map[int64]int64)
var currentTime = time.Now().Unix()
for i := 1; i <= timeRange.Slices; i++ {
var label = currentTime - int64(i*timeRange.SliceWidth)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
for _, value := range revLabelList {
labelList = append(labelList, value)
}
var viewList []int64
log.Print("in routePanelAnalyticsAgentViews")
acc := qgen.Builder.Accumulator()
// TODO: Verify the agent is valid
rows, err := acc.Select("viewchunks_agents").Columns("count, createdAt").Where("browser = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(agent)
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
err := rows.Scan(&count, &createdAt)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt)
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
for _, value := range revLabelList {
viewList = append(viewList, viewMap[value])
}
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(agent), graph, timeRange.Range}
return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi)
}
func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
var timeQuantity = 6
var timeUnit = "hour"
var timeSlices = 12
@ -617,10 +882,10 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo
}
var viewList []int64
log.Print("in routePanelAnalyticsViews")
log.Print("in routePanelAnalyticsPosts")
acc := qgen.Builder.Accumulator()
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeQuantity, timeUnit).Query()
rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeQuantity, timeUnit).Query()
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
@ -659,220 +924,7 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange}
if common.PreRenderHooks["pre_render_panel_analytics"] != nil {
if common.RunPreRenderHook("pre_render_panel_analytics", w, r, &user, &pi) {
return nil
}
}
err = common.Templates.ExecuteTemplate(w, "panel-analytics-views.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routePanelAnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user common.User, route string) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
var timeQuantity = 6
var timeUnit = "hour"
var timeSlices = 12
var sliceWidth = 60 * 30
var timeRange = "six-hours"
switch r.FormValue("timeRange") {
case "one-month":
timeQuantity = 30
timeUnit = "day"
timeSlices = 30
sliceWidth = 60 * 60 * 24
timeRange = "one-month"
case "two-days": // Two days is experimental
timeQuantity = 2
timeUnit = "day"
timeSlices = 24
sliceWidth = 60 * 60 * 2
timeRange = "two-days"
case "one-day":
timeQuantity = 1
timeUnit = "day"
timeSlices = 24
sliceWidth = 60 * 60
timeRange = "one-day"
case "twelve-hours":
timeQuantity = 12
timeSlices = 24
timeRange = "twelve-hours"
case "six-hours", "":
timeRange = "six-hours"
default:
return common.LocalError("Unknown time range", w, r, user)
}
var revLabelList []int64
var labelList []int64
var viewMap = make(map[int64]int64)
var currentTime = time.Now().Unix()
for i := 1; i <= timeSlices; i++ {
var label = currentTime - int64(i*sliceWidth)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
for _, value := range revLabelList {
labelList = append(labelList, value)
}
var viewList []int64
log.Print("in routePanelAnalyticsRouteViews")
acc := qgen.Builder.Accumulator()
// TODO: Validate the route is valid
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ?").DateCutoff("createdAt", timeQuantity, timeUnit).Query(route)
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
err := rows.Scan(&count, &createdAt)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt)
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
for _, value := range revLabelList {
viewList = append(viewList, viewMap[value])
}
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsRoutePage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(route), graph, timeRange}
return panelRenderTemplate("panel_analytics_route_views", w, r, user, &pi)
}
func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user common.User, agent string) common.RouteError {
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
var timeQuantity = 6
var timeUnit = "hour"
var timeSlices = 12
var sliceWidth = 60 * 30
var timeRange = "six-hours"
switch r.FormValue("timeRange") {
case "one-month":
timeQuantity = 30
timeUnit = "day"
timeSlices = 30
sliceWidth = 60 * 60 * 24
timeRange = "one-month"
case "two-days": // Two days is experimental
timeQuantity = 2
timeUnit = "day"
timeSlices = 24
sliceWidth = 60 * 60 * 2
timeRange = "two-days"
case "one-day":
timeQuantity = 1
timeUnit = "day"
timeSlices = 24
sliceWidth = 60 * 60
timeRange = "one-day"
case "twelve-hours":
timeQuantity = 12
timeSlices = 24
timeRange = "twelve-hours"
case "six-hours", "":
timeRange = "six-hours"
default:
return common.LocalError("Unknown time range", w, r, user)
}
var revLabelList []int64
var labelList []int64
var viewMap = make(map[int64]int64)
var currentTime = time.Now().Unix()
for i := 1; i <= timeSlices; i++ {
var label = currentTime - int64(i*sliceWidth)
revLabelList = append(revLabelList, label)
viewMap[label] = 0
}
for _, value := range revLabelList {
labelList = append(labelList, value)
}
var viewList []int64
log.Print("in routePanelAnalyticsAgentViews")
acc := qgen.Builder.Accumulator()
// TODO: Verify the agent is valid
rows, err := acc.Select("viewchunks_agents").Columns("count, createdAt").Where("browser = ?").DateCutoff("createdAt", timeQuantity, timeUnit).Query(agent)
if err != nil && err != ErrNoRows {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
var count int64
var createdAt time.Time
err := rows.Scan(&count, &createdAt)
if err != nil {
return common.InternalError(err, w, r)
}
log.Print("count: ", count)
log.Print("createdAt: ", createdAt)
var unixCreatedAt = createdAt.Unix()
log.Print("unixCreatedAt: ", unixCreatedAt)
for _, value := range labelList {
if unixCreatedAt > value {
viewMap[value] += count
break
}
}
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
for _, value := range revLabelList {
viewList = append(viewList, viewMap[value])
}
graph := common.PanelTimeGraph{Series: viewList, Labels: labelList}
log.Printf("graph: %+v\n", graph)
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", html.EscapeString(agent), graph, timeRange}
return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi)
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
}
func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {

View File

@ -6,6 +6,9 @@ var conn;
var selectedTopics = [];
var attachItemCallback = function(){}
// Topic move
var forumToMoveTo = 0;
// TODO: Write a friendlier error handler which uses a .notice or something, we could have a specialised one for alerts
function ajaxError(xhr,status,errstr) {
console.log("The AJAX request failed");
@ -500,6 +503,20 @@ $(document).ready(function(){
$(".mod_floater").removeClass("auto_hide");
});
});
let bulkActionSender = function(action, selectedTopics) {
let url = "/topic/"+action+"/submit/?session=" + session;
$.ajax({
url: url,
type: "POST",
data: JSON.stringify(selectedTopics),
contentType: "application/json",
error: ajaxError,
success: function() {
window.location.reload();
}
});
};
$(".mod_floater_submit").click(function(event){
event.preventDefault();
let selectNode = this.form.querySelector(".mod_floater_options");
@ -511,22 +528,24 @@ $(document).ready(function(){
switch(action) {
case "move":
console.log("move action");
let modTopicMover = $("#mod_topic_mover");
$("#mod_topic_mover").removeClass("auto_hide");
$("#mod_topic_mover .pane_row").click(function(){
modTopicMover.find(".pane_row").removeClass("pane_selected");
let fid = this.getAttribute("data-fid");
if (fid == null) {
return;
}
this.classList.add("pane_selected");
console.log("fid: " + fid);
let moverFid = document.getElementById("#mover_fid");
console.log("moverFid: ", moverFid);
moverFid.value = fid;
});
return;
}
let url = "/topic/"+action+"/submit/";
//console.log("JSON.stringify(selectedTopics) ", JSON.stringify(selectedTopics));
$.ajax({
url: url,
type: "POST",
data: JSON.stringify(selectedTopics),
contentType: "application/json",
error: ajaxError,
success: function() {
window.location.reload();
}
});
bulkActionSender(action,selectedTopics);
});
});

View File

@ -386,6 +386,15 @@ func createTables(adapter qgen.Adapter) error {
)
*/
qgen.Install.CreateTable("postchunks", "", "",
[]qgen.DBTableColumn{
qgen.DBTableColumn{"count", "int", 0, false, false, "0"},
qgen.DBTableColumn{"createdAt", "datetime", 0, false, false, ""},
// TODO: Add a column for the parent topic / profile?
},
[]qgen.DBTableKey{},
)
qgen.Install.CreateTable("sync", "", "",
[]qgen.DBTableColumn{
qgen.DBTableColumn{"last_update", "datetime", 0, false, false, ""},

View File

@ -299,6 +299,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
if prefix == "/static" {
@ -349,6 +350,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if common.Dev.DebugMode {
log.Print("Blank UA: ", req.UserAgent())
log.Print("Method: ", req.Method)
for key, value := range req.Header {
for _, vvalue := range value {
log.Print("Header '" + key + "': " + vvalue + "!!")
@ -358,6 +360,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
default:
common.AgentViewCounter.Bump({{.AllAgentMap.unknown}})
@ -373,6 +376,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
log.Print("req.URL.Path: ", req.URL.Path)
log.Print("extraData: ", extraData)
log.Print("req.Referer(): ", req.Referer())
log.Print("req.RemoteAddr: ", req.RemoteAddr)
}
}

View File

@ -95,10 +95,25 @@ func AnonAction(fname string, path string, args ...string) *RouteImpl {
return route(fname, path, args...).Before("ParseForm")
}
func UploadAction(fname string, path string, args ...string) *RouteImpl {
// Make this it's own type to force the user to manipulate methods on it to set parameters
type uploadAction struct {
Route *RouteImpl
}
func UploadAction(fname string, path string, args ...string) *uploadAction {
route := route(fname, path, args...)
if !route.hasBefore("SuperModOnly", "AdminOnly") {
route.Before("MemberOnly")
}
return route
return &uploadAction{route}
}
func (action *uploadAction) MaxSizeVar(varName string) *RouteImpl {
action.Route.LitBefore(`err = common.HandleUploadRoute(w,req,user,` + varName + `)
if err != nil {
router.handleError(err,w,req,user)
return
}`)
action.Route.Before("NoUploadSessionMismatch")
return action.Route
}

View File

@ -26,6 +26,8 @@ func routes() {
buildPanelRoutes()
buildUserRoutes()
buildTopicRoutes()
buildReplyRoutes()
}
// TODO: Test the email token route
@ -36,7 +38,7 @@ func buildUserRoutes() {
MemberView("routeAccountEditCritical", "/user/edit/critical/"),
Action("routeAccountEditCriticalSubmit", "/user/edit/critical/submit/"), // TODO: Full test this
MemberView("routeAccountEditAvatar", "/user/edit/avatar/"),
UploadAction("routeAccountEditAvatarSubmit", "/user/edit/avatar/submit/"),
UploadAction("routeAccountEditAvatarSubmit", "/user/edit/avatar/submit/").MaxSizeVar("common.Config.MaxRequestSize"),
MemberView("routeAccountEditUsername", "/user/edit/username/"),
Action("routeAccountEditUsernameSubmit", "/user/edit/username/submit/"), // TODO: Full test this
MemberView("routeAccountEditEmail", "/user/edit/email/"),
@ -55,6 +57,37 @@ func buildUserRoutes() {
addRouteGroup(userGroup)
}
func buildTopicRoutes() {
topicGroup := newRouteGroup("/topic/")
topicGroup.Routes(
View("routeTopicID", "/topic/", "extraData"),
Action("routeTopicCreateSubmit", "/topic/create/submit/"),
Action("routeEditTopicSubmit", "/topic/edit/submit/", "extraData"),
Action("routeDeleteTopicSubmit", "/topic/delete/submit/").LitBefore("req.URL.Path += extraData"),
Action("routeStickTopicSubmit", "/topic/stick/submit/", "extraData"),
Action("routeUnstickTopicSubmit", "/topic/unstick/submit/", "extraData"),
Action("routeLockTopicSubmit", "/topic/lock/submit/").LitBefore("req.URL.Path += extraData"),
Action("routeUnlockTopicSubmit", "/topic/unlock/submit/", "extraData"),
Action("routeMoveTopicSubmit", "/topic/move/submit/", "extraData"),
Action("routeLikeTopicSubmit", "/topic/like/submit/", "extraData"),
)
addRouteGroup(topicGroup)
}
func buildReplyRoutes() {
//router.HandleFunc("/reply/edit/", routeReplyEdit) // No js fallback
//router.HandleFunc("/reply/delete/", routeReplyDelete) // No js confirmation page? We could have a confirmation modal for the JS case
replyGroup := newRouteGroup("/reply/")
replyGroup.Routes(
// TODO: Reduce this to 1MB for attachments for each file?
UploadAction("routeCreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/
Action("routeReplyEditSubmit", "/reply/edit/submit/", "extraData"),
Action("routeReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData"),
)
addRouteGroup(replyGroup)
}
func buildPanelRoutes() {
panelGroup := newRouteGroup("/panel/").Before("SuperModOnly")
panelGroup.Routes(
@ -96,6 +129,7 @@ func buildPanelRoutes() {
View("routePanelAnalyticsAgents", "/panel/analytics/agents/"),
View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"),
View("routePanelAnalyticsAgentViews", "/panel/analytics/agent/", "extraData"),
View("routePanelAnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"),
View("routePanelGroups", "/panel/groups/"),
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),

View File

@ -438,7 +438,7 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo
return nil
}
func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User, urlBit string) common.RouteError {
var err error
var page, offset int
var replyList []common.ReplyUser
@ -447,7 +447,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
// SEO URLs...
// TODO: Make a shared function for this
halves := strings.Split(r.URL.Path[len("/topic/"):], ".")
halves := strings.Split(urlBit, ".")
if len(halves) < 2 {
halves = append(halves, halves[0])
}

View File

@ -0,0 +1,4 @@
CREATE TABLE [postchunks] (
[count] int DEFAULT 0 not null,
[createdAt] datetime not null
);

View File

@ -0,0 +1,4 @@
CREATE TABLE `postchunks` (
`count` int DEFAULT 0 not null,
`createdAt` datetime not null
);

View File

@ -0,0 +1,4 @@
CREATE TABLE `postchunks` (
`count` int DEFAULT 0 not null,
`createdAt` timestamp not null
);

View File

@ -87,165 +87,176 @@ var header_21 = []byte(`</div>`)
var topic_0 = []byte(`
<form id="edit_topic_form" action='/topic/edit/submit/`)
var topic_1 = []byte(`' method="post"></form>
var topic_1 = []byte(`?session=`)
var topic_2 = []byte(`' method="post"></form>
`)
var topic_2 = []byte(`<link rel="prev" href="/topic/`)
var topic_3 = []byte(`?page=`)
var topic_4 = []byte(`" />
var topic_3 = []byte(`<link rel="prev" href="/topic/`)
var topic_4 = []byte(`?page=`)
var topic_5 = []byte(`" />
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="Go to the previous page" rel="prev" href="/topic/`)
var topic_5 = []byte(`?page=`)
var topic_6 = []byte(`">&lt;</a></div>`)
var topic_7 = []byte(`<link rel="prerender next" href="/topic/`)
var topic_8 = []byte(`?page=`)
var topic_9 = []byte(`" />
var topic_6 = []byte(`?page=`)
var topic_7 = []byte(`">&lt;</a></div>`)
var topic_8 = []byte(`<link rel="prerender next" href="/topic/`)
var topic_9 = []byte(`?page=`)
var topic_10 = []byte(`" />
<div id="nextFloat" class="next_button">
<a class="next_link" aria-label="Go to the next page" rel="next" href="/topic/`)
var topic_10 = []byte(`?page=`)
var topic_11 = []byte(`">&gt;</a>
var topic_11 = []byte(`?page=`)
var topic_12 = []byte(`">&gt;</a>
</div>`)
var topic_12 = []byte(`
var topic_13 = []byte(`
<main>
<div class="rowblock rowhead topic_block" aria-label="The opening post of this topic">
<div class="rowitem topic_item`)
var topic_13 = []byte(` topic_sticky_head`)
var topic_14 = []byte(` topic_closed_head`)
var topic_15 = []byte(`">
var topic_14 = []byte(` topic_sticky_head`)
var topic_15 = []byte(` topic_closed_head`)
var topic_16 = []byte(`">
<h1 class='topic_name hide_on_edit'>`)
var topic_16 = []byte(`</h1>
var topic_17 = []byte(`</h1>
`)
var topic_17 = []byte(`<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='Status: Closed'>&#x1F512;&#xFE0E</span>`)
var topic_18 = []byte(`
var topic_18 = []byte(`<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='Status: Closed'>&#x1F512;&#xFE0E</span>`)
var topic_19 = []byte(`
<input form='edit_topic_form' class='show_on_edit topic_name_input' name="topic_name" value='`)
var topic_19 = []byte(`' type="text" />
var topic_20 = []byte(`' type="text" />
<button form='edit_topic_form' name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
`)
var topic_20 = []byte(`
var topic_21 = []byte(`
</div>
</div>
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="The opening post for this topic">
<div class="rowitem passive editable_parent post_item `)
var topic_21 = []byte(`" style="background-image: url(`)
var topic_22 = []byte(`), url(/static/`)
var topic_23 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
var topic_24 = []byte(`-1`)
var topic_25 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
var topic_22 = []byte(`" style="background-image: url(`)
var topic_23 = []byte(`), url(/static/`)
var topic_24 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
var topic_25 = []byte(`-1`)
var topic_26 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
<p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">`)
var topic_26 = []byte(`</p>
var topic_27 = []byte(`</p>
<textarea name="topic_content" class="show_on_edit topic_content_input">`)
var topic_27 = []byte(`</textarea>
var topic_28 = []byte(`</textarea>
<span class="controls" aria-label="Controls and Author Information">
<a href="`)
var topic_28 = []byte(`" class="username real_username" rel="author">`)
var topic_29 = []byte(`</a>&nbsp;&nbsp;
var topic_29 = []byte(`" class="username real_username" rel="author">`)
var topic_30 = []byte(`</a>&nbsp;&nbsp;
`)
var topic_30 = []byte(`<a href="/topic/like/submit/`)
var topic_31 = []byte(`" class="mod_button" title="Love it" `)
var topic_32 = []byte(`aria-label="Unlike this topic"`)
var topic_33 = []byte(`aria-label="Like this topic"`)
var topic_34 = []byte(` style="color:#202020;">
var topic_31 = []byte(`<a href="/topic/like/submit/`)
var topic_32 = []byte(`?session=`)
var topic_33 = []byte(`" class="mod_button" title="Love it" `)
var topic_34 = []byte(`aria-label="Unlike this topic"`)
var topic_35 = []byte(`aria-label="Like this topic"`)
var topic_36 = []byte(` style="color:#202020;">
<button class="username like_label"`)
var topic_35 = []byte(` style="background-color:#D6FFD6;"`)
var topic_36 = []byte(`></button></a>`)
var topic_37 = []byte(`<a href='/topic/edit/`)
var topic_38 = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>`)
var topic_39 = []byte(`<a href='/topic/delete/submit/`)
var topic_40 = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>`)
var topic_41 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
var topic_42 = []byte(`' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>`)
var topic_43 = []byte(`<a href='/topic/lock/submit/`)
var topic_44 = []byte(`' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>`)
var topic_45 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
var topic_46 = []byte(`' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>`)
var topic_47 = []byte(`<a href='/topic/stick/submit/`)
var topic_48 = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>`)
var topic_49 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
var topic_50 = []byte(`' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is `)
var topic_51 = []byte(`"><button class="username ip_label"></button></a>`)
var topic_52 = []byte(`
var topic_37 = []byte(` style="background-color:#D6FFD6;"`)
var topic_38 = []byte(`></button></a>`)
var topic_39 = []byte(`<a href='/topic/edit/`)
var topic_40 = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>`)
var topic_41 = []byte(`<a href='/topic/delete/submit/`)
var topic_42 = []byte(`?session=`)
var topic_43 = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>`)
var topic_44 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
var topic_45 = []byte(`?session=`)
var topic_46 = []byte(`' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>`)
var topic_47 = []byte(`<a href='/topic/lock/submit/`)
var topic_48 = []byte(`?session=`)
var topic_49 = []byte(`' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>`)
var topic_50 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
var topic_51 = []byte(`?session=`)
var topic_52 = []byte(`' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>`)
var topic_53 = []byte(`<a href='/topic/stick/submit/`)
var topic_54 = []byte(`?session=`)
var topic_55 = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>`)
var topic_56 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
var topic_57 = []byte(`' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is `)
var topic_58 = []byte(`"><button class="username ip_label"></button></a>`)
var topic_59 = []byte(`
<a href="/report/submit/`)
var topic_53 = []byte(`?session=`)
var topic_54 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
var topic_60 = []byte(`?session=`)
var topic_61 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
`)
var topic_55 = []byte(`<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">`)
var topic_56 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
var topic_57 = []byte(`<a class="username hide_on_micro user_tag">`)
var topic_58 = []byte(`</a>`)
var topic_59 = []byte(`<a class="username hide_on_micro level" aria-label="The poster's level">`)
var topic_60 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
var topic_61 = []byte(`
var topic_62 = []byte(`<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">`)
var topic_63 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
var topic_64 = []byte(`<a class="username hide_on_micro user_tag">`)
var topic_65 = []byte(`</a>`)
var topic_66 = []byte(`<a class="username hide_on_micro level" aria-label="The poster's level">`)
var topic_67 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
var topic_68 = []byte(`
</span>
</div>
</article>
<div class="rowblock post_container" aria-label="The current page for this topic" style="overflow: hidden;">`)
var topic_62 = []byte(`
var topic_69 = []byte(`
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item action_item">
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
var topic_63 = []byte(`</span>
var topic_70 = []byte(`</span>
<span itemprop="text">`)
var topic_64 = []byte(`</span>
var topic_71 = []byte(`</span>
</article>
`)
var topic_65 = []byte(`
var topic_72 = []byte(`
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item `)
var topic_66 = []byte(`" style="background-image: url(`)
var topic_67 = []byte(`), url(/static/`)
var topic_68 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
var topic_69 = []byte(`-1`)
var topic_70 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
var topic_73 = []byte(`" style="background-image: url(`)
var topic_74 = []byte(`), url(/static/`)
var topic_75 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
var topic_76 = []byte(`-1`)
var topic_77 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
`)
var topic_71 = []byte(`
var topic_78 = []byte(`
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">`)
var topic_72 = []byte(`</p>
var topic_79 = []byte(`</p>
<span class="controls">
<a href="`)
var topic_73 = []byte(`" class="username real_username" rel="author">`)
var topic_74 = []byte(`</a>&nbsp;&nbsp;
var topic_80 = []byte(`" class="username real_username" rel="author">`)
var topic_81 = []byte(`</a>&nbsp;&nbsp;
`)
var topic_75 = []byte(`<a href="/reply/like/submit/`)
var topic_76 = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"`)
var topic_77 = []byte(` style="background-color:#D6FFD6;"`)
var topic_78 = []byte(`></button></a>`)
var topic_79 = []byte(`<a href="/reply/edit/submit/`)
var topic_80 = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>`)
var topic_81 = []byte(`<a href="/reply/delete/submit/`)
var topic_82 = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
var topic_83 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
var topic_84 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
var topic_85 = []byte(`
var topic_82 = []byte(`<a href="/reply/like/submit/`)
var topic_83 = []byte(`?session=`)
var topic_84 = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"`)
var topic_85 = []byte(` style="background-color:#D6FFD6;"`)
var topic_86 = []byte(`></button></a>`)
var topic_87 = []byte(`<a href="/reply/edit/submit/`)
var topic_88 = []byte(`?session=`)
var topic_89 = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>`)
var topic_90 = []byte(`<a href="/reply/delete/submit/`)
var topic_91 = []byte(`?session=`)
var topic_92 = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
var topic_93 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
var topic_94 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
var topic_95 = []byte(`
<a href="/report/submit/`)
var topic_86 = []byte(`?session=`)
var topic_87 = []byte(`&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
var topic_96 = []byte(`?session=`)
var topic_97 = []byte(`&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
`)
var topic_88 = []byte(`<a class="username hide_on_micro like_count">`)
var topic_89 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
var topic_90 = []byte(`<a class="username hide_on_micro user_tag">`)
var topic_91 = []byte(`</a>`)
var topic_92 = []byte(`<a class="username hide_on_micro level">`)
var topic_93 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
var topic_94 = []byte(`
var topic_98 = []byte(`<a class="username hide_on_micro like_count">`)
var topic_99 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
var topic_100 = []byte(`<a class="username hide_on_micro user_tag">`)
var topic_101 = []byte(`</a>`)
var topic_102 = []byte(`<a class="username hide_on_micro level">`)
var topic_103 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
var topic_104 = []byte(`
</span>
</article>
`)
var topic_95 = []byte(`</div>
var topic_105 = []byte(`</div>
`)
var topic_96 = []byte(`
var topic_106 = []byte(`
<div class="rowblock topic_reply_form quick_create_form">
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/?session=`)
var topic_107 = []byte(`" method="post"></form>
<input form="reply_form" name="tid" value='`)
var topic_97 = []byte(`' type="hidden" />
var topic_108 = []byte(`' type="hidden" />
<div class="formrow real_first_child">
<div class="formitem">
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
@ -255,16 +266,16 @@ var topic_97 = []byte(`' type="hidden" />
<div class="formitem">
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
`)
var topic_98 = []byte(`
var topic_109 = []byte(`
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">Add File</label>
<div id="upload_file_dock"></div>`)
var topic_99 = []byte(`
var topic_110 = []byte(`
</div>
</div>
</div>
`)
var topic_100 = []byte(`
var topic_111 = []byte(`
</main>
@ -318,21 +329,22 @@ var topic_alt_10 = []byte(`
<div class="rowblock rowhead topic_block" aria-label="The opening post of this topic">
<form action='/topic/edit/submit/`)
var topic_alt_11 = []byte(`' method="post">
var topic_alt_11 = []byte(`?session=`)
var topic_alt_12 = []byte(`' method="post">
<div class="rowitem topic_item`)
var topic_alt_12 = []byte(` topic_sticky_head`)
var topic_alt_13 = []byte(` topic_closed_head`)
var topic_alt_14 = []byte(`">
var topic_alt_13 = []byte(` topic_sticky_head`)
var topic_alt_14 = []byte(` topic_closed_head`)
var topic_alt_15 = []byte(`">
<h1 class='topic_name hide_on_edit'>`)
var topic_alt_15 = []byte(`</h1>
var topic_alt_16 = []byte(`</h1>
`)
var topic_alt_16 = []byte(`<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='Status: Closed' style="font-weight:normal;float: right;position:relative;top:-5px;">&#x1F512;&#xFE0E</span>`)
var topic_alt_17 = []byte(`
var topic_alt_17 = []byte(`<span class='username hide_on_micro topic_status_e topic_status_closed hide_on_edit' title='Status: Closed' style="font-weight:normal;float: right;position:relative;top:-5px;">&#x1F512;&#xFE0E</span>`)
var topic_alt_18 = []byte(`
<input class='show_on_edit topic_name_input' name="topic_name" value='`)
var topic_alt_18 = []byte(`' type="text" />
var topic_alt_19 = []byte(`' type="text" />
<button name="topic-button" class="formbutton show_on_edit submit_edit">Update</button>
`)
var topic_alt_19 = []byte(`
var topic_alt_20 = []byte(`
</div>
</form>
</div>
@ -341,158 +353,168 @@ var topic_alt_19 = []byte(`
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item top_post" aria-label="The opening post for this topic">
<div class="userinfo" aria-label="The information on the poster">
<div class="avatar_item" style="background-image: url(`)
var topic_alt_20 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
var topic_alt_21 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="`)
var topic_alt_21 = []byte(`" class="the_name" rel="author">`)
var topic_alt_22 = []byte(`</a>
var topic_alt_22 = []byte(`" class="the_name" rel="author">`)
var topic_alt_23 = []byte(`</a>
`)
var topic_alt_23 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_24 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_25 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_26 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_27 = []byte(`
var topic_alt_24 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_25 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_26 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_27 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_28 = []byte(`
</div>
<div class="content_container">
<div class="hide_on_edit topic_content user_content" itemprop="text">`)
var topic_alt_28 = []byte(`</div>
var topic_alt_29 = []byte(`</div>
<textarea name="topic_content" class="show_on_edit topic_content_input">`)
var topic_alt_29 = []byte(`</textarea>
var topic_alt_30 = []byte(`</textarea>
<div class="button_container">
`)
var topic_alt_30 = []byte(`<a href="/topic/like/submit/`)
var topic_alt_31 = []byte(`" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>`)
var topic_alt_32 = []byte(`<a href="/topic/edit/`)
var topic_alt_33 = []byte(`" class="action_button open_edit" aria-label="Edit this post" data-action="edit"></a>`)
var topic_alt_34 = []byte(`<a href="/topic/delete/submit/`)
var topic_alt_35 = []byte(`" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>`)
var topic_alt_36 = []byte(`<a href='/topic/unlock/submit/`)
var topic_alt_37 = []byte(`' class="action_button unlock_item" data-action="unlock"></a>`)
var topic_alt_38 = []byte(`<a href='/topic/lock/submit/`)
var topic_alt_39 = []byte(`' class="action_button lock_item" data-action="lock"></a>`)
var topic_alt_40 = []byte(`<a href='/topic/unstick/submit/`)
var topic_alt_41 = []byte(`' class="action_button unpin_item" data-action="unpin"></a>`)
var topic_alt_42 = []byte(`<a href='/topic/stick/submit/`)
var topic_alt_43 = []byte(`' class="action_button pin_item" data-action="pin"></a>`)
var topic_alt_44 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_45 = []byte(`" title="IP Address" class="action_button ip_item_button hide_on_big" aria-label="This user's IP" data-action="ip"></a>`)
var topic_alt_46 = []byte(`
var topic_alt_31 = []byte(`<a href="/topic/like/submit/`)
var topic_alt_32 = []byte(`?session=`)
var topic_alt_33 = []byte(`" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>`)
var topic_alt_34 = []byte(`<a href="/topic/edit/`)
var topic_alt_35 = []byte(`" class="action_button open_edit" aria-label="Edit this post" data-action="edit"></a>`)
var topic_alt_36 = []byte(`<a href="/topic/delete/submit/`)
var topic_alt_37 = []byte(`?session=`)
var topic_alt_38 = []byte(`" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>`)
var topic_alt_39 = []byte(`<a href='/topic/unlock/submit/`)
var topic_alt_40 = []byte(`?session=`)
var topic_alt_41 = []byte(`' class="action_button unlock_item" data-action="unlock"></a>`)
var topic_alt_42 = []byte(`<a href='/topic/lock/submit/`)
var topic_alt_43 = []byte(`?session=`)
var topic_alt_44 = []byte(`' class="action_button lock_item" data-action="lock"></a>`)
var topic_alt_45 = []byte(`<a href='/topic/unstick/submit/`)
var topic_alt_46 = []byte(`?session=`)
var topic_alt_47 = []byte(`' class="action_button unpin_item" data-action="unpin"></a>`)
var topic_alt_48 = []byte(`<a href='/topic/stick/submit/`)
var topic_alt_49 = []byte(`?session=`)
var topic_alt_50 = []byte(`' class="action_button pin_item" data-action="pin"></a>`)
var topic_alt_51 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_52 = []byte(`" title="IP Address" class="action_button ip_item_button hide_on_big" aria-label="This user's IP" data-action="ip"></a>`)
var topic_alt_53 = []byte(`
<a href="/report/submit/`)
var topic_alt_47 = []byte(`?session=`)
var topic_alt_48 = []byte(`&type=topic" class="action_button report_item" aria-label="Report this post" data-action="report"></a>
var topic_alt_54 = []byte(`?session=`)
var topic_alt_55 = []byte(`&type=topic" class="action_button report_item" aria-label="Report this post" data-action="report"></a>
<a href="#" class="action_button button_menu"></a>
`)
var topic_alt_49 = []byte(`
var topic_alt_56 = []byte(`
<div class="action_button_right`)
var topic_alt_50 = []byte(` has_likes`)
var topic_alt_51 = []byte(`">
var topic_alt_57 = []byte(` has_likes`)
var topic_alt_58 = []byte(`">
`)
var topic_alt_52 = []byte(`<a class="action_button like_count hide_on_micro">`)
var topic_alt_53 = []byte(`</a>`)
var topic_alt_54 = []byte(`
var topic_alt_59 = []byte(`<a class="action_button like_count hide_on_micro">`)
var topic_alt_60 = []byte(`</a>`)
var topic_alt_61 = []byte(`
<a class="action_button created_at hide_on_mobile">`)
var topic_alt_55 = []byte(`</a>
var topic_alt_62 = []byte(`</a>
`)
var topic_alt_56 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_57 = []byte(`" title="IP Address" class="action_button ip_item hide_on_mobile">`)
var topic_alt_58 = []byte(`</a>`)
var topic_alt_59 = []byte(`
var topic_alt_63 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_64 = []byte(`" title="IP Address" class="action_button ip_item hide_on_mobile">`)
var topic_alt_65 = []byte(`</a>`)
var topic_alt_66 = []byte(`
</div>
</div>
</div><div style="clear:both;"></div>
</article>
`)
var topic_alt_60 = []byte(`
var topic_alt_67 = []byte(`
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item `)
var topic_alt_61 = []byte(`action_item`)
var topic_alt_62 = []byte(`">
var topic_alt_68 = []byte(`action_item`)
var topic_alt_69 = []byte(`">
<div class="userinfo" aria-label="The information on the poster">
<div class="avatar_item" style="background-image: url(`)
var topic_alt_63 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
var topic_alt_70 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="`)
var topic_alt_64 = []byte(`" class="the_name" rel="author">`)
var topic_alt_65 = []byte(`</a>
var topic_alt_71 = []byte(`" class="the_name" rel="author">`)
var topic_alt_72 = []byte(`</a>
`)
var topic_alt_66 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_67 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_68 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_69 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_70 = []byte(`
var topic_alt_73 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_74 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_75 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_76 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_77 = []byte(`
</div>
<div class="content_container" `)
var topic_alt_71 = []byte(`style="margin-left: 0px;"`)
var topic_alt_72 = []byte(`>
var topic_alt_78 = []byte(`style="margin-left: 0px;"`)
var topic_alt_79 = []byte(`>
`)
var topic_alt_73 = []byte(`
var topic_alt_80 = []byte(`
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
var topic_alt_74 = []byte(`</span>
var topic_alt_81 = []byte(`</span>
<span itemprop="text">`)
var topic_alt_75 = []byte(`</span>
var topic_alt_82 = []byte(`</span>
`)
var topic_alt_76 = []byte(`
var topic_alt_83 = []byte(`
<div class="editable_block user_content" itemprop="text">`)
var topic_alt_77 = []byte(`</div>
var topic_alt_84 = []byte(`</div>
<div class="button_container">
`)
var topic_alt_78 = []byte(`<a href="/reply/like/submit/`)
var topic_alt_79 = []byte(`" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>`)
var topic_alt_80 = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_81 = []byte(`" class="action_button edit_item" aria-label="Edit this post" data-action="edit"></a>`)
var topic_alt_82 = []byte(`<a href="/reply/delete/submit/`)
var topic_alt_83 = []byte(`" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>`)
var topic_alt_84 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_85 = []byte(`" title="IP Address" class="action_button ip_item_button hide_on_big" aria-label="This user's IP Address" data-action="ip"></a>`)
var topic_alt_86 = []byte(`
var topic_alt_85 = []byte(`<a href="/reply/like/submit/`)
var topic_alt_86 = []byte(`?session=`)
var topic_alt_87 = []byte(`" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>`)
var topic_alt_88 = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_89 = []byte(`?session=`)
var topic_alt_90 = []byte(`" class="action_button edit_item" aria-label="Edit this post" data-action="edit"></a>`)
var topic_alt_91 = []byte(`<a href="/reply/delete/submit/`)
var topic_alt_92 = []byte(`?session=`)
var topic_alt_93 = []byte(`" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>`)
var topic_alt_94 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_95 = []byte(`" title="IP Address" class="action_button ip_item_button hide_on_big" aria-label="This user's IP Address" data-action="ip"></a>`)
var topic_alt_96 = []byte(`
<a href="/report/submit/`)
var topic_alt_87 = []byte(`?session=`)
var topic_alt_88 = []byte(`&type=reply" class="action_button report_item" aria-label="Report this post" data-action="report"></a>
var topic_alt_97 = []byte(`?session=`)
var topic_alt_98 = []byte(`&type=reply" class="action_button report_item" aria-label="Report this post" data-action="report"></a>
<a href="#" class="action_button button_menu"></a>
`)
var topic_alt_89 = []byte(`
<div class="action_button_right`)
var topic_alt_90 = []byte(` has_likes`)
var topic_alt_91 = []byte(`">
`)
var topic_alt_92 = []byte(`<a class="action_button like_count hide_on_micro">`)
var topic_alt_93 = []byte(`</a>`)
var topic_alt_94 = []byte(`
<a class="action_button created_at hide_on_mobile">`)
var topic_alt_95 = []byte(`</a>
`)
var topic_alt_96 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_97 = []byte(`" title="IP Address" class="action_button ip_item hide_on_mobile">`)
var topic_alt_98 = []byte(`</a>`)
var topic_alt_99 = []byte(`
<div class="action_button_right`)
var topic_alt_100 = []byte(` has_likes`)
var topic_alt_101 = []byte(`">
`)
var topic_alt_102 = []byte(`<a class="action_button like_count hide_on_micro">`)
var topic_alt_103 = []byte(`</a>`)
var topic_alt_104 = []byte(`
<a class="action_button created_at hide_on_mobile">`)
var topic_alt_105 = []byte(`</a>
`)
var topic_alt_106 = []byte(`<a href="/users/ips/?ip=`)
var topic_alt_107 = []byte(`" title="IP Address" class="action_button ip_item hide_on_mobile">`)
var topic_alt_108 = []byte(`</a>`)
var topic_alt_109 = []byte(`
</div>
</div>
`)
var topic_alt_100 = []byte(`
var topic_alt_110 = []byte(`
</div>
<div style="clear:both;"></div>
</article>
`)
var topic_alt_101 = []byte(`</div>
var topic_alt_111 = []byte(`</div>
`)
var topic_alt_102 = []byte(`
var topic_alt_112 = []byte(`
<div class="rowblock topic_reply_container">
<div class="userinfo" aria-label="The information on the poster">
<div class="avatar_item" style="background-image: url(`)
var topic_alt_103 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
var topic_alt_113 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="`)
var topic_alt_104 = []byte(`" class="the_name" rel="author">`)
var topic_alt_105 = []byte(`</a>
var topic_alt_114 = []byte(`" class="the_name" rel="author">`)
var topic_alt_115 = []byte(`</a>
`)
var topic_alt_106 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_107 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_108 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_109 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_110 = []byte(`
var topic_alt_116 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_117 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_118 = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_119 = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_120 = []byte(`
</div>
<div class="rowblock topic_reply_form quick_create_form">
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/?session=`)
var topic_alt_121 = []byte(`" method="post"></form>
<input form="reply_form" name="tid" value='`)
var topic_alt_111 = []byte(`' type="hidden" />
var topic_alt_122 = []byte(`' type="hidden" />
<div class="formrow real_first_child">
<div class="formitem">
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="What do you think?" required></textarea>
@ -502,17 +524,17 @@ var topic_alt_111 = []byte(`' type="hidden" />
<div class="formitem">
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
`)
var topic_alt_112 = []byte(`
var topic_alt_123 = []byte(`
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">Add File</label>
<div id="upload_file_dock"></div>`)
var topic_alt_113 = []byte(`
var topic_alt_124 = []byte(`
</div>
</div>
</div>
</div>
`)
var topic_alt_114 = []byte(`
var topic_alt_125 = []byte(`
</main>
@ -821,18 +843,20 @@ var topics_8 = []byte(`
`)
var topics_9 = []byte(`
<div id="mod_topic_mover" class="modal_pane auto_hide">
<form action="/topic/move/submit/" method="post">
<form action="/topic/move/submit/?session=`)
var topics_10 = []byte(`" method="post">
<input id="mover_fid" name="fid" value="0" type="hidden" />
<div class="pane_header">
<h3>Move Topics (3)</h3>
<h3>Move these topics to?</h3>
</div>
<div class="pane_body">
<div class="pane_table">
`)
var topics_10 = []byte(`<div id="mover_fid_`)
var topics_11 = []byte(`" data-fid="`)
var topics_12 = []byte(`" class="pane_row">`)
var topics_13 = []byte(`</div>`)
var topics_14 = []byte(`
var topics_11 = []byte(`<div id="mover_fid_`)
var topics_12 = []byte(`" data-fid="`)
var topics_13 = []byte(`" class="pane_row">`)
var topics_14 = []byte(`</div>`)
var topics_15 = []byte(`
</div>
</div>
<div class="pane_buttons">
@ -841,20 +865,21 @@ var topics_14 = []byte(`
</form>
</div>
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/?session=`)
var topics_16 = []byte(`" method="post"></form>
<img class="little_row_avatar" src="`)
var topics_15 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
var topics_17 = []byte(`" height="64" alt="Your Avatar" title="Your Avatar" />
<div class="main_form">
<div class="topic_meta">
<div class="formrow topic_board_row real_first_child">
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">
`)
var topics_16 = []byte(`<option `)
var topics_17 = []byte(`selected`)
var topics_18 = []byte(` value="`)
var topics_19 = []byte(`">`)
var topics_20 = []byte(`</option>`)
var topics_21 = []byte(`
var topics_18 = []byte(`<option `)
var topics_19 = []byte(`selected`)
var topics_20 = []byte(` value="`)
var topics_21 = []byte(`">`)
var topics_22 = []byte(`</option>`)
var topics_23 = []byte(`
</select></div>
</div>
<div class="formrow topic_name_row">
@ -872,77 +897,77 @@ var topics_21 = []byte(`
<div class="formitem">
<button form="topic_create_form_form" class="formbutton">Create Topic</button>
`)
var topics_22 = []byte(`
var topics_24 = []byte(`
<input name="upload_files" form="topic_create_form_form" id="upload_files" multiple type="file" style="display: none;" />
<label for="upload_files" class="formbutton add_file_button">Add File</label>
<div id="upload_file_dock"></div>`)
var topics_23 = []byte(`
var topics_25 = []byte(`
<button class="formbutton close_form">Cancel</button>
</div>
</div>
</div>
</div>
`)
var topics_24 = []byte(`
var topics_26 = []byte(`
<div id="topic_list" class="rowblock topic_list" aria-label="A list containing topics from every forum">
`)
var topics_25 = []byte(`<div class="topic_row" data-tid="`)
var topics_26 = []byte(`">
var topics_27 = []byte(`<div class="topic_row" data-tid="`)
var topics_28 = []byte(`">
<div class="rowitem topic_left passive datarow `)
var topics_27 = []byte(`topic_sticky`)
var topics_28 = []byte(`topic_closed`)
var topics_29 = []byte(`">
var topics_29 = []byte(`topic_sticky`)
var topics_30 = []byte(`topic_closed`)
var topics_31 = []byte(`">
<span class="selector"></span>
<a href="`)
var topics_30 = []byte(`"><img src="`)
var topics_31 = []byte(`" height="64" alt="`)
var topics_32 = []byte(`'s Avatar" title="`)
var topics_33 = []byte(`'s Avatar" /></a>
var topics_32 = []byte(`"><img src="`)
var topics_33 = []byte(`" height="64" alt="`)
var topics_34 = []byte(`'s Avatar" title="`)
var topics_35 = []byte(`'s Avatar" /></a>
<span class="topic_inner_left">
<a class="rowtopic" href="`)
var topics_34 = []byte(`" itemprop="itemListElement"><span>`)
var topics_35 = []byte(`</span></a> `)
var topics_36 = []byte(`<a class="rowsmall parent_forum" href="`)
var topics_37 = []byte(`">`)
var topics_38 = []byte(`</a>`)
var topics_39 = []byte(`
var topics_36 = []byte(`" itemprop="itemListElement"><span>`)
var topics_37 = []byte(`</span></a> `)
var topics_38 = []byte(`<a class="rowsmall parent_forum" href="`)
var topics_39 = []byte(`">`)
var topics_40 = []byte(`</a>`)
var topics_41 = []byte(`
<br /><a class="rowsmall starter" href="`)
var topics_40 = []byte(`">`)
var topics_41 = []byte(`</a>
var topics_42 = []byte(`">`)
var topics_43 = []byte(`</a>
`)
var topics_42 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | &#x1F512;&#xFE0E</span>`)
var topics_43 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | &#x1F4CD;&#xFE0E</span>`)
var topics_44 = []byte(`
var topics_44 = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | &#x1F512;&#xFE0E</span>`)
var topics_45 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | &#x1F4CD;&#xFE0E</span>`)
var topics_46 = []byte(`
</span>
<span class="topic_inner_right rowsmall" style="float: right;">
<span class="replyCount">`)
var topics_45 = []byte(`</span><br />
var topics_47 = []byte(`</span><br />
<span class="likeCount">`)
var topics_46 = []byte(`</span>
var topics_48 = []byte(`</span>
</span>
</div>
<div class="rowitem topic_right passive datarow `)
var topics_47 = []byte(`topic_sticky`)
var topics_48 = []byte(`topic_closed`)
var topics_49 = []byte(`">
var topics_49 = []byte(`topic_sticky`)
var topics_50 = []byte(`topic_closed`)
var topics_51 = []byte(`">
<a href="`)
var topics_50 = []byte(`"><img src="`)
var topics_51 = []byte(`" height="64" alt="`)
var topics_52 = []byte(`'s Avatar" title="`)
var topics_53 = []byte(`'s Avatar" /></a>
var topics_52 = []byte(`"><img src="`)
var topics_53 = []byte(`" height="64" alt="`)
var topics_54 = []byte(`'s Avatar" title="`)
var topics_55 = []byte(`'s Avatar" /></a>
<span>
<a href="`)
var topics_54 = []byte(`" class="lastName" style="font-size: 14px;">`)
var topics_55 = []byte(`</a><br>
var topics_56 = []byte(`" class="lastName" style="font-size: 14px;">`)
var topics_57 = []byte(`</a><br>
<span class="rowsmall lastReplyAt">`)
var topics_56 = []byte(`</span>
var topics_58 = []byte(`</span>
</span>
</div>
</div>`)
var topics_57 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
var topics_58 = []byte(` <a href="/topics/create/">Start one?</a>`)
var topics_59 = []byte(`</div>`)
var topics_60 = []byte(`
var topics_59 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
var topics_60 = []byte(` <a href="/topics/create/">Start one?</a>`)
var topics_61 = []byte(`</div>`)
var topics_62 = []byte(`
</div>
</main>

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "net/http"
import "./common"
import "strconv"
import "net/http"
// nolint
func init() {
@ -83,225 +83,247 @@ w.Write(header_21)
w.Write(topic_0)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_1)
if tmpl_topic_vars.Page > 1 {
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_2)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
if tmpl_topic_vars.Page > 1 {
w.Write(topic_3)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Page - 1)))
w.Write(topic_4)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_5)
w.Write(topic_4)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Page - 1)))
w.Write(topic_5)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_6)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Page - 1)))
w.Write(topic_7)
}
if tmpl_topic_vars.LastPage != tmpl_topic_vars.Page {
w.Write(topic_7)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_8)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Page + 1)))
w.Write(topic_9)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_10)
w.Write(topic_9)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Page + 1)))
w.Write(topic_10)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_11)
}
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Page + 1)))
w.Write(topic_12)
if tmpl_topic_vars.Topic.Sticky {
}
w.Write(topic_13)
if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_14)
} else {
if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_14)
}
}
w.Write(topic_15)
w.Write([]byte(tmpl_topic_vars.Topic.Title))
}
}
w.Write(topic_16)
if tmpl_topic_vars.Topic.IsClosed {
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_17)
if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_18)
}
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_18)
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_19)
}
w.Write([]byte(tmpl_topic_vars.Topic.Title))
w.Write(topic_20)
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
}
w.Write(topic_21)
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write([]byte(tmpl_topic_vars.Topic.ClassName))
w.Write(topic_22)
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
w.Write(topic_23)
if tmpl_topic_vars.Topic.ContentLines <= 5 {
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_24)
}
if tmpl_topic_vars.Topic.ContentLines <= 5 {
w.Write(topic_25)
w.Write([]byte(tmpl_topic_vars.Topic.ContentHTML))
w.Write(topic_26)
w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_27)
w.Write([]byte(tmpl_topic_vars.Topic.UserLink))
w.Write(topic_28)
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
w.Write(topic_29)
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_30)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_31)
if tmpl_topic_vars.Topic.Liked {
w.Write(topic_32)
} else {
w.Write(topic_33)
}
w.Write(topic_34)
w.Write(topic_26)
w.Write([]byte(tmpl_topic_vars.Topic.ContentHTML))
w.Write(topic_27)
w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_28)
w.Write([]byte(tmpl_topic_vars.Topic.UserLink))
w.Write(topic_29)
w.Write([]byte(tmpl_topic_vars.Topic.CreatedByName))
w.Write(topic_30)
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_31)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_32)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_33)
if tmpl_topic_vars.Topic.Liked {
w.Write(topic_34)
} else {
w.Write(topic_35)
}
w.Write(topic_36)
}
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
if tmpl_topic_vars.Topic.Liked {
w.Write(topic_37)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
}
w.Write(topic_38)
}
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_39)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_40)
}
if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_vars.Topic.IsClosed {
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
w.Write(topic_41)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_42)
} else {
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_43)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
}
if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_vars.Topic.IsClosed {
w.Write(topic_44)
}
}
if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_45)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_45)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_46)
} else {
w.Write(topic_47)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_48)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_49)
}
}
if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_vars.Topic.Sticky {
w.Write(topic_50)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_51)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_52)
} else {
w.Write(topic_53)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_54)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_55)
}
}
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_49)
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_50)
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_51)
}
w.Write(topic_52)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_53)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_54)
if tmpl_topic_vars.Topic.LikeCount > 0 {
w.Write(topic_55)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
w.Write(topic_56)
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_57)
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
w.Write(topic_58)
}
w.Write(topic_59)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_60)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_61)
if tmpl_topic_vars.Topic.LikeCount > 0 {
w.Write(topic_62)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
w.Write(topic_63)
}
if tmpl_topic_vars.Topic.Tag != "" {
w.Write(topic_57)
w.Write(topic_64)
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
w.Write(topic_58)
w.Write(topic_65)
} else {
w.Write(topic_59)
w.Write(topic_66)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
w.Write(topic_60)
w.Write(topic_67)
}
w.Write(topic_61)
w.Write(topic_68)
if len(tmpl_topic_vars.ItemList) != 0 {
for _, item := range tmpl_topic_vars.ItemList {
if item.ActionType != "" {
w.Write(topic_62)
w.Write([]byte(item.ActionIcon))
w.Write(topic_63)
w.Write([]byte(item.ActionType))
w.Write(topic_64)
} else {
w.Write(topic_65)
w.Write([]byte(item.ClassName))
w.Write(topic_66)
w.Write([]byte(item.Avatar))
w.Write(topic_67)
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_68)
if item.ContentLines <= 5 {
w.Write(topic_69)
}
w.Write([]byte(item.ActionIcon))
w.Write(topic_70)
w.Write([]byte(item.ActionType))
w.Write(topic_71)
w.Write([]byte(item.ContentHtml))
} else {
w.Write(topic_72)
w.Write([]byte(item.UserLink))
w.Write([]byte(item.ClassName))
w.Write(topic_73)
w.Write([]byte(item.CreatedByName))
w.Write([]byte(item.Avatar))
w.Write(topic_74)
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
w.Write(topic_75)
w.Write([]byte(strconv.Itoa(item.ID)))
if item.ContentLines <= 5 {
w.Write(topic_76)
if item.Liked {
w.Write(topic_77)
}
w.Write(topic_77)
w.Write(topic_78)
w.Write([]byte(item.ContentHtml))
w.Write(topic_79)
w.Write([]byte(item.UserLink))
w.Write(topic_80)
w.Write([]byte(item.CreatedByName))
w.Write(topic_81)
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_82)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_83)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_84)
if item.Liked {
w.Write(topic_85)
}
w.Write(topic_86)
}
if tmpl_topic_vars.CurrentUser.Perms.EditReply {
w.Write(topic_79)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_80)
}
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_81)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_82)
}
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_83)
w.Write([]byte(item.IPAddress))
w.Write(topic_84)
}
w.Write(topic_85)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_86)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_87)
if item.LikeCount > 0 {
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_88)
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_89)
}
if item.Tag != "" {
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_90)
w.Write([]byte(item.Tag))
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_91)
} else {
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_92)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_93)
}
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_93)
w.Write([]byte(item.IPAddress))
w.Write(topic_94)
}
}
}
w.Write(topic_95)
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_96)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_97)
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
if item.LikeCount > 0 {
w.Write(topic_98)
}
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_99)
}
if item.Tag != "" {
w.Write(topic_100)
w.Write([]byte(item.Tag))
w.Write(topic_101)
} else {
w.Write(topic_102)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_103)
}
w.Write(topic_104)
}
}
}
w.Write(topic_105)
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_106)
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
w.Write(topic_107)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_108)
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
w.Write(topic_109)
}
w.Write(topic_110)
}
w.Write(topic_111)
w.Write(footer_0)
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
w.Write(footer_1)

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "net/http"
import "./common"
import "strconv"
// nolint
func init() {
@ -105,231 +105,253 @@ w.Write(topic_alt_9)
w.Write(topic_alt_10)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_11)
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_12)
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_13)
} else {
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_13)
}
}
w.Write(topic_alt_14)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
}
}
w.Write(topic_alt_15)
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_16)
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_17)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_17)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_18)
}
w.Write([]byte(tmpl_topic_alt_vars.Topic.Title))
w.Write(topic_alt_19)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
w.Write(topic_alt_20)
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
w.Write(topic_alt_21)
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
w.Write(topic_alt_22)
if tmpl_topic_alt_vars.Topic.Tag != "" {
w.Write(topic_alt_23)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag))
w.Write(topic_alt_24)
} else {
w.Write(topic_alt_25)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level)))
w.Write(topic_alt_26)
}
w.Write(topic_alt_20)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Avatar))
w.Write(topic_alt_21)
w.Write([]byte(tmpl_topic_alt_vars.Topic.UserLink))
w.Write(topic_alt_22)
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedByName))
w.Write(topic_alt_23)
if tmpl_topic_alt_vars.Topic.Tag != "" {
w.Write(topic_alt_24)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Tag))
w.Write(topic_alt_25)
} else {
w.Write(topic_alt_26)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level)))
w.Write(topic_alt_27)
w.Write([]byte(tmpl_topic_alt_vars.Topic.ContentHTML))
}
w.Write(topic_alt_28)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write([]byte(tmpl_topic_alt_vars.Topic.ContentHTML))
w.Write(topic_alt_29)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_30)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_30)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_31)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_32)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_32)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_33)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteTopic {
if tmpl_topic_alt_vars.CurrentUser.Perms.EditTopic {
w.Write(topic_alt_34)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_35)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_alt_vars.Topic.IsClosed {
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteTopic {
w.Write(topic_alt_36)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_37)
} else {
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_38)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
}
if tmpl_topic_alt_vars.CurrentUser.Perms.CloseTopic {
if tmpl_topic_alt_vars.Topic.IsClosed {
w.Write(topic_alt_39)
}
}
if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_40)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_40)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_41)
} else {
w.Write(topic_alt_42)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_43)
}
}
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_44)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_45)
}
w.Write(topic_alt_46)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_47)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_48)
w.Write(topic_alt_44)
}
}
if tmpl_topic_alt_vars.CurrentUser.Perms.PinTopic {
if tmpl_topic_alt_vars.Topic.Sticky {
w.Write(topic_alt_45)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_46)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_47)
} else {
w.Write(topic_alt_48)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_49)
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_50)
}
w.Write(topic_alt_51)
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_52)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_53)
}
w.Write(topic_alt_54)
w.Write([]byte(tmpl_topic_alt_vars.Topic.RelativeCreatedAt))
w.Write(topic_alt_55)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_56)
w.Write(topic_alt_51)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_57)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_58)
w.Write(topic_alt_52)
}
w.Write(topic_alt_53)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_54)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_55)
}
w.Write(topic_alt_56)
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_57)
}
w.Write(topic_alt_58)
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_59)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_60)
}
w.Write(topic_alt_61)
w.Write([]byte(tmpl_topic_alt_vars.Topic.RelativeCreatedAt))
w.Write(topic_alt_62)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_63)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_64)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IPAddress))
w.Write(topic_alt_65)
}
w.Write(topic_alt_66)
if len(tmpl_topic_alt_vars.ItemList) != 0 {
for _, item := range tmpl_topic_alt_vars.ItemList {
w.Write(topic_alt_60)
if item.ActionType != "" {
w.Write(topic_alt_61)
}
w.Write(topic_alt_62)
w.Write([]byte(item.Avatar))
w.Write(topic_alt_63)
w.Write([]byte(item.UserLink))
w.Write(topic_alt_64)
w.Write([]byte(item.CreatedByName))
w.Write(topic_alt_65)
if item.Tag != "" {
w.Write(topic_alt_66)
w.Write([]byte(item.Tag))
w.Write(topic_alt_67)
} else {
if item.ActionType != "" {
w.Write(topic_alt_68)
w.Write([]byte(strconv.Itoa(item.Level)))
}
w.Write(topic_alt_69)
}
w.Write([]byte(item.Avatar))
w.Write(topic_alt_70)
if item.ActionType != "" {
w.Write([]byte(item.UserLink))
w.Write(topic_alt_71)
}
w.Write([]byte(item.CreatedByName))
w.Write(topic_alt_72)
if item.ActionType != "" {
if item.Tag != "" {
w.Write(topic_alt_73)
w.Write([]byte(item.ActionIcon))
w.Write([]byte(item.Tag))
w.Write(topic_alt_74)
w.Write([]byte(item.ActionType))
w.Write(topic_alt_75)
} else {
w.Write(topic_alt_75)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_76)
w.Write([]byte(item.ContentHtml))
}
w.Write(topic_alt_77)
if item.ActionType != "" {
w.Write(topic_alt_78)
}
w.Write(topic_alt_79)
if item.ActionType != "" {
w.Write(topic_alt_80)
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_81)
w.Write([]byte(item.ActionType))
w.Write(topic_alt_82)
} else {
w.Write(topic_alt_83)
w.Write([]byte(item.ContentHtml))
w.Write(topic_alt_84)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_78)
w.Write(topic_alt_85)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_79)
w.Write(topic_alt_86)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_87)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_80)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_81)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_82)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_83)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_84)
w.Write([]byte(item.IPAddress))
w.Write(topic_alt_85)
}
w.Write(topic_alt_86)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_87)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_88)
}
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_89)
if item.LikeCount > 0 {
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_90)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_91)
if item.LikeCount > 0 {
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_92)
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_93)
}
w.Write(topic_alt_94)
w.Write([]byte(item.RelativeCreatedAt))
w.Write(topic_alt_95)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_94)
w.Write([]byte(item.IPAddress))
w.Write(topic_alt_95)
}
w.Write(topic_alt_96)
w.Write([]byte(item.IPAddress))
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_97)
w.Write([]byte(item.IPAddress))
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_98)
}
w.Write(topic_alt_99)
}
if item.LikeCount > 0 {
w.Write(topic_alt_100)
}
}
w.Write(topic_alt_101)
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
if item.LikeCount > 0 {
w.Write(topic_alt_102)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar))
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_alt_103)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
}
w.Write(topic_alt_104)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name))
w.Write([]byte(item.RelativeCreatedAt))
w.Write(topic_alt_105)
if tmpl_topic_alt_vars.CurrentUser.Tag != "" {
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_106)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag))
w.Write([]byte(item.IPAddress))
w.Write(topic_alt_107)
} else {
w.Write([]byte(item.IPAddress))
w.Write(topic_alt_108)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.CurrentUser.Level)))
}
w.Write(topic_alt_109)
}
w.Write(topic_alt_110)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
}
}
w.Write(topic_alt_111)
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles {
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_alt_112)
}
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar))
w.Write(topic_alt_113)
}
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link))
w.Write(topic_alt_114)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name))
w.Write(topic_alt_115)
if tmpl_topic_alt_vars.CurrentUser.Tag != "" {
w.Write(topic_alt_116)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag))
w.Write(topic_alt_117)
} else {
w.Write(topic_alt_118)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.CurrentUser.Level)))
w.Write(topic_alt_119)
}
w.Write(topic_alt_120)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_121)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_122)
if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles {
w.Write(topic_alt_123)
}
w.Write(topic_alt_124)
}
w.Write(topic_alt_125)
w.Write(footer_0)
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_alt_vars.Header)))
w.Write(footer_1)

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "net/http"
import "./common"
import "strconv"
// nolint
func init() {
@ -99,120 +99,124 @@ if tmpl_topics_vars.CurrentUser.ID != 0 {
w.Write(topics_8)
if len(tmpl_topics_vars.ForumList) != 0 {
w.Write(topics_9)
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
w.Write(topics_10)
if len(tmpl_topics_vars.ForumList) != 0 {
for _, item := range tmpl_topics_vars.ForumList {
w.Write(topics_10)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_11)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_12)
w.Write([]byte(item.Name))
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_13)
}
}
w.Write([]byte(item.Name))
w.Write(topics_14)
w.Write([]byte(tmpl_topics_vars.CurrentUser.Avatar))
}
}
w.Write(topics_15)
w.Write([]byte(tmpl_topics_vars.CurrentUser.Session))
w.Write(topics_16)
w.Write([]byte(tmpl_topics_vars.CurrentUser.Avatar))
w.Write(topics_17)
if len(tmpl_topics_vars.ForumList) != 0 {
for _, item := range tmpl_topics_vars.ForumList {
w.Write(topics_16)
if item.ID == tmpl_topics_vars.DefaultForum {
w.Write(topics_17)
}
w.Write(topics_18)
w.Write([]byte(strconv.Itoa(item.ID)))
if item.ID == tmpl_topics_vars.DefaultForum {
w.Write(topics_19)
w.Write([]byte(item.Name))
}
w.Write(topics_20)
}
}
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_21)
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
w.Write([]byte(item.Name))
w.Write(topics_22)
}
}
w.Write(topics_23)
}
}
if tmpl_topics_vars.CurrentUser.Perms.UploadFiles {
w.Write(topics_24)
}
w.Write(topics_25)
}
}
w.Write(topics_26)
if len(tmpl_topics_vars.TopicList) != 0 {
for _, item := range tmpl_topics_vars.TopicList {
w.Write(topics_25)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_26)
if item.Sticky {
w.Write(topics_27)
} else {
if item.IsClosed {
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topics_28)
}
}
if item.Sticky {
w.Write(topics_29)
w.Write([]byte(item.Creator.Link))
w.Write(topics_30)
w.Write([]byte(item.Creator.Avatar))
w.Write(topics_31)
w.Write([]byte(item.Creator.Name))
w.Write(topics_32)
w.Write([]byte(item.Creator.Name))
w.Write(topics_33)
w.Write([]byte(item.Link))
w.Write(topics_34)
w.Write([]byte(item.Title))
w.Write(topics_35)
if item.ForumName != "" {
w.Write(topics_36)
w.Write([]byte(item.ForumLink))
w.Write(topics_37)
w.Write([]byte(item.ForumName))
w.Write(topics_38)
}
w.Write(topics_39)
w.Write([]byte(item.Creator.Link))
w.Write(topics_40)
w.Write([]byte(item.Creator.Name))
w.Write(topics_41)
if item.IsClosed {
w.Write(topics_42)
}
if item.Sticky {
w.Write(topics_43)
}
w.Write(topics_44)
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(topics_45)
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topics_46)
if item.Sticky {
w.Write(topics_47)
} else {
if item.IsClosed {
w.Write(topics_30)
}
}
w.Write(topics_31)
w.Write([]byte(item.Creator.Link))
w.Write(topics_32)
w.Write([]byte(item.Creator.Avatar))
w.Write(topics_33)
w.Write([]byte(item.Creator.Name))
w.Write(topics_34)
w.Write([]byte(item.Creator.Name))
w.Write(topics_35)
w.Write([]byte(item.Link))
w.Write(topics_36)
w.Write([]byte(item.Title))
w.Write(topics_37)
if item.ForumName != "" {
w.Write(topics_38)
w.Write([]byte(item.ForumLink))
w.Write(topics_39)
w.Write([]byte(item.ForumName))
w.Write(topics_40)
}
w.Write(topics_41)
w.Write([]byte(item.Creator.Link))
w.Write(topics_42)
w.Write([]byte(item.Creator.Name))
w.Write(topics_43)
if item.IsClosed {
w.Write(topics_44)
}
if item.Sticky {
w.Write(topics_45)
}
w.Write(topics_46)
w.Write([]byte(strconv.Itoa(item.PostCount)))
w.Write(topics_47)
w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topics_48)
}
}
if item.Sticky {
w.Write(topics_49)
w.Write([]byte(item.LastUser.Link))
} else {
if item.IsClosed {
w.Write(topics_50)
w.Write([]byte(item.LastUser.Avatar))
}
}
w.Write(topics_51)
w.Write([]byte(item.LastUser.Name))
w.Write(topics_52)
w.Write([]byte(item.LastUser.Name))
w.Write(topics_53)
w.Write([]byte(item.LastUser.Link))
w.Write(topics_52)
w.Write([]byte(item.LastUser.Avatar))
w.Write(topics_53)
w.Write([]byte(item.LastUser.Name))
w.Write(topics_54)
w.Write([]byte(item.LastUser.Name))
w.Write(topics_55)
w.Write([]byte(item.RelativeLastReplyAt))
w.Write([]byte(item.LastUser.Link))
w.Write(topics_56)
}
} else {
w.Write([]byte(item.LastUser.Name))
w.Write(topics_57)
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
w.Write([]byte(item.RelativeLastReplyAt))
w.Write(topics_58)
}
} else {
w.Write(topics_59)
}
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
w.Write(topics_60)
}
w.Write(topics_61)
}
w.Write(topics_62)
w.Write(footer_0)
w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header)))
w.Write(footer_1)

View File

@ -4,7 +4,7 @@
<div class="rowitem"><h1>Create Topic</h1></div>
</div>
<div class="rowblock">
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/?session={{.CurrentUser.Session}}" method="post"></form>
<div class="formrow real_first_child">
<div class="formitem formlabel"><a>Board</a></div>
<div class="formitem"><select form="topic_create_form_form" id="topic_board_input" name="topic-board">

View File

@ -0,0 +1,62 @@
{{template "header.html" . }}
<div class="colstack panel_stack">
{{template "panel-menu.html" . }}
<main id="panel_dashboard_right" class="colstack_right">
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/posts/" method="get">
<div class="colstack_item colstack_head">
<div class="rowitem">
<a>Posts</a>
<select class="timeRangeSelector to_right" name="timeRange">
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
<option val="two-days"{{if eq .TimeRange "two-days"}} selected{{end}}>2 days</option>
<option val="one-day"{{if eq .TimeRange "one-day"}} selected{{end}}>1 day</option>
<option val="twelve-hours"{{if eq .TimeRange "twelve-hours"}} selected{{end}}>12 hours</option>
<option val="six-hours"{{if eq .TimeRange "six-hours"}} selected{{end}}>6 hours</option>
</select>
</div>
</div>
</form>
<div id="panel_analytics_posts" class="colstack_graph_holder">
<div class="ct_chart" aria-label="Post Chart"></div>
</div>
<div class="colstack_item colstack_head">
<div class="rowitem"><a>Details</a></div>
</div>
<div id="panel_analytics_posts_table" class="colstack_item rowlist" aria-label="Post Table, this has the same information as the post chart">
{{range .ViewItems}}
<div class="rowitem panel_compactrow editable_parent">
<a class="panel_upshift unix_to_24_hour_time">{{.Time}}</a>
<span class="panel_compacttext to_right">{{.Count}} views</span>
</div>
{{end}}
</div>
</main>
</div>
<script>
let labels = [];
let rawLabels = [{{range .PrimaryGraph.Labels}}
{{.}},{{end}}
];
for(const i in rawLabels) {
let date = new Date(rawLabels[i]*1000);
console.log("date: ", date);
let minutes = "0" + date.getMinutes();
let label = date.getHours() + ":" + minutes.substr(-2);
console.log("label:", label);
labels.push(label);
}
labels = labels.reverse()
let seriesData = [{{range .PrimaryGraph.Series}}
{{.}},{{end}}
];
seriesData = seriesData.reverse();
Chartist.Line('.ct_chart', {
labels: labels,
series: [seriesData],
}, {
height: '250px',
});
</script>
{{template "footer.html" . }}

View File

@ -19,6 +19,17 @@
<div id="panel_analytics_views" class="colstack_graph_holder">
<div class="ct-chart"></div>
</div>
<div class="colstack_item colstack_head">
<div class="rowitem"><a>Details</a></div>
</div>
<div id="panel_analytics_views_table" class="colstack_item rowlist" aria-label="View Table, this has the same information as the view chart">
{{range .ViewItems}}
<div class="rowitem panel_compactrow editable_parent">
<a class="panel_upshift unix_to_24_hour_time">{{.Time}}</a>
<span class="panel_compacttext to_right">{{.Count}} views</span>
</div>
{{end}}
</div>
</main>
</div>
<script>

View File

@ -1,6 +1,6 @@
{{template "header.html" . }}
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}' method="post"></form>
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' method="post"></form>
{{if gt .Page 1}}<link rel="prev" href="/topic/{{.Topic.ID}}?page={{subtract .Page 1}}" />
<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="Go to the previous page" rel="prev" href="/topic/{{.Topic.ID}}?page={{subtract .Page 1}}">&lt;</a></div>{{end}}
@ -30,16 +30,16 @@
<span class="controls" aria-label="Controls and Author Information">
<a href="{{.Topic.UserLink}}" class="username real_username" rel="author">{{.Topic.CreatedByName}}</a>&nbsp;&nbsp;
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="mod_button" title="Love it" {{if .Topic.Liked}}aria-label="Unlike this topic"{{else}}aria-label="Like this topic"{{end}} style="color:#202020;">
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="mod_button" title="Love it" {{if .Topic.Liked}}aria-label="Unlike this topic"{{else}}aria-label="Like this topic"{{end}} style="color:#202020;">
<button class="username like_label"{{if .Topic.Liked}} style="background-color:#D6FFD6;"{{end}}></button></a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href='/topic/delete/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>{{end}}
{{if .CurrentUser.Perms.CloseTopic}}{{if .Topic.IsClosed}}<a class="mod_button" href='/topic/unlock/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>{{else}}<a href='/topic/lock/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>{{end}}{{end}}
{{if .CurrentUser.Perms.CloseTopic}}{{if .Topic.IsClosed}}<a class="mod_button" href='/topic/unlock/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>{{else}}<a href='/topic/lock/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>{{end}}{{end}}
{{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>{{end}}{{end}}
{{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>{{end}}{{end}}
{{if .CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.Topic.IPAddress}}' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is {{.Topic.IPAddress}}"><button class="username ip_label"></button></a>{{end}}
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
@ -64,11 +64,11 @@
<span class="controls">
<a href="{{.UserLink}}" class="username real_username" rel="author">{{.CreatedByName}}</a>&nbsp;&nbsp;
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"{{if .Liked}} style="background-color:#D6FFD6;"{{end}}></button></a>{{end}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"{{if .Liked}} style="background-color:#D6FFD6;"{{end}}></button></a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>{{end}}
{{if $.CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.IPAddress}}' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
@ -82,7 +82,7 @@
{{if .CurrentUser.Perms.CreateReply}}
<div class="rowblock topic_reply_form quick_create_form">
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/?session={{.CurrentUser.Session}}" method="post"></form>
<input form="reply_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
<div class="formrow real_first_child">
<div class="formitem">

View File

@ -9,7 +9,7 @@
<main>
<div class="rowblock rowhead topic_block" aria-label="The opening post of this topic">
<form action='/topic/edit/submit/{{.Topic.ID}}' 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}}">
<h1 class='topic_name hide_on_edit'>{{.Topic.Title}}</h1>
{{/** TODO: Inline this CSS **/}}
@ -34,13 +34,13 @@
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
<div class="button_container">
{{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>{{end}}
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit" aria-label="Edit this post" data-action="edit"></a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>{{end}}
{{if .CurrentUser.Perms.CloseTopic}}
{{if .Topic.IsClosed}}<a href='/topic/unlock/submit/{{.Topic.ID}}' class="action_button unlock_item" data-action="unlock"></a>{{else}}<a href='/topic/lock/submit/{{.Topic.ID}}' class="action_button lock_item" data-action="lock"></a>{{end}}{{end}}
{{if .Topic.IsClosed}}<a href='/topic/unlock/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="action_button unlock_item" data-action="unlock"></a>{{else}}<a href='/topic/lock/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="action_button lock_item" data-action="lock"></a>{{end}}{{end}}
{{if .CurrentUser.Perms.PinTopic}}
{{if .Topic.Sticky}}<a href='/topic/unstick/submit/{{.Topic.ID}}' class="action_button unpin_item" data-action="unpin"></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="action_button pin_item" data-action="pin"></a>{{end}}{{end}}
{{if .Topic.Sticky}}<a href='/topic/unstick/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="action_button unpin_item" data-action="unpin"></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}' class="action_button pin_item" data-action="pin"></a>{{end}}{{end}}
{{if .CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.Topic.IPAddress}}" title="IP Address" class="action_button ip_item_button hide_on_big" aria-label="This user's IP" data-action="ip"></a>{{end}}
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item" aria-label="Report this post" data-action="report"></a>
<a href="#" class="action_button button_menu"></a>
@ -70,9 +70,9 @@
<div class="editable_block user_content" itemprop="text">{{.ContentHtml}}</div>
<div class="button_container">
{{if $.CurrentUser.Loggedin}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item" aria-label="Edit this post" data-action="edit"></a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>{{end}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button like_item add_like" aria-label="Like this post" data-action="like"></a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button edit_item" aria-label="Edit this post" data-action="edit"></a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}?session={{$.CurrentUser.Session}}" class="action_button delete_item" aria-label="Delete this post" data-action="delete"></a>{{end}}
{{if $.CurrentUser.Perms.ViewIPs}}<a href="/users/ips/?ip={{.IPAddress}}" title="IP Address" class="action_button ip_item_button hide_on_big" aria-label="This user's IP Address" data-action="ip"></a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item" aria-label="Report this post" data-action="report"></a>
<a href="#" class="action_button button_menu"></a>
@ -97,7 +97,7 @@
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level {{.CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}
</div>
<div class="rowblock topic_reply_form quick_create_form">
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/?session={{.CurrentUser.Session}}" method="post"></form>
<input form="reply_form" name="tid" value='{{.Topic.ID}}' type="hidden" />
<div class="formrow real_first_child">
<div class="formitem">

View File

@ -38,9 +38,10 @@
{{/** TODO: Have a seperate forum list for moving topics? Maybe an AJAX forum search compatible with plugin_guilds? **/}}
{{/** TODO: Add ARIA attributes for this **/}}
<div id="mod_topic_mover" class="modal_pane auto_hide">
<form action="/topic/move/submit/" method="post">
<form action="/topic/move/submit/?session={{.CurrentUser.Session}}" method="post">
<input id="mover_fid" name="fid" value="0" type="hidden" />
<div class="pane_header">
<h3>Move Topics (3)</h3>
<h3>Move these topics to?</h3>
</div>
<div class="pane_body">
<div class="pane_table">
@ -53,7 +54,7 @@
</form>
</div>
<div class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
<form name="topic_create_form_form" id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/?session={{.CurrentUser.Session}}" method="post"></form>
<img class="little_row_avatar" src="{{.CurrentUser.Avatar}}" height="64" alt="Your Avatar" title="Your Avatar" />
<div class="main_form">
<div class="topic_meta">

View File

@ -317,15 +317,14 @@ h1, h3 {
border: 1px solid var(--header-border-color);
border-bottom: 2px solid var(--header-border-color);
/*padding: 8px;*/
padding-left: 16px;
padding-right: 16px;
padding-left: 24px;
padding-right: 24px;
z-index: 9999;
animation: fadein 0.8s;
}
.pane_header {
color: hsl(0,0%,55%);
padding-top: 12px;
padding-top: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--element-border-color);
margin-bottom: 2px;
@ -339,9 +338,13 @@ h1, h3 {
color: var(--light-text-color);
border-bottom: 1px solid var(--element-border-color);
font-size: 13px;
padding-top: 5px;
padding-bottom: 8px;
padding-top: 12px;
padding-bottom: 12px;
margin-bottom: 3px;
cursor: pointer;
}
.pane_selected {
font-weight: bold;
}
.pane_buttons {