diff --git a/bot_routes.go b/bot_routes.go index a9299dba..4e26f376 100644 --- a/bot_routes.go +++ b/bot_routes.go @@ -47,7 +47,7 @@ func routeSitemapXml(w http.ResponseWriter, r *http.Request) common.RouteError { writeXMLHeader(w, r) w.Write([]byte("\n")) sitemapItem("sitemaps/topics.xml") - sitemapItem("sitemaps/forums.xml") + //sitemapItem("sitemaps/forums.xml") //sitemapItem("sitemaps/users.xml") w.Write([]byte("")) diff --git a/common/counters.go b/common/counters.go index 8853dee5..87932509 100644 --- a/common/counters.go +++ b/common/counters.go @@ -2,7 +2,6 @@ package common import ( "database/sql" - "log" "sync" "sync/atomic" @@ -28,6 +27,7 @@ func NewChunkedViewCounter() (*ChunkedViewCounter, error) { } AddScheduledFifteenMinuteTask(counter.Tick) // This is run once every fifteen minutes to match the frequency of the RouteViewCounter //AddScheduledSecondTask(counter.Tick) + AddShutdownTask(counter.Tick) return counter, acc.FirstError() } @@ -82,6 +82,7 @@ func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) { } AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second //AddScheduledSecondTask(counter.Tick) + AddShutdownTask(counter.Tick) return counter, acc.FirstError() } @@ -113,7 +114,7 @@ func (counter *DefaultRouteViewCounter) insertChunk(count int, route int) error func (counter *DefaultRouteViewCounter) Bump(route int) { // TODO: Test this check - log.Print("counter.routeBuckets[route]: ", counter.routeBuckets[route]) + debugLog("counter.routeBuckets[", route, "]: ", counter.routeBuckets[route]) if len(counter.routeBuckets) <= route { return } @@ -157,41 +158,53 @@ func NewDefaultTopicViewCounter() (*DefaultTopicViewCounter, error) { evenTopics: make(map[int]*RWMutexCounterBucket), update: acc.Update("topics").Set("views = views + ?").Where("tid = ?").Prepare(), } - AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second + AddScheduledFifteenMinuteTask(counter.Tick) // Who knows how many topics we have queued up, we probably don't want this running too frequently //AddScheduledSecondTask(counter.Tick) + AddShutdownTask(counter.Tick) return counter, acc.FirstError() } func (counter *DefaultTopicViewCounter) Tick() error { counter.oddLock.RLock() - for topicID, topic := range counter.oddTopics { + oddTopics := counter.oddTopics + counter.oddLock.RUnlock() + for topicID, topic := range oddTopics { var count int topic.RLock() count = topic.counter topic.RUnlock() + // TODO: Only delete the bucket when it's zero to avoid hitting popular topics? + counter.oddLock.Lock() + delete(counter.oddTopics, topicID) + counter.oddLock.Unlock() err := counter.insertChunk(count, topicID) if err != nil { return err } } - counter.oddLock.RUnlock() counter.evenLock.RLock() - for topicID, topic := range counter.evenTopics { + evenTopics := counter.evenTopics + counter.evenLock.RUnlock() + for topicID, topic := range evenTopics { var count int topic.RLock() count = topic.counter topic.RUnlock() + // TODO: Only delete the bucket when it's zero to avoid hitting popular topics? + counter.evenLock.Lock() + delete(counter.evenTopics, topicID) + counter.evenLock.Unlock() err := counter.insertChunk(count, topicID) if err != nil { return err } } - counter.evenLock.RUnlock() return nil } +// TODO: Optimise this further. E.g. Using IN() on every one view topic. Rinse and repeat for two views, three views, four views and five views. func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) error { if count == 0 { return nil @@ -204,9 +217,9 @@ func (counter *DefaultTopicViewCounter) insertChunk(count int, topicID int) erro func (counter *DefaultTopicViewCounter) Bump(topicID int) { // Is the ID even? if topicID%2 == 0 { - counter.evenLock.Lock() + counter.evenLock.RLock() topic, ok := counter.evenTopics[topicID] - counter.evenLock.Unlock() + counter.evenLock.RUnlock() if ok { topic.Lock() topic.counter++ @@ -219,9 +232,9 @@ func (counter *DefaultTopicViewCounter) Bump(topicID int) { return } - counter.oddLock.Lock() + counter.oddLock.RLock() topic, ok := counter.oddTopics[topicID] - counter.oddLock.Unlock() + counter.oddLock.RUnlock() if ok { topic.Lock() topic.counter++ diff --git a/common/tasks.go b/common/tasks.go index 513a13de..0069bb19 100644 --- a/common/tasks.go +++ b/common/tasks.go @@ -21,6 +21,7 @@ type TaskStmts struct { var ScheduledSecondTasks []func() error var ScheduledFifteenMinuteTasks []func() error +var ShutdownTasks []func() error var taskStmts TaskStmts var lastSync time.Time @@ -45,6 +46,11 @@ func AddScheduledFifteenMinuteTask(task func() error) { ScheduledFifteenMinuteTasks = append(ScheduledFifteenMinuteTasks, task) } +// AddShutdownTask is not concurrency safe +func AddShutdownTask(task func() error) { + ShutdownTasks = append(ShutdownTasks, task) +} + // TODO: Use AddScheduledSecondTask func HandleExpiredScheduledGroups() error { rows, err := taskStmts.getExpiredScheduledGroups.Query() diff --git a/gen_router.go b/gen_router.go index 3d0ee519..8132033d 100644 --- a/gen_router.go +++ b/gen_router.go @@ -72,6 +72,8 @@ var RouteMap = map[string]interface{}{ "routeUnban": routeUnban, "routeActivate": routeActivate, "routeIps": routeIps, + "routeDynamic": routeDynamic, + "routeUploads": routeUploads, } // ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS @@ -133,6 +135,8 @@ var routeMapEnum = map[string]int{ "routeUnban": 54, "routeActivate": 55, "routeIps": 56, + "routeDynamic": 57, + "routeUploads": 58, } var reverseRouteMapEnum = map[int]string{ 0: "routeAPI", @@ -192,6 +196,8 @@ var reverseRouteMapEnum = map[int]string{ 54: "routeUnban", 55: "routeActivate", 56: "routeIps", + 57: "routeDynamic", + 58: "routeUploads", } // TODO: Stop spilling these into the package scope? @@ -794,6 +800,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.NotFound(w,req) return } + common.RouteViewCounter.Bump(58) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? router.UploadHandler(w,req) // TODO: Count these views @@ -837,8 +844,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { router.RUnlock() if ok { + common.RouteViewCounter.Bump(57) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData - err = handle(w,req,user) // TODO: Count these views + err = handle(w,req,user) if err != nil { router.handleError(err,w,req,user) } diff --git a/main.go b/main.go index e6128765..82c76032 100644 --- a/main.go +++ b/main.go @@ -234,6 +234,14 @@ func main() { } } + var runTasks = func(tasks []func() error) { + for _, task := range tasks { + if task() != nil { + common.LogError(err) + } + } + } + // Run this goroutine once a second secondTicker := time.NewTicker(1 * time.Second) fifteenMinuteTicker := time.NewTicker(15 * time.Minute) @@ -242,22 +250,16 @@ func main() { for { select { case <-secondTicker.C: - //log.Print("Running the second ticker") // TODO: Add a plugin hook here + runTasks(common.ScheduledSecondTasks) - for _, task := range common.ScheduledSecondTasks { - if task() != nil { - common.LogError(err) - } - } - + // TODO: Stop hard-coding this err := common.HandleExpiredScheduledGroups() if err != nil { common.LogError(err) } // TODO: Handle delayed moderation tasks - // TODO: Handle the daily clean-up. Move this to a 24 hour task? // Sync with the database, if there are any changes err = common.HandleServerSync() @@ -273,18 +275,15 @@ func main() { // TODO: Add a plugin hook here case <-fifteenMinuteTicker.C: // TODO: Add a plugin hook here - - for _, task := range common.ScheduledFifteenMinuteTasks { - if task() != nil { - common.LogError(err) - } - } + runTasks(common.ScheduledFifteenMinuteTasks) // TODO: Automatically lock topics, if they're really old, and the associated setting is enabled. // TODO: Publish scheduled posts. // TODO: Add a plugin hook here } + + // TODO: Handle the daily clean-up. } }() @@ -329,6 +328,7 @@ func main() { go func() { sig := <-sigs // TODO: Gracefully shutdown the HTTP server + runTasks(common.ShutdownTasks) log.Fatal("Received a signal to shutdown: ", sig) }() diff --git a/router_gen/main.go b/router_gen/main.go index 72db1971..c4b272c1 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -150,6 +150,9 @@ func main() { }` } + // Stubs for us to refer to these routes through + mapIt("routeDynamic") + mapIt("routeUploads") tmplVars.AllRouteNames = allRouteNames tmplVars.AllRouteMap = allRouteMap @@ -294,6 +297,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.NotFound(w,req) return } + common.RouteViewCounter.Bump({{.AllRouteMap.routeUploads}}) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? router.UploadHandler(w,req) // TODO: Count these views @@ -337,8 +341,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { router.RUnlock() if ok { + common.RouteViewCounter.Bump({{.AllRouteMap.routeDynamic}}) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData - err = handle(w,req,user) // TODO: Count these views + err = handle(w,req,user) if err != nil { router.handleError(err,w,req,user) } diff --git a/routes.go b/routes.go index bd39a64e..24efdc80 100644 --- a/routes.go +++ b/routes.go @@ -40,6 +40,12 @@ func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } +// Temporary stubs for view tracking +func routeDynamic() { +} +func routeUploads() { +} + // GET functions func routeStatic(w http.ResponseWriter, r *http.Request) { file, ok := common.StaticFiles.Get(r.URL.Path) @@ -613,6 +619,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm if err != nil { return common.InternalError(err, w, r) } + common.TopicViewCounter.Bump(topic.ID) // TODO Move this into the router? return nil } diff --git a/template_list.go b/template_list.go index 0d43f1c6..4f08e252 100644 --- a/template_list.go +++ b/template_list.go @@ -601,8 +601,7 @@ var profile_21 = []byte(`

Comments

-
- `) +
`) var profile_comments_row_0 = []byte(`
diff --git a/templates/profile.html b/templates/profile.html index bf84108a..0648f223 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -69,18 +69,16 @@

Comments

-
- {{template "profile_comments_row.html" . }} -
+
{{template "profile_comments_row.html" . }}
{{if not .CurrentUser.IsBanned}}
-
+
-
+
diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index c00acdc7..b57df627 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -479,6 +479,9 @@ select, input, textarea, button { width: 100%; height: min-content; } +.topic_reply_form .formrow { + padding: 0px !important; +} .topic_reply_form .trumbowyg-button-pane:after { display: none; } @@ -935,6 +938,9 @@ select, input, textarea, button { #profile_comments { margin-bottom: 12px; } +#profile_comments:empty { + display: none !important; +} #profile_comments .rowitem { background-image: none !important; } diff --git a/themes/cosora/public/misc.js b/themes/cosora/public/misc.js index 759806b8..157fc4e8 100644 --- a/themes/cosora/public/misc.js +++ b/themes/cosora/public/misc.js @@ -25,6 +25,10 @@ $(document).ready(function(){ btns: btnlist, autogrow: true, }); + $('#profile_comments_form .topic_reply_form .input_content').trumbowyg({ + btns: [['viewHTML'],['strong','em','del'],['link'],['insertImage'],['removeformat']], + autogrow: true, + }); // TODO: Refactor this to use `each` less $('.button_menu').click(function(){