diff --git a/common/common.go b/common/common.go index f920dca2..106730d3 100644 --- a/common/common.go +++ b/common/common.go @@ -107,3 +107,15 @@ func debugLogf(str string, args ...interface{}) { log.Printf(str, args...) } } + +// TODO: Make a neater API for this +var routeMapEnum map[string]int +var reverseRouteMapEnum map[int]string + +func SetRouteMapEnum(rme map[string]int) { + routeMapEnum = rme +} + +func SetReverseRouteMapEnum(rrme map[int]string) { + reverseRouteMapEnum = rrme +} diff --git a/common/counters.go b/common/counters.go index 2e8f1196..1bece345 100644 --- a/common/counters.go +++ b/common/counters.go @@ -2,32 +2,35 @@ package common import ( "database/sql" + "log" + "sync" "sync/atomic" "../query_gen/lib" ) -var GlobalViewCounter *BufferedViewCounter +var GlobalViewCounter *ChunkedViewCounter +var RouteViewCounter *RouteViewCounterImpl -type BufferedViewCounter struct { +type ChunkedViewCounter struct { buckets [2]int64 currentBucket int64 insert *sql.Stmt } -func NewGlobalViewCounter() (*BufferedViewCounter, error) { +func NewChunkedViewCounter() (*ChunkedViewCounter, error) { acc := qgen.Builder.Accumulator() - counter := &BufferedViewCounter{ + counter := &ChunkedViewCounter{ currentBucket: 0, - insert: acc.SimpleInsert("viewchunks", "count, createdAt", "?,UTC_TIMESTAMP()"), + insert: acc.Insert("viewchunks").Columns("count, createdAt").Fields("?,UTC_TIMESTAMP()").Prepare(), } - //AddScheduledFifteenMinuteTask(counter.Tick) - AddScheduledSecondTask(counter.Tick) + AddScheduledFifteenMinuteTask(counter.Tick) // This is run once every fifteen minutes to match the frequency of the RouteViewCounter + //AddScheduledSecondTask(counter.Tick) return counter, acc.FirstError() } -func (counter *BufferedViewCounter) Tick() (err error) { +func (counter *ChunkedViewCounter) Tick() (err error) { var oldBucket = counter.currentBucket var nextBucket int64 if counter.currentBucket == 1 { @@ -38,22 +41,17 @@ func (counter *BufferedViewCounter) Tick() (err error) { atomic.AddInt64(&counter.buckets[oldBucket], counter.buckets[nextBucket]) atomic.StoreInt64(&counter.buckets[nextBucket], 0) atomic.StoreInt64(&counter.currentBucket, nextBucket) - /*debugLog("counter.buckets[nextBucket]: ", counter.buckets[nextBucket]) - debugLog("counter.buckets[oldBucket]: ", counter.buckets[oldBucket]) - debugLog("counter.currentBucket:", counter.currentBucket) - debugLog("oldBucket:", oldBucket) - debugLog("nextBucket:", nextBucket)*/ var previousViewChunk = counter.buckets[oldBucket] atomic.AddInt64(&counter.buckets[oldBucket], -previousViewChunk) return counter.insertChunk(previousViewChunk) } -func (counter *BufferedViewCounter) Bump() { +func (counter *ChunkedViewCounter) Bump() { atomic.AddInt64(&counter.buckets[counter.currentBucket], 1) } -func (counter *BufferedViewCounter) insertChunk(count int64) error { +func (counter *ChunkedViewCounter) insertChunk(count int64) error { if count == 0 { return nil } @@ -61,3 +59,194 @@ func (counter *BufferedViewCounter) insertChunk(count int64) error { _, err := counter.insert.Exec(count) return err } + +type RWMutexCounterBucket struct { + counter int + sync.RWMutex +} + +// The name of the struct clashes with the name of the variable, so we're adding Impl to the end +type RouteViewCounterImpl struct { + routeBuckets []*RWMutexCounterBucket //[RouteID]count + insert *sql.Stmt +} + +func NewRouteViewCounter() (*RouteViewCounterImpl, error) { + acc := qgen.Builder.Accumulator() + var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum)) + for bucketID, _ := range routeBuckets { + routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0} + } + counter := &RouteViewCounterImpl{ + routeBuckets: routeBuckets, + insert: acc.Insert("viewchunks").Columns("count, createdAt, route").Fields("?,UTC_TIMESTAMP(),?").Prepare(), + } + AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second + //AddScheduledSecondTask(counter.Tick) + return counter, acc.FirstError() +} + +func (counter *RouteViewCounterImpl) Tick() error { + for routeID, routeBucket := range counter.routeBuckets { + var count int + routeBucket.RLock() + count = routeBucket.counter + routeBucket.counter = 0 + routeBucket.RUnlock() + + err := counter.insertChunk(count, routeID) // TODO: Bulk insert for speed? + if err != nil { + return err + } + } + return nil +} + +func (counter *RouteViewCounterImpl) insertChunk(count int, route int) error { + if count == 0 { + return nil + } + var routeName = reverseRouteMapEnum[route] + debugLogf("Inserting a viewchunk with a count of %d for route %s (%d)", count, routeName, route) + _, err := counter.insert.Exec(count, routeName) + return err +} + +func (counter *RouteViewCounterImpl) Bump(route int) { + // TODO: Test this check + log.Print("counter.routeBuckets[route]: ", counter.routeBuckets[route]) + if len(counter.routeBuckets) <= route { + return + } + counter.routeBuckets[route].Lock() + counter.routeBuckets[route].counter++ + counter.routeBuckets[route].Unlock() +} + +// TODO: The ForumViewCounter and TopicViewCounter + +// TODO: Unload forum counters without any views over the past 15 minutes, if the admin has configured the forumstore with a cap and it's been hit? +// Forums can be reloaded from the database at any time, so we want to keep the counters separate from them +type ForumViewCounter struct { + buckets [2]int64 + currentBucket int64 +} + +/*func (counter *ForumViewCounter) insertChunk(count int, forum int) error { + if count == 0 { + return nil + } + debugLogf("Inserting a viewchunk with a count of %d for forum %d", count, forum) + _, err := counter.insert.Exec(count, forum) + return err +}*/ + +// TODO: Use two odd-even maps for now, and move to something more concurrent later, maybe a sharded map? +type TopicViewCounter struct { + oddTopics map[int]*RWMutexCounterBucket // map[tid]struct{counter,sync.RWMutex} + evenTopics map[int]*RWMutexCounterBucket + oddLock sync.RWMutex + evenLock sync.RWMutex + + update *sql.Stmt +} + +func NewTopicViewCounter() (*TopicViewCounter, error) { + acc := qgen.Builder.Accumulator() + counter := &TopicViewCounter{ + oddTopics: make(map[int]*RWMutexCounterBucket), + evenTopics: make(map[int]*RWMutexCounterBucket), + update: acc.Update("topics").Set("views = ?").Where("tid = ?").Prepare(), // TODO: Add the views column to the topics table + } + AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second + //AddScheduledSecondTask(counter.Tick) + return counter, acc.FirstError() +} + +func (counter *TopicViewCounter) Tick() error { + counter.oddLock.RLock() + for topicID, topic := range counter.oddTopics { + var count int + topic.RLock() + count = topic.counter + topic.RUnlock() + err := counter.insertChunk(count, topicID) + if err != nil { + return err + } + } + counter.oddLock.RUnlock() + + counter.evenLock.RLock() + for topicID, topic := range counter.evenTopics { + var count int + topic.RLock() + count = topic.counter + topic.RUnlock() + err := counter.insertChunk(count, topicID) + if err != nil { + return err + } + } + counter.evenLock.RUnlock() + + return nil +} + +func (counter *TopicViewCounter) insertChunk(count int, topicID int) error { + if count == 0 { + return nil + } + debugLogf("Inserting %d views into topic %d", count, topicID) + _, err := counter.update.Exec(count, topicID) + return err +} + +func (counter *TopicViewCounter) Bump(topicID int) { + // Is the ID even? + if topicID%2 == 0 { + counter.evenLock.Lock() + topic, ok := counter.evenTopics[topicID] + counter.evenLock.Unlock() + if ok { + topic.Lock() + topic.counter++ + topic.Unlock() + } else { + counter.evenLock.Lock() + counter.evenTopics[topicID] = &RWMutexCounterBucket{counter: 1} + counter.evenLock.Unlock() + } + return + } + + counter.oddLock.Lock() + topic, ok := counter.oddTopics[topicID] + counter.oddLock.Unlock() + if ok { + topic.Lock() + topic.counter++ + topic.Unlock() + } else { + counter.oddLock.Lock() + counter.oddTopics[topicID] = &RWMutexCounterBucket{counter: 1} + counter.oddLock.Unlock() + } +} + +type TreeCounterNode struct { + Value int64 + Zero *TreeCounterNode + One *TreeCounterNode + Parent *TreeCounterNode +} + +// MEGA EXPERIMENTAL. Start from the right-most bits in the integer and move leftwards +type TreeTopicViewCounter struct { + zero *TreeCounterNode + one *TreeCounterNode +} + +func (counter *TreeTopicViewCounter) Bump(topicID int64) { + +} diff --git a/common/email.go b/common/email.go index 38ba2207..a6bf7de0 100644 --- a/common/email.go +++ b/common/email.go @@ -26,6 +26,7 @@ func SendValidationEmail(username string, email string, token string) bool { } // TODO: Refactor this +// TODO: Add support for TLS func SendEmail(email string, subject string, msg string) bool { // This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server? if Vhooks["email_send_intercept"] != nil { @@ -68,6 +69,6 @@ func SendEmail(email string, subject string, msg string) bool { if err != nil { return false } - err = con.Quit() - return err == nil + + return con.Quit() == nil } diff --git a/common/errors.go b/common/errors.go index cb17260c..cc1ad568 100644 --- a/common/errors.go +++ b/common/errors.go @@ -136,7 +136,7 @@ func LocalErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, user U func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) RouteError { w.WriteHeader(500) - _, _ = w.Write([]byte(`{'errmsg': '` + errmsg + `'}`)) + _, _ = w.Write([]byte(`{"errmsg": "` + errmsg + `"}`)) return HandledRouteError() } diff --git a/common/routes_common.go b/common/routes_common.go index 9f814c8e..313266bf 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -284,6 +284,14 @@ func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) { return *usercpy, true } +// SuperAdminOnly makes sure that only super admin can access certain critical panel routes +func SuperAdminOnly(w http.ResponseWriter, r *http.Request, user User) RouteError { + if !user.IsSuperAdmin { + return NoPermissions(w, r, user) + } + return nil +} + // AdminOnly makes sure that only admins can access certain panel routes func AdminOnly(w http.ResponseWriter, r *http.Request, user User) RouteError { if !user.IsAdmin { diff --git a/common/site.go b/common/site.go index cb2e0813..f10d2d93 100644 --- a/common/site.go +++ b/common/site.go @@ -59,6 +59,7 @@ type config struct { SMTPUsername string SMTPPassword string SMTPPort string + //SMTPEnableTLS bool DefaultRoute func(http.ResponseWriter, *http.Request, User) RouteError DefaultGroup int diff --git a/common/topic.go b/common/topic.go index 10eaf02e..c28e3183 100644 --- a/common/topic.go +++ b/common/topic.go @@ -149,7 +149,7 @@ func init() { // Flush the topic out of the cache // ? - We do a CacheRemove() here instead of mutating the pointer to avoid creating a race condition func (topic *Topic) cacheRemove() { - tcache := Topics.(TopicCache) + tcache := Topics.GetCache() if tcache != nil { tcache.Remove(topic.ID) } diff --git a/common/utils.go b/common/utils.go index d375b0be..55e644e7 100644 --- a/common/utils.go +++ b/common/utils.go @@ -73,9 +73,12 @@ func RelativeTime(t time.Time) string { weeks := int(hours / 24 / 7) months := int(hours / 24 / 31) switch { - case months > 11: - //return t.Format("Mon Jan 2 2006") - return t.Format("Jan 2 2006") + case months > 3: + if t.Year() != time.Now().Year() { + //return t.Format("Mon Jan 2 2006") + return t.Format("Jan 2 2006") + } + return t.Format("Jan 2") case months > 1: return fmt.Sprintf("%d months ago", months) case months == 1: diff --git a/gen_router.go b/gen_router.go index 549c945c..8f9304c2 100644 --- a/gen_router.go +++ b/gen_router.go @@ -13,7 +13,8 @@ import ( ) var ErrNoRoute = errors.New("That route doesn't exist.") -var RouteMap = map[string]interface{}{ +// TODO: What about the /uploads/ route? x.x +var RouteMap = map[string]interface{}{ "routeAPI": routeAPI, "routeOverview": routeOverview, "routeForums": routeForums, @@ -72,9 +73,133 @@ var RouteMap = map[string]interface{}{ "routeIps": routeIps, } +// ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS +var routeMapEnum = map[string]int{ + "routeAPI": 0, + "routeOverview": 1, + "routeForums": 2, + "routeForum": 3, + "routeChangeTheme": 4, + "routeShowAttachment": 5, + "routeReportSubmit": 6, + "routeTopicCreate": 7, + "routeTopics": 8, + "routePanelForums": 9, + "routePanelForumsCreateSubmit": 10, + "routePanelForumsDelete": 11, + "routePanelForumsDeleteSubmit": 12, + "routePanelForumsEdit": 13, + "routePanelForumsEditSubmit": 14, + "routePanelForumsEditPermsSubmit": 15, + "routePanelSettings": 16, + "routePanelSettingEdit": 17, + "routePanelSettingEditSubmit": 18, + "routePanelWordFilters": 19, + "routePanelWordFiltersCreate": 20, + "routePanelWordFiltersEdit": 21, + "routePanelWordFiltersEditSubmit": 22, + "routePanelWordFiltersDeleteSubmit": 23, + "routePanelThemes": 24, + "routePanelThemesSetDefault": 25, + "routePanelPlugins": 26, + "routePanelPluginsActivate": 27, + "routePanelPluginsDeactivate": 28, + "routePanelPluginsInstall": 29, + "routePanelUsers": 30, + "routePanelUsersEdit": 31, + "routePanelUsersEditSubmit": 32, + "routePanelGroups": 33, + "routePanelGroupsEdit": 34, + "routePanelGroupsEditPerms": 35, + "routePanelGroupsEditSubmit": 36, + "routePanelGroupsEditPermsSubmit": 37, + "routePanelGroupsCreateSubmit": 38, + "routePanelBackups": 39, + "routePanelLogsMod": 40, + "routePanelDebug": 41, + "routePanel": 42, + "routeAccountEditCritical": 43, + "routeAccountEditCriticalSubmit": 44, + "routeAccountEditAvatar": 45, + "routeAccountEditAvatarSubmit": 46, + "routeAccountEditUsername": 47, + "routeAccountEditUsernameSubmit": 48, + "routeAccountEditEmail": 49, + "routeAccountEditEmailTokenSubmit": 50, + "routeProfile": 51, + "routeBanSubmit": 52, + "routeUnban": 53, + "routeActivate": 54, + "routeIps": 55, +} +var reverseRouteMapEnum = map[int]string{ + 0: "routeAPI", + 1: "routeOverview", + 2: "routeForums", + 3: "routeForum", + 4: "routeChangeTheme", + 5: "routeShowAttachment", + 6: "routeReportSubmit", + 7: "routeTopicCreate", + 8: "routeTopics", + 9: "routePanelForums", + 10: "routePanelForumsCreateSubmit", + 11: "routePanelForumsDelete", + 12: "routePanelForumsDeleteSubmit", + 13: "routePanelForumsEdit", + 14: "routePanelForumsEditSubmit", + 15: "routePanelForumsEditPermsSubmit", + 16: "routePanelSettings", + 17: "routePanelSettingEdit", + 18: "routePanelSettingEditSubmit", + 19: "routePanelWordFilters", + 20: "routePanelWordFiltersCreate", + 21: "routePanelWordFiltersEdit", + 22: "routePanelWordFiltersEditSubmit", + 23: "routePanelWordFiltersDeleteSubmit", + 24: "routePanelThemes", + 25: "routePanelThemesSetDefault", + 26: "routePanelPlugins", + 27: "routePanelPluginsActivate", + 28: "routePanelPluginsDeactivate", + 29: "routePanelPluginsInstall", + 30: "routePanelUsers", + 31: "routePanelUsersEdit", + 32: "routePanelUsersEditSubmit", + 33: "routePanelGroups", + 34: "routePanelGroupsEdit", + 35: "routePanelGroupsEditPerms", + 36: "routePanelGroupsEditSubmit", + 37: "routePanelGroupsEditPermsSubmit", + 38: "routePanelGroupsCreateSubmit", + 39: "routePanelBackups", + 40: "routePanelLogsMod", + 41: "routePanelDebug", + 42: "routePanel", + 43: "routeAccountEditCritical", + 44: "routeAccountEditCriticalSubmit", + 45: "routeAccountEditAvatar", + 46: "routeAccountEditAvatarSubmit", + 47: "routeAccountEditUsername", + 48: "routeAccountEditUsernameSubmit", + 49: "routeAccountEditEmail", + 50: "routeAccountEditEmailTokenSubmit", + 51: "routeProfile", + 52: "routeBanSubmit", + 53: "routeUnban", + 54: "routeActivate", + 55: "routeIps", +} + +// TODO: Stop spilling these into the package scope? +func init() { + common.SetRouteMapEnum(routeMapEnum) + common.SetReverseRouteMapEnum(reverseRouteMapEnum) +} + type GenRouter struct { UploadHandler func(http.ResponseWriter, *http.Request) - extra_routes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError + extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError sync.RWMutex } @@ -82,7 +207,7 @@ type GenRouter struct { func NewGenRouter(uploads http.Handler) *GenRouter { return &GenRouter{ UploadHandler: http.StripPrefix("/uploads/",uploads).ServeHTTP, - extra_routes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError), + extraRoutes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError), } } @@ -104,17 +229,17 @@ func (router *GenRouter) Handle(_ string, _ http.Handler) { func (router *GenRouter) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request, common.User) common.RouteError) { router.Lock() defer router.Unlock() - router.extra_routes[pattern] = handle + router.extraRoutes[pattern] = handle } func (router *GenRouter) RemoveFunc(pattern string) error { router.Lock() defer router.Unlock() - _, ok := router.extra_routes[pattern] + _, ok := router.extraRoutes[pattern] if !ok { return ErrNoRoute } - delete(router.extra_routes, pattern) + delete(router.extraRoutes, pattern) return nil } @@ -125,10 +250,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - var prefix, extra_data string + var prefix, extraData string prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1] if req.URL.Path[len(req.URL.Path) - 1] != '/' { - extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] + extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] } @@ -136,12 +261,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { log.Print("before routeStatic") log.Print("prefix: ", prefix) log.Print("req.URL.Path: ", req.URL.Path) - log.Print("extra_data: ", extra_data) + log.Print("extraData: ", extraData) log.Print("req.Referer(): ", req.Referer()) } if prefix == "/static" { - req.URL.Path += extra_data + req.URL.Path += extraData routeStatic(w, req) return } @@ -159,27 +284,32 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } if common.Dev.SuperDebug { log.Print("after PreRoute") + log.Print("routeMapEnum: ", routeMapEnum) } var err common.RouteError switch(prefix) { case "/api": + common.RouteViewCounter.Bump(0) err = routeAPI(w,req,user) if err != nil { router.handleError(err,w,req,user) } case "/overview": + common.RouteViewCounter.Bump(1) err = routeOverview(w,req,user) if err != nil { router.handleError(err,w,req,user) } case "/forums": + common.RouteViewCounter.Bump(2) err = routeForums(w,req,user) if err != nil { router.handleError(err,w,req,user) } case "/forum": - err = routeForum(w,req,user,extra_data) + common.RouteViewCounter.Bump(3) + err = routeForum(w,req,user,extraData) if err != nil { router.handleError(err,w,req,user) } @@ -190,6 +320,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(4) err = routeChangeTheme(w,req,user) if err != nil { router.handleError(err,w,req,user) @@ -201,7 +332,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routeShowAttachment(w,req,user,extra_data) + common.RouteViewCounter.Bump(5) + err = routeShowAttachment(w,req,user,extraData) if err != nil { router.handleError(err,w,req,user) } @@ -226,7 +358,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routeReportSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(6) + err = routeReportSubmit(w,req,user,extraData) } if err != nil { router.handleError(err,w,req,user) @@ -240,8 +373,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routeTopicCreate(w,req,user,extra_data) + common.RouteViewCounter.Bump(7) + err = routeTopicCreate(w,req,user,extraData) default: + common.RouteViewCounter.Bump(8) err = routeTopics(w,req,user) } if err != nil { @@ -256,6 +391,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { switch(req.URL.Path) { case "/panel/forums/": + common.RouteViewCounter.Bump(9) err = routePanelForums(w,req,user) case "/panel/forums/create/": err = common.NoSessionMismatch(w,req,user) @@ -264,6 +400,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(10) err = routePanelForumsCreateSubmit(w,req,user) case "/panel/forums/delete/": err = common.NoSessionMismatch(w,req,user) @@ -272,7 +409,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelForumsDelete(w,req,user,extra_data) + common.RouteViewCounter.Bump(11) + err = routePanelForumsDelete(w,req,user,extraData) case "/panel/forums/delete/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -280,9 +418,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelForumsDeleteSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(12) + err = routePanelForumsDeleteSubmit(w,req,user,extraData) case "/panel/forums/edit/": - err = routePanelForumsEdit(w,req,user,extra_data) + common.RouteViewCounter.Bump(13) + err = routePanelForumsEdit(w,req,user,extraData) case "/panel/forums/edit/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -290,7 +430,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelForumsEditSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(14) + err = routePanelForumsEditSubmit(w,req,user,extraData) case "/panel/forums/edit/perms/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -298,11 +439,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelForumsEditPermsSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(15) + err = routePanelForumsEditPermsSubmit(w,req,user,extraData) case "/panel/settings/": + common.RouteViewCounter.Bump(16) err = routePanelSettings(w,req,user) case "/panel/settings/edit/": - err = routePanelSettingEdit(w,req,user,extra_data) + common.RouteViewCounter.Bump(17) + err = routePanelSettingEdit(w,req,user,extraData) case "/panel/settings/edit/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -310,8 +454,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelSettingEditSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(18) + err = routePanelSettingEditSubmit(w,req,user,extraData) case "/panel/settings/word-filters/": + common.RouteViewCounter.Bump(19) err = routePanelWordFilters(w,req,user) case "/panel/settings/word-filters/create/": err = common.NoSessionMismatch(w,req,user) @@ -320,9 +466,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(20) err = routePanelWordFiltersCreate(w,req,user) case "/panel/settings/word-filters/edit/": - err = routePanelWordFiltersEdit(w,req,user,extra_data) + common.RouteViewCounter.Bump(21) + err = routePanelWordFiltersEdit(w,req,user,extraData) case "/panel/settings/word-filters/edit/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -330,7 +478,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelWordFiltersEditSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(22) + err = routePanelWordFiltersEditSubmit(w,req,user,extraData) case "/panel/settings/word-filters/delete/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -338,8 +487,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelWordFiltersDeleteSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(23) + err = routePanelWordFiltersDeleteSubmit(w,req,user,extraData) case "/panel/themes/": + common.RouteViewCounter.Bump(24) err = routePanelThemes(w,req,user) case "/panel/themes/default/": err = common.NoSessionMismatch(w,req,user) @@ -348,8 +499,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelThemesSetDefault(w,req,user,extra_data) + common.RouteViewCounter.Bump(25) + err = routePanelThemesSetDefault(w,req,user,extraData) case "/panel/plugins/": + common.RouteViewCounter.Bump(26) err = routePanelPlugins(w,req,user) case "/panel/plugins/activate/": err = common.NoSessionMismatch(w,req,user) @@ -358,7 +511,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelPluginsActivate(w,req,user,extra_data) + common.RouteViewCounter.Bump(27) + err = routePanelPluginsActivate(w,req,user,extraData) case "/panel/plugins/deactivate/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -366,7 +520,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelPluginsDeactivate(w,req,user,extra_data) + common.RouteViewCounter.Bump(28) + err = routePanelPluginsDeactivate(w,req,user,extraData) case "/panel/plugins/install/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -374,11 +529,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelPluginsInstall(w,req,user,extra_data) + common.RouteViewCounter.Bump(29) + err = routePanelPluginsInstall(w,req,user,extraData) case "/panel/users/": + common.RouteViewCounter.Bump(30) err = routePanelUsers(w,req,user) case "/panel/users/edit/": - err = routePanelUsersEdit(w,req,user,extra_data) + common.RouteViewCounter.Bump(31) + err = routePanelUsersEdit(w,req,user,extraData) case "/panel/users/edit/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -386,13 +544,17 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelUsersEditSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(32) + err = routePanelUsersEditSubmit(w,req,user,extraData) case "/panel/groups/": + common.RouteViewCounter.Bump(33) err = routePanelGroups(w,req,user) case "/panel/groups/edit/": - err = routePanelGroupsEdit(w,req,user,extra_data) + common.RouteViewCounter.Bump(34) + err = routePanelGroupsEdit(w,req,user,extraData) case "/panel/groups/edit/perms/": - err = routePanelGroupsEditPerms(w,req,user,extra_data) + common.RouteViewCounter.Bump(35) + err = routePanelGroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -400,7 +562,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelGroupsEditSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(36) + err = routePanelGroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -408,7 +571,8 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routePanelGroupsEditPermsSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(37) + err = routePanelGroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = common.NoSessionMismatch(w,req,user) if err != nil { @@ -416,10 +580,19 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(38) err = routePanelGroupsCreateSubmit(w,req,user) case "/panel/backups/": - err = routePanelBackups(w,req,user,extra_data) + err = common.SuperAdminOnly(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + common.RouteViewCounter.Bump(39) + err = routePanelBackups(w,req,user,extraData) case "/panel/logs/mod/": + common.RouteViewCounter.Bump(40) err = routePanelLogsMod(w,req,user) case "/panel/debug/": err = common.AdminOnly(w,req,user) @@ -428,8 +601,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(41) err = routePanelDebug(w,req,user) default: + common.RouteViewCounter.Bump(42) err = routePanel(w,req,user) } if err != nil { @@ -444,6 +619,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(43) err = routeAccountEditCritical(w,req,user) case "/user/edit/critical/submit/": err = common.NoSessionMismatch(w,req,user) @@ -458,6 +634,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(44) err = routeAccountEditCriticalSubmit(w,req,user) case "/user/edit/avatar/": err = common.MemberOnly(w,req,user) @@ -466,6 +643,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(45) err = routeAccountEditAvatar(w,req,user) case "/user/edit/avatar/submit/": err = common.MemberOnly(w,req,user) @@ -474,6 +652,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(46) err = routeAccountEditAvatarSubmit(w,req,user) case "/user/edit/username/": err = common.MemberOnly(w,req,user) @@ -482,6 +661,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(47) err = routeAccountEditUsername(w,req,user) case "/user/edit/username/submit/": err = common.NoSessionMismatch(w,req,user) @@ -496,6 +676,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(48) err = routeAccountEditUsernameSubmit(w,req,user) case "/user/edit/email/": err = common.MemberOnly(w,req,user) @@ -504,6 +685,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(49) err = routeAccountEditEmail(w,req,user) case "/user/edit/token/": err = common.NoSessionMismatch(w,req,user) @@ -518,9 +700,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - err = routeAccountEditEmailTokenSubmit(w,req,user,extra_data) + common.RouteViewCounter.Bump(50) + err = routeAccountEditEmailTokenSubmit(w,req,user,extraData) default: - req.URL.Path += extra_data + req.URL.Path += extraData + common.RouteViewCounter.Bump(51) err = routeProfile(w,req,user) } if err != nil { @@ -541,6 +725,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(52) err = routeBanSubmit(w,req,user) case "/users/unban/": err = common.NoSessionMismatch(w,req,user) @@ -555,6 +740,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(53) err = routeUnban(w,req,user) case "/users/activate/": err = common.NoSessionMismatch(w,req,user) @@ -569,6 +755,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(54) err = routeActivate(w,req,user) case "/users/ips/": err = common.MemberOnly(w,req,user) @@ -577,50 +764,51 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + common.RouteViewCounter.Bump(55) err = routeIps(w,req,user) } if err != nil { router.handleError(err,w,req,user) } case "/uploads": - if extra_data == "" { + if extraData == "" { common.NotFound(w,req) return } - req.URL.Path += extra_data + req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? - router.UploadHandler(w,req) + router.UploadHandler(w,req) // TODO: Count these views case "": // Stop the favicons, robots.txt file, etc. resolving to the topics list // TODO: Add support for favicons and robots.txt files - switch(extra_data) { + switch(extraData) { case "robots.txt": - err = routeRobotsTxt(w,req) + err = routeRobotsTxt(w,req) // TODO: Count these views if err != nil { router.handleError(err,w,req,user) } return } - if extra_data != "" { + if extraData != "" { common.NotFound(w,req) return } - common.Config.DefaultRoute(w,req,user) + common.Config.DefaultRoute(w,req,user) // TODO: Count these views default: // A fallback for the routes which haven't been converted to the new router yet or plugins router.RLock() - handle, ok := router.extra_routes[req.URL.Path] + handle, ok := router.extraRoutes[req.URL.Path] router.RUnlock() if ok { - req.URL.Path += extra_data - err = handle(w,req,user) + req.URL.Path += extraData + err = handle(w,req,user) // TODO: Count these views if err != nil { router.handleError(err,w,req,user) } return } - common.NotFound(w,req) + common.NotFound(w,req) // TODO: Collect all the error view counts so we can add a replacement for GlobalViewCounter by adding up the view counts of every route? Complex and may be inaccurate, collecting it globally and locally would at-least help find places we aren't capturing views } } diff --git a/main.go b/main.go index 30465373..e697270c 100644 --- a/main.go +++ b/main.go @@ -80,7 +80,11 @@ func afterDBInit() (err error) { if err != nil { return err } - common.GlobalViewCounter, err = common.NewGlobalViewCounter() + common.GlobalViewCounter, err = common.NewChunkedViewCounter() + if err != nil { + return err + } + common.RouteViewCounter, err = common.NewRouteViewCounter() if err != nil { return err } diff --git a/misc_test.go b/misc_test.go index e1bcfa91..4f158b6a 100644 --- a/misc_test.go +++ b/misc_test.go @@ -478,6 +478,28 @@ func TestPermsMiddleware(t *testing.T) { expect(t, ferr == nil, "Logged in users should be able to access member areas") // TODO: Loop over the /user/ routes and make sure only members can access the ones other than /user/username + + // TODO: Write tests for AdminOnly() + + user = common.BlankUser() + + ferr = common.SuperAdminOnly(dummyResponseRecorder, dummyRequest, *user) + expect(t, ferr != nil, "Blank users shouldn't be considered super admins") + + user.IsSuperAdmin = false + ferr = common.SuperAdminOnly(dummyResponseRecorder, dummyRequest, *user) + expect(t, ferr != nil, "Non-super admins shouldn't be allowed through the super admin gate") + + user.IsSuperAdmin = true + ferr = common.SuperAdminOnly(dummyResponseRecorder, dummyRequest, *user) + expect(t, ferr == nil, "Super admins should be allowed through super admin gates") + + // TODO: Make sure only super admins can access the backups route + + //dummyResponseRecorder = httptest.NewRecorder() + //bytesBuffer = bytes.NewBuffer([]byte("")) + //dummyRequest = httptest.NewRequest("", "/panel/backups/", bytesBuffer) + } func TestTopicStore(t *testing.T) { diff --git a/panel_routes.go b/panel_routes.go index b2730270..b1107681 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -1569,9 +1569,6 @@ func routePanelBackups(w http.ResponseWriter, r *http.Request, user common.User, if ferr != nil { return ferr } - if !user.IsSuperAdmin { - return common.NoPermissions(w, r, user) - } if backupURL != "" { // We don't want them trying to break out of this directory, it shouldn't hurt since it's a super admin, but it's always good to practice good security hygiene, especially if this is one of many instances on a managed server not controlled by the superadmin/s diff --git a/query_gen/tables.go b/query_gen/tables.go index 555a7237..4393129a 100644 --- a/query_gen/tables.go +++ b/query_gen/tables.go @@ -159,6 +159,7 @@ func createTables(adapter qgen.Adapter) error { qgen.DBTableColumn{"postCount", "int", 0, false, false, "1"}, qgen.DBTableColumn{"likeCount", "int", 0, false, false, "0"}, qgen.DBTableColumn{"words", "int", 0, false, false, "0"}, + //qgen.DBTableColumn{"views", "int", 0, false, false, "0"}, qgen.DBTableColumn{"css_class", "varchar", 100, false, false, "''"}, qgen.DBTableColumn{"data", "varchar", 200, false, false, "''"}, }, @@ -358,10 +359,22 @@ func createTables(adapter qgen.Adapter) error { []qgen.DBTableColumn{ qgen.DBTableColumn{"count", "int", 0, false, false, "0"}, qgen.DBTableColumn{"createdAt", "datetime", 0, false, false, ""}, + qgen.DBTableColumn{"route", "varchar", 200, false, false, ""}, }, []qgen.DBTableKey{}, ) + /* + qgen.Install.CreateTable("viewchunks_forums", "", "", + []qgen.DBTableColumn{ + qgen.DBTableColumn{"count", "int", 0, false, false, "0"}, + qgen.DBTableColumn{"createdAt", "datetime", 0, false, false, ""}, + qgen.DBTableColumn{"forum", "int", 0, false, false, ""}, + }, + []qgen.DBTableKey{}, + ) + */ + qgen.Install.CreateTable("sync", "", "", []qgen.DBTableColumn{ qgen.DBTableColumn{"last_update", "datetime", 0, false, false, ""}, diff --git a/router_gen/main.go b/router_gen/main.go index fb0d644d..7c732ce5 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -2,25 +2,40 @@ package main import ( + "bytes" "log" "os" + "strconv" + "text/template" ) -//import "strings" - var routeList []*RouteImpl var routeGroups []*RouteGroup +type TmplVars struct { + RouteList []*RouteImpl + RouteGroups []*RouteGroup + AllRouteNames []string + AllRouteMap map[string]int +} + func main() { log.Println("Generating the router...") // Load all the routes... routes() - var out, routeMap string + var tmplVars = TmplVars{ + RouteList: routeList, + RouteGroups: routeGroups, + } + var allRouteNames []string + var allRouteMap = make(map[string]int) + + var out string var mapIt = func(name string) { - routeMap += "\t\"" + name + "\": " + name + ",\n" - //reverseRouteMap += "\t" + name + ": \"" + name + "\",\n" + allRouteNames = append(allRouteNames, name) + allRouteMap[name] = len(allRouteNames) - 1 } var countToIndents = func(indent int) (indentor string) { for i := 0; i < indent; i++ { @@ -47,9 +62,11 @@ func main() { } for _, route := range routeList { + mapIt(route.Name) var end = len(route.Path) - 1 out += "\n\t\tcase \"" + route.Path[0:end] + "\":" out += runBefore(route.RunBefore, 4) + out += "\n\t\t\tcommon.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[route.Name]) + ")" out += "\n\t\t\terr = " + route.Name + "(w,req,user" for _, item := range route.Vars { out += "," + item @@ -58,7 +75,6 @@ func main() { if err != nil { router.handleError(err,w,req,user) }` - mapIt(route.Name) } for _, group := range routeGroups { @@ -73,6 +89,7 @@ func main() { defaultRoute = route continue } + mapIt(route.Name) out += "\n\t\t\t\tcase \"" + route.Path + "\":" if len(route.RunBefore) > 0 { @@ -107,23 +124,24 @@ func main() { } } } + out += "\n\t\t\t\t\tcommon.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[route.Name]) + ")" out += "\n\t\t\t\t\terr = " + route.Name + "(w,req,user" for _, item := range route.Vars { out += "," + item } out += ")" - mapIt(route.Name) } if defaultRoute.Name != "" { + mapIt(defaultRoute.Name) out += "\n\t\t\t\tdefault:" out += runBefore(defaultRoute.RunBefore, 4) + out += "\n\t\t\t\t\tcommon.RouteViewCounter.Bump(" + strconv.Itoa(allRouteMap[defaultRoute.Name]) + ")" out += "\n\t\t\t\t\terr = " + defaultRoute.Name + "(w,req,user" for _, item := range defaultRoute.Vars { out += ", " + item } out += ")" - mapIt(defaultRoute.Name) } out += ` } @@ -132,6 +150,9 @@ func main() { }` } + tmplVars.AllRouteNames = allRouteNames + tmplVars.AllRouteMap = allRouteMap + var fileData = `// Code generated by. DO NOT EDIT. /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main @@ -147,12 +168,28 @@ import ( ) var ErrNoRoute = errors.New("That route doesn't exist.") -var RouteMap = map[string]interface{}{ -` + routeMap + `} +// TODO: What about the /uploads/ route? x.x +var RouteMap = map[string]interface{}{ {{range .AllRouteNames}} + "{{.}}": {{.}},{{end}} +} + +// ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS +var routeMapEnum = map[string]int{ {{range $index, $element := .AllRouteNames}} + "{{$element}}": {{$index}},{{end}} +} +var reverseRouteMapEnum = map[int]string{ {{range $index, $element := .AllRouteNames}} + {{$index}}: "{{$element}}",{{end}} +} + +// TODO: Stop spilling these into the package scope? +func init() { + common.SetRouteMapEnum(routeMapEnum) + common.SetReverseRouteMapEnum(reverseRouteMapEnum) +} type GenRouter struct { UploadHandler func(http.ResponseWriter, *http.Request) - extra_routes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError + extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError sync.RWMutex } @@ -160,7 +197,7 @@ type GenRouter struct { func NewGenRouter(uploads http.Handler) *GenRouter { return &GenRouter{ UploadHandler: http.StripPrefix("/uploads/",uploads).ServeHTTP, - extra_routes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError), + extraRoutes: make(map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError), } } @@ -182,17 +219,17 @@ func (router *GenRouter) Handle(_ string, _ http.Handler) { func (router *GenRouter) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request, common.User) common.RouteError) { router.Lock() defer router.Unlock() - router.extra_routes[pattern] = handle + router.extraRoutes[pattern] = handle } func (router *GenRouter) RemoveFunc(pattern string) error { router.Lock() defer router.Unlock() - _, ok := router.extra_routes[pattern] + _, ok := router.extraRoutes[pattern] if !ok { return ErrNoRoute } - delete(router.extra_routes, pattern) + delete(router.extraRoutes, pattern) return nil } @@ -203,10 +240,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - var prefix, extra_data string + var prefix, extraData string prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1] if req.URL.Path[len(req.URL.Path) - 1] != '/' { - extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] + extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] } @@ -214,12 +251,12 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { log.Print("before routeStatic") log.Print("prefix: ", prefix) log.Print("req.URL.Path: ", req.URL.Path) - log.Print("extra_data: ", extra_data) + log.Print("extraData: ", extraData) log.Print("req.Referer(): ", req.Referer()) } if prefix == "/static" { - req.URL.Path += extra_data + req.URL.Path += extraData routeStatic(w, req) return } @@ -237,54 +274,62 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } if common.Dev.SuperDebug { log.Print("after PreRoute") + log.Print("routeMapEnum: ", routeMapEnum) } var err common.RouteError switch(prefix) {` + out + ` case "/uploads": - if extra_data == "" { + if extraData == "" { common.NotFound(w,req) return } - req.URL.Path += extra_data + req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? - router.UploadHandler(w,req) + router.UploadHandler(w,req) // TODO: Count these views case "": // Stop the favicons, robots.txt file, etc. resolving to the topics list // TODO: Add support for favicons and robots.txt files - switch(extra_data) { + switch(extraData) { case "robots.txt": - err = routeRobotsTxt(w,req) + err = routeRobotsTxt(w,req) // TODO: Count these views if err != nil { router.handleError(err,w,req,user) } return } - if extra_data != "" { + if extraData != "" { common.NotFound(w,req) return } - common.Config.DefaultRoute(w,req,user) + common.Config.DefaultRoute(w,req,user) // TODO: Count these views default: // A fallback for the routes which haven't been converted to the new router yet or plugins router.RLock() - handle, ok := router.extra_routes[req.URL.Path] + handle, ok := router.extraRoutes[req.URL.Path] router.RUnlock() if ok { - req.URL.Path += extra_data - err = handle(w,req,user) + req.URL.Path += extraData + err = handle(w,req,user) // TODO: Count these views if err != nil { router.handleError(err,w,req,user) } return } - common.NotFound(w,req) + common.NotFound(w,req) // TODO: Collect all the error view counts so we can add a replacement for GlobalViewCounter by adding up the view counts of every route? Complex and may be inaccurate, collecting it globally and locally would at-least help find places we aren't capturing views } } ` - writeFile("./gen_router.go", fileData) + var tmpl = template.Must(template.New("router").Parse(fileData)) + var b bytes.Buffer + err := tmpl.Execute(&b, tmplVars) + if err != nil { + log.Fatal(err) + } + + writeFile("./gen_router.go", string(b.Bytes())) log.Println("Successfully generated the router") } diff --git a/router_gen/routes.go b/router_gen/routes.go index 5a93ebbc..e29d3b4e 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -4,23 +4,23 @@ package main func routes() { addRoute(View("routeAPI", "/api/")) addRoute(View("routeOverview", "/overview/")) - //addRoute("routeCustomPage","/pages/",""/*,"&extra_data"*/) + //addRoute("routeCustomPage","/pages/",""/*,"&extraData"*/) addRoute(View("routeForums", "/forums/" /*,"&forums"*/)) - addRoute(View("routeForum", "/forum/", "extra_data")) + addRoute(View("routeForum", "/forum/", "extraData")) addRoute(AnonAction("routeChangeTheme", "/theme/")) addRoute( - View("routeShowAttachment", "/attachs/", "extra_data").Before("ParseForm"), + View("routeShowAttachment", "/attachs/", "extraData").Before("ParseForm"), ) // TODO: Reduce the number of Befores. With a new method, perhaps? reportGroup := newRouteGroup("/report/", - Action("routeReportSubmit", "/report/submit/", "extra_data"), + Action("routeReportSubmit", "/report/submit/", "extraData"), ).Before("NoBanned") addRouteGroup(reportGroup) topicGroup := newRouteGroup("/topics/", View("routeTopics", "/topics/"), - MemberView("routeTopicCreate", "/topics/create/", "extra_data"), + MemberView("routeTopicCreate", "/topics/create/", "extraData"), ) addRouteGroup(topicGroup) @@ -32,7 +32,7 @@ func routes() { func buildUserRoutes() { userGroup := newRouteGroup("/user/") userGroup.Routes( - View("routeProfile", "/user/").LitBefore("req.URL.Path += extra_data"), + View("routeProfile", "/user/").LitBefore("req.URL.Path += extraData"), MemberView("routeAccountEditCritical", "/user/edit/critical/"), Action("routeAccountEditCriticalSubmit", "/user/edit/critical/submit/"), // TODO: Full test this MemberView("routeAccountEditAvatar", "/user/edit/avatar/"), @@ -40,7 +40,7 @@ func buildUserRoutes() { MemberView("routeAccountEditUsername", "/user/edit/username/"), Action("routeAccountEditUsernameSubmit", "/user/edit/username/submit/"), // TODO: Full test this MemberView("routeAccountEditEmail", "/user/edit/email/"), - Action("routeAccountEditEmailTokenSubmit", "/user/edit/token/", "extra_data"), + Action("routeAccountEditEmailTokenSubmit", "/user/edit/token/", "extraData"), ) addRouteGroup(userGroup) @@ -50,7 +50,7 @@ func buildUserRoutes() { Action("routeBanSubmit", "/users/ban/submit/"), Action("routeUnban", "/users/unban/"), Action("routeActivate", "/users/activate/"), - MemberView("routeIps", "/users/ips/"), + MemberView("routeIps", "/users/ips/"), // TODO: .Perms("ViewIPs")? ) addRouteGroup(userGroup) } @@ -61,42 +61,42 @@ func buildPanelRoutes() { View("routePanel", "/panel/"), View("routePanelForums", "/panel/forums/"), Action("routePanelForumsCreateSubmit", "/panel/forums/create/"), - Action("routePanelForumsDelete", "/panel/forums/delete/", "extra_data"), - Action("routePanelForumsDeleteSubmit", "/panel/forums/delete/submit/", "extra_data"), - View("routePanelForumsEdit", "/panel/forums/edit/", "extra_data"), - Action("routePanelForumsEditSubmit", "/panel/forums/edit/submit/", "extra_data"), - Action("routePanelForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extra_data"), + Action("routePanelForumsDelete", "/panel/forums/delete/", "extraData"), + Action("routePanelForumsDeleteSubmit", "/panel/forums/delete/submit/", "extraData"), + View("routePanelForumsEdit", "/panel/forums/edit/", "extraData"), + Action("routePanelForumsEditSubmit", "/panel/forums/edit/submit/", "extraData"), + Action("routePanelForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extraData"), View("routePanelSettings", "/panel/settings/"), - View("routePanelSettingEdit", "/panel/settings/edit/", "extra_data"), - Action("routePanelSettingEditSubmit", "/panel/settings/edit/submit/", "extra_data"), + View("routePanelSettingEdit", "/panel/settings/edit/", "extraData"), + Action("routePanelSettingEditSubmit", "/panel/settings/edit/submit/", "extraData"), View("routePanelWordFilters", "/panel/settings/word-filters/"), Action("routePanelWordFiltersCreate", "/panel/settings/word-filters/create/"), - View("routePanelWordFiltersEdit", "/panel/settings/word-filters/edit/", "extra_data"), - Action("routePanelWordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extra_data"), - Action("routePanelWordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extra_data"), + View("routePanelWordFiltersEdit", "/panel/settings/word-filters/edit/", "extraData"), + Action("routePanelWordFiltersEditSubmit", "/panel/settings/word-filters/edit/submit/", "extraData"), + Action("routePanelWordFiltersDeleteSubmit", "/panel/settings/word-filters/delete/submit/", "extraData"), View("routePanelThemes", "/panel/themes/"), - Action("routePanelThemesSetDefault", "/panel/themes/default/", "extra_data"), + Action("routePanelThemesSetDefault", "/panel/themes/default/", "extraData"), View("routePanelPlugins", "/panel/plugins/"), - Action("routePanelPluginsActivate", "/panel/plugins/activate/", "extra_data"), - Action("routePanelPluginsDeactivate", "/panel/plugins/deactivate/", "extra_data"), - Action("routePanelPluginsInstall", "/panel/plugins/install/", "extra_data"), + Action("routePanelPluginsActivate", "/panel/plugins/activate/", "extraData"), + Action("routePanelPluginsDeactivate", "/panel/plugins/deactivate/", "extraData"), + Action("routePanelPluginsInstall", "/panel/plugins/install/", "extraData"), View("routePanelUsers", "/panel/users/"), - View("routePanelUsersEdit", "/panel/users/edit/", "extra_data"), - Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extra_data"), + View("routePanelUsersEdit", "/panel/users/edit/", "extraData"), + Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"), View("routePanelGroups", "/panel/groups/"), - View("routePanelGroupsEdit", "/panel/groups/edit/", "extra_data"), - View("routePanelGroupsEditPerms", "/panel/groups/edit/perms/", "extra_data"), - Action("routePanelGroupsEditSubmit", "/panel/groups/edit/submit/", "extra_data"), - Action("routePanelGroupsEditPermsSubmit", "/panel/groups/edit/perms/submit/", "extra_data"), + View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"), + View("routePanelGroupsEditPerms", "/panel/groups/edit/perms/", "extraData"), + Action("routePanelGroupsEditSubmit", "/panel/groups/edit/submit/", "extraData"), + Action("routePanelGroupsEditPermsSubmit", "/panel/groups/edit/perms/submit/", "extraData"), Action("routePanelGroupsCreateSubmit", "/panel/groups/create/"), - View("routePanelBackups", "/panel/backups/", "extra_data"), + View("routePanelBackups", "/panel/backups/", "extraData").Before("SuperAdminOnly"), // TODO: Test View("routePanelLogsMod", "/panel/logs/mod/"), View("routePanelDebug", "/panel/debug/").Before("AdminOnly"), ) diff --git a/schema/mssql/query_viewchunks.sql b/schema/mssql/query_viewchunks.sql index efff665a..c3ba8519 100644 --- a/schema/mssql/query_viewchunks.sql +++ b/schema/mssql/query_viewchunks.sql @@ -1,4 +1,5 @@ CREATE TABLE [viewchunks] ( [count] int DEFAULT 0 not null, - [createdAt] datetime not null + [createdAt] datetime not null, + [route] nvarchar (200) not null ); \ No newline at end of file diff --git a/schema/mysql/query_viewchunks.sql b/schema/mysql/query_viewchunks.sql index 803c612e..a0ebf7d7 100644 --- a/schema/mysql/query_viewchunks.sql +++ b/schema/mysql/query_viewchunks.sql @@ -1,4 +1,5 @@ CREATE TABLE `viewchunks` ( `count` int DEFAULT 0 not null, - `createdAt` datetime not null + `createdAt` datetime not null, + `route` varchar(200) not null ); \ No newline at end of file diff --git a/schema/pgsql/query_viewchunks.sql b/schema/pgsql/query_viewchunks.sql index 4065bcbc..5177bdc8 100644 --- a/schema/pgsql/query_viewchunks.sql +++ b/schema/pgsql/query_viewchunks.sql @@ -1,4 +1,5 @@ CREATE TABLE `viewchunks` ( `count` int DEFAULT 0 not null, - `createdAt` timestamp not null + `createdAt` timestamp not null, + `route` varchar (200) not null ); \ No newline at end of file diff --git a/template_forum.go b/template_forum.go index 3b784f23..b0551377 100644 --- a/template_forum.go +++ b/template_forum.go @@ -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() { @@ -143,54 +143,58 @@ w.Write([]byte(item.Creator.Link)) w.Write(forum_30) w.Write([]byte(item.Creator.Avatar)) w.Write(forum_31) -w.Write([]byte(item.Link)) +w.Write([]byte(item.Creator.Avatar)) w.Write(forum_32) -w.Write([]byte(item.Title)) +w.Write([]byte(item.Link)) w.Write(forum_33) -w.Write([]byte(item.Creator.Link)) +w.Write([]byte(item.Title)) w.Write(forum_34) -w.Write([]byte(item.Creator.Name)) +w.Write([]byte(item.Creator.Link)) w.Write(forum_35) -if item.IsClosed { +w.Write([]byte(item.Creator.Name)) w.Write(forum_36) -} -if item.Sticky { +if item.IsClosed { w.Write(forum_37) } -w.Write(forum_38) -w.Write([]byte(strconv.Itoa(item.PostCount))) -w.Write(forum_39) -w.Write([]byte(strconv.Itoa(item.LikeCount))) -w.Write(forum_40) if item.Sticky { +w.Write(forum_38) +} +w.Write(forum_39) +w.Write([]byte(strconv.Itoa(item.PostCount))) +w.Write(forum_40) +w.Write([]byte(strconv.Itoa(item.LikeCount))) w.Write(forum_41) +if item.Sticky { +w.Write(forum_42) } else { if item.IsClosed { -w.Write(forum_42) -} -} w.Write(forum_43) -w.Write([]byte(item.LastUser.Link)) +} +} w.Write(forum_44) -w.Write([]byte(item.LastUser.Avatar)) -w.Write(forum_45) w.Write([]byte(item.LastUser.Link)) +w.Write(forum_45) +w.Write([]byte(item.LastUser.Avatar)) w.Write(forum_46) w.Write([]byte(item.LastUser.Name)) w.Write(forum_47) -w.Write([]byte(item.RelativeLastReplyAt)) +w.Write([]byte(item.LastUser.Link)) w.Write(forum_48) +w.Write([]byte(item.LastUser.Name)) +w.Write(forum_49) +w.Write([]byte(item.RelativeLastReplyAt)) +w.Write(forum_50) } } else { -w.Write(forum_49) -if tmpl_forum_vars.CurrentUser.Perms.CreateTopic { -w.Write(forum_50) -w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID))) w.Write(forum_51) -} +if tmpl_forum_vars.CurrentUser.Perms.CreateTopic { w.Write(forum_52) -} +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID))) w.Write(forum_53) +} +w.Write(forum_54) +} +w.Write(forum_55) w.Write(footer_0) w.Write([]byte(common.BuildWidget("footer",tmpl_forum_vars.Header))) w.Write(footer_1) diff --git a/template_list.go b/template_list.go index 5f29baee..7599f2fe 100644 --- a/template_list.go +++ b/template_list.go @@ -352,127 +352,139 @@ var topic_alt_29 = []byte(`
`) var topic_alt_30 = []byte(`+1`) +var topic_alt_31 = []byte(`" class="action_button like_item add_like" aria-label="Like this post" data-action="like">`) var topic_alt_32 = []byte(`Edit`) +var topic_alt_33 = []byte(`" class="action_button open_edit" aria-label="Edit this post" data-action="edit">`) var topic_alt_34 = []byte(`Delete`) +var topic_alt_35 = []byte(`" class="action_button delete_item" aria-label="Delete this post" data-action="delete">`) var topic_alt_36 = []byte(`Unlock`) +var topic_alt_37 = []byte(`' class="action_button unlock_item" data-action="unlock">`) var topic_alt_38 = []byte(`Lock`) +var topic_alt_39 = []byte(`' class="action_button lock_item" data-action="lock">`) var topic_alt_40 = []byte(`Unpin`) +var topic_alt_41 = []byte(`' class="action_button unpin_item" data-action="unpin">`) var topic_alt_42 = []byte(`Pin`) -var topic_alt_44 = []byte(` +var topic_alt_43 = []byte(`' class="action_button pin_item" data-action="pin">`) +var topic_alt_44 = []byte(``) +var topic_alt_46 = []byte(` Report +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"> + `) -var topic_alt_47 = []byte(` -
+var topic_alt_49 = []byte(` +
`) -var topic_alt_48 = []byte(``) -var topic_alt_50 = []byte(` - `) -var topic_alt_51 = []byte(` - `) -var topic_alt_52 = []byte(``) +var topic_alt_52 = []byte(``) var topic_alt_54 = []byte(` + `) +var topic_alt_55 = []byte(` + `) +var topic_alt_56 = []byte(``) +var topic_alt_58 = []byte(``) +var topic_alt_59 = []byte(`
`) -var topic_alt_55 = []byte(` +var topic_alt_60 = []byte(`
+var topic_alt_61 = []byte(`action_item`) +var topic_alt_62 = []byte(`">
 
+var topic_alt_63 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;"> 
+var topic_alt_64 = []byte(`" class="the_name" rel="author">`) +var topic_alt_65 = []byte(` `) -var topic_alt_61 = []byte(`
`) -var topic_alt_63 = []byte(`
`) -var topic_alt_65 = []byte(` +var topic_alt_66 = []byte(`
`) +var topic_alt_68 = []byte(`
`) +var topic_alt_70 = []byte(`
+var topic_alt_71 = []byte(`style="margin-left: 0px;"`) +var topic_alt_72 = []byte(`> `) -var topic_alt_68 = []byte(` +var topic_alt_73 = []byte(` `) -var topic_alt_69 = []byte(` +var topic_alt_74 = []byte(` `) -var topic_alt_70 = []byte(` +var topic_alt_75 = []byte(` `) -var topic_alt_71 = []byte(` +var topic_alt_76 = []byte(`
`) -var topic_alt_72 = []byte(`
+var topic_alt_77 = []byte(`
`) -var topic_alt_73 = []byte(``) -var topic_alt_75 = []byte(`Edit`) -var topic_alt_77 = []byte(`Delete`) -var topic_alt_79 = []byte(` +var topic_alt_78 = []byte(``) +var topic_alt_80 = []byte(``) +var topic_alt_82 = []byte(``) +var topic_alt_84 = []byte(``) +var topic_alt_86 = []byte(` Report +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"> + `) -var topic_alt_82 = []byte(` -
- `) -var topic_alt_83 = []byte(``) -var topic_alt_85 = []byte(` - `) -var topic_alt_86 = []byte(` - `) -var topic_alt_87 = []byte(``) -var topic_alt_88 = []byte(``) var topic_alt_89 = []byte(` +
+ `) +var topic_alt_92 = []byte(``) +var topic_alt_94 = []byte(` + `) +var topic_alt_95 = []byte(` + `) +var topic_alt_96 = []byte(``) +var topic_alt_98 = []byte(``) +var topic_alt_99 = []byte(`
`) -var topic_alt_90 = []byte(` +var topic_alt_100 = []byte(`
`) -var topic_alt_91 = []byte(` +var topic_alt_101 = []byte(` `) -var topic_alt_92 = []byte(` +var topic_alt_102 = []byte(`
 
+var topic_alt_103 = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;"> 
+var topic_alt_104 = []byte(`" class="the_name" rel="author">`) +var topic_alt_105 = []byte(` `) -var topic_alt_96 = []byte(`
`) -var topic_alt_98 = []byte(`
`) -var topic_alt_100 = []byte(` +var topic_alt_106 = []byte(`
`) +var topic_alt_108 = []byte(`
`) +var topic_alt_110 = []byte(`
+var topic_alt_111 = []byte(`' type="hidden" />
@@ -482,17 +494,17 @@ var topic_alt_101 = []byte(`' type="hidden" />
`) -var topic_alt_102 = []byte(` +var topic_alt_112 = []byte(`
`) -var topic_alt_103 = []byte(` +var topic_alt_113 = []byte(`
`) -var topic_alt_104 = []byte(` +var topic_alt_114 = []byte(` @@ -762,7 +774,7 @@ var topics_9 = []byte(` `) diff --git a/template_topic_alt.go b/template_topic_alt.go index a541eb14..2334f38d 100644 --- a/template_topic_alt.go +++ b/template_topic_alt.go @@ -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() { @@ -176,132 +176,154 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write(topic_alt_43) } } -w.Write(topic_alt_44) -w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) -w.Write(topic_alt_45) -w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) -w.Write(topic_alt_46) -} -w.Write(topic_alt_47) -if tmpl_topic_alt_vars.Topic.LikeCount > 0 { -w.Write(topic_alt_48) -w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount))) -w.Write(topic_alt_49) -} -w.Write(topic_alt_50) -w.Write([]byte(tmpl_topic_alt_vars.Topic.RelativeCreatedAt)) -w.Write(topic_alt_51) if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { -w.Write(topic_alt_52) +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_49) +if tmpl_topic_alt_vars.Topic.LikeCount > 0 { +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([]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_59) if len(tmpl_topic_alt_vars.ItemList) != 0 { for _, item := range tmpl_topic_alt_vars.ItemList { -w.Write(topic_alt_55) -if item.ActionType != "" { -w.Write(topic_alt_56) -} -w.Write(topic_alt_57) -w.Write([]byte(item.Avatar)) -w.Write(topic_alt_58) -w.Write([]byte(item.UserLink)) -w.Write(topic_alt_59) -w.Write([]byte(item.CreatedByName)) w.Write(topic_alt_60) -if item.Tag != "" { +if item.ActionType != "" { w.Write(topic_alt_61) -w.Write([]byte(item.Tag)) +} w.Write(topic_alt_62) -} else { +w.Write([]byte(item.Avatar)) w.Write(topic_alt_63) -w.Write([]byte(strconv.Itoa(item.Level))) +w.Write([]byte(item.UserLink)) w.Write(topic_alt_64) -} +w.Write([]byte(item.CreatedByName)) w.Write(topic_alt_65) -if item.ActionType != "" { +if item.Tag != "" { w.Write(topic_alt_66) -} +w.Write([]byte(item.Tag)) w.Write(topic_alt_67) -if item.ActionType != "" { -w.Write(topic_alt_68) -w.Write([]byte(item.ActionIcon)) -w.Write(topic_alt_69) -w.Write([]byte(item.ActionType)) -w.Write(topic_alt_70) } else { +w.Write(topic_alt_68) +w.Write([]byte(strconv.Itoa(item.Level))) +w.Write(topic_alt_69) +} +w.Write(topic_alt_70) +if item.ActionType != "" { w.Write(topic_alt_71) -w.Write([]byte(item.ContentHtml)) +} w.Write(topic_alt_72) +if item.ActionType != "" { +w.Write(topic_alt_73) +w.Write([]byte(item.ActionIcon)) +w.Write(topic_alt_74) +w.Write([]byte(item.ActionType)) +w.Write(topic_alt_75) +} else { +w.Write(topic_alt_76) +w.Write([]byte(item.ContentHtml)) +w.Write(topic_alt_77) if tmpl_topic_alt_vars.CurrentUser.Loggedin { if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem { -w.Write(topic_alt_73) +w.Write(topic_alt_78) w.Write([]byte(strconv.Itoa(item.ID))) -w.Write(topic_alt_74) +w.Write(topic_alt_79) } if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply { -w.Write(topic_alt_75) -w.Write([]byte(strconv.Itoa(item.ID))) -w.Write(topic_alt_76) -} -if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply { -w.Write(topic_alt_77) -w.Write([]byte(strconv.Itoa(item.ID))) -w.Write(topic_alt_78) -} -w.Write(topic_alt_79) -w.Write([]byte(strconv.Itoa(item.ID))) w.Write(topic_alt_80) -w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session)) +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) -if item.LikeCount > 0 { +w.Write([]byte(strconv.Itoa(item.ID))) w.Write(topic_alt_83) -w.Write([]byte(strconv.Itoa(item.LikeCount))) -w.Write(topic_alt_84) } -w.Write(topic_alt_85) -w.Write([]byte(item.RelativeCreatedAt)) -w.Write(topic_alt_86) if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { -w.Write(topic_alt_87) +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(topic_alt_89) -} +if item.LikeCount > 0 { w.Write(topic_alt_90) } -} w.Write(topic_alt_91) -if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply { +if item.LikeCount > 0 { w.Write(topic_alt_92) -w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar)) +w.Write([]byte(strconv.Itoa(item.LikeCount))) w.Write(topic_alt_93) -w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Link)) +} w.Write(topic_alt_94) -w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Name)) +w.Write([]byte(item.RelativeCreatedAt)) w.Write(topic_alt_95) -if tmpl_topic_alt_vars.CurrentUser.Tag != "" { +if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { w.Write(topic_alt_96) -w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag)) +w.Write([]byte(item.IPAddress)) w.Write(topic_alt_97) -} else { +w.Write([]byte(item.IPAddress)) w.Write(topic_alt_98) -w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.CurrentUser.Level))) +} w.Write(topic_alt_99) } w.Write(topic_alt_100) -w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) +} +} w.Write(topic_alt_101) -if tmpl_topic_alt_vars.CurrentUser.Perms.UploadFiles { +if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply { w.Write(topic_alt_102) -} +w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Avatar)) 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(topic_alt_105) +if tmpl_topic_alt_vars.CurrentUser.Tag != "" { +w.Write(topic_alt_106) +w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Tag)) +w.Write(topic_alt_107) +} else { +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 { +w.Write(topic_alt_112) +} +w.Write(topic_alt_113) +} +w.Write(topic_alt_114) w.Write(footer_0) w.Write([]byte(common.BuildWidget("footer",tmpl_topic_alt_vars.Header))) w.Write(footer_1) diff --git a/template_topics.go b/template_topics.go index 2f092150..78a5e736 100644 --- a/template_topics.go +++ b/template_topics.go @@ -133,60 +133,64 @@ w.Write([]byte(item.Creator.Link)) w.Write(topics_25) w.Write([]byte(item.Creator.Avatar)) w.Write(topics_26) -w.Write([]byte(item.Link)) -w.Write(topics_27) -w.Write([]byte(item.Title)) -w.Write(topics_28) -if item.ForumName != "" { -w.Write(topics_29) -w.Write([]byte(item.ForumLink)) -w.Write(topics_30) -w.Write([]byte(item.ForumName)) -w.Write(topics_31) -} -w.Write(topics_32) -w.Write([]byte(item.Creator.Link)) -w.Write(topics_33) w.Write([]byte(item.Creator.Name)) -w.Write(topics_34) -if item.IsClosed { -w.Write(topics_35) +w.Write(topics_27) +w.Write([]byte(item.Link)) +w.Write(topics_28) +w.Write([]byte(item.Title)) +w.Write(topics_29) +if item.ForumName != "" { +w.Write(topics_30) +w.Write([]byte(item.ForumLink)) +w.Write(topics_31) +w.Write([]byte(item.ForumName)) +w.Write(topics_32) } -if item.Sticky { +w.Write(topics_33) +w.Write([]byte(item.Creator.Link)) +w.Write(topics_34) +w.Write([]byte(item.Creator.Name)) +w.Write(topics_35) +if item.IsClosed { w.Write(topics_36) } -w.Write(topics_37) -w.Write([]byte(strconv.Itoa(item.PostCount))) -w.Write(topics_38) -w.Write([]byte(strconv.Itoa(item.LikeCount))) -w.Write(topics_39) if item.Sticky { +w.Write(topics_37) +} +w.Write(topics_38) +w.Write([]byte(strconv.Itoa(item.PostCount))) +w.Write(topics_39) +w.Write([]byte(strconv.Itoa(item.LikeCount))) w.Write(topics_40) +if item.Sticky { +w.Write(topics_41) } else { if item.IsClosed { -w.Write(topics_41) -} -} w.Write(topics_42) -w.Write([]byte(item.LastUser.Link)) +} +} w.Write(topics_43) -w.Write([]byte(item.LastUser.Avatar)) -w.Write(topics_44) w.Write([]byte(item.LastUser.Link)) +w.Write(topics_44) +w.Write([]byte(item.LastUser.Avatar)) w.Write(topics_45) w.Write([]byte(item.LastUser.Name)) w.Write(topics_46) -w.Write([]byte(item.RelativeLastReplyAt)) +w.Write([]byte(item.LastUser.Link)) w.Write(topics_47) -} -} else { +w.Write([]byte(item.LastUser.Name)) w.Write(topics_48) -if tmpl_topics_vars.CurrentUser.Perms.CreateTopic { +w.Write([]byte(item.RelativeLastReplyAt)) w.Write(topics_49) } +} else { w.Write(topics_50) -} +if tmpl_topics_vars.CurrentUser.Perms.CreateTopic { w.Write(topics_51) +} +w.Write(topics_52) +} +w.Write(topics_53) w.Write(footer_0) w.Write([]byte(common.BuildWidget("footer",tmpl_topics_vars.Header))) w.Write(footer_1) diff --git a/templates/forum.html b/templates/forum.html index 9c67eb49..1bc671ec 100644 --- a/templates/forum.html +++ b/templates/forum.html @@ -39,7 +39,7 @@ {{if .CurrentUser.Perms.CreateTopic}}