Added per-forum analytics.
Added one week as a time range option. Moved routeForum to routes.ViewForum Renamed form_vars to formVars. Moved the analytics chart JS into it's own file. Changed the header text on the requests chart from Views to Requests. Added simple hooks to global.js Moved some of the in funcname prints to debug mode. De-duplicated some of the back-end analytics logic.
This commit is contained in:
parent
60964868d4
commit
0879f56893
|
@ -8,7 +8,7 @@ import (
|
|||
"../../query_gen/lib"
|
||||
)
|
||||
|
||||
// TODO: The forum view counter
|
||||
var ForumViewCounter *DefaultForumViewCounter
|
||||
|
||||
// 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
|
||||
|
@ -29,7 +29,7 @@ func NewDefaultForumViewCounter() (*DefaultForumViewCounter, error) {
|
|||
insert: acc.Insert("viewchunks_forums").Columns("count, createdAt, forum").Fields("?,UTC_TIMESTAMP(),?").Prepare(),
|
||||
}
|
||||
common.AddScheduledFifteenMinuteTask(counter.Tick) // There could be a lot of routes, so we don't want to be running this every second
|
||||
//AddScheduledSecondTask(counter.Tick)
|
||||
//common.AddScheduledSecondTask(counter.Tick)
|
||||
common.AddShutdownTask(counter.Tick)
|
||||
return counter, acc.FirstError()
|
||||
}
|
||||
|
@ -83,4 +83,36 @@ func (counter *DefaultForumViewCounter) insertChunk(count int, forum int) error
|
|||
return err
|
||||
}
|
||||
|
||||
func (counter *DefaultForumViewCounter) Bump(forumID int) {
|
||||
// Is the ID even?
|
||||
if forumID%2 == 0 {
|
||||
counter.evenLock.RLock()
|
||||
forum, ok := counter.evenMap[forumID]
|
||||
counter.evenLock.RUnlock()
|
||||
if ok {
|
||||
forum.Lock()
|
||||
forum.counter++
|
||||
forum.Unlock()
|
||||
} else {
|
||||
counter.evenLock.Lock()
|
||||
counter.evenMap[forumID] = &RWMutexCounterBucket{counter: 1}
|
||||
counter.evenLock.Unlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
counter.oddLock.RLock()
|
||||
forum, ok := counter.oddMap[forumID]
|
||||
counter.oddLock.RUnlock()
|
||||
if ok {
|
||||
forum.Lock()
|
||||
forum.counter++
|
||||
forum.Unlock()
|
||||
} else {
|
||||
counter.oddLock.Lock()
|
||||
counter.oddMap[forumID] = &RWMutexCounterBucket{counter: 1}
|
||||
counter.oddLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add a forum counter backed by two maps which grow as forums are created but never shrinks
|
||||
|
|
|
@ -23,7 +23,6 @@ type Stmts struct {
|
|||
getActivityEntry *sql.Stmt
|
||||
forumEntryExists *sql.Stmt
|
||||
groupEntryExists *sql.Stmt
|
||||
getForumTopicsOffset *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
getProfileReplies *sql.Stmt
|
||||
|
@ -164,13 +163,6 @@ func _gen_mssql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getForumTopicsOffset statement.")
|
||||
stmts.getForumTopicsOffset, err = db.Prepare("SELECT [tid],[title],[content],[createdBy],[is_closed],[sticky],[createdAt],[lastReplyAt],[lastReplyBy],[parentID],[postCount],[likeCount] FROM [topics] WHERE [parentID] = ?1 ORDER BY sticky DESC,lastReplyAt DESC,createdBy DESC OFFSET ?2 ROWS FETCH NEXT ?3 ROWS ONLY")
|
||||
if err != nil {
|
||||
log.Print("Bad Query: ","SELECT [tid],[title],[content],[createdBy],[is_closed],[sticky],[createdAt],[lastReplyAt],[lastReplyBy],[parentID],[postCount],[likeCount] FROM [topics] WHERE [parentID] = ?1 ORDER BY sticky DESC,lastReplyAt DESC,createdBy DESC OFFSET ?2 ROWS FETCH NEXT ?3 ROWS ONLY")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getAttachment statement.")
|
||||
stmts.getAttachment, err = db.Prepare("SELECT [sectionID],[sectionTable],[originID],[originTable],[uploadedBy],[path] FROM [attachments] WHERE [path] = ?1 AND [sectionID] = ?2 AND [sectionTable] = ?3")
|
||||
if err != nil {
|
||||
|
|
|
@ -25,7 +25,6 @@ type Stmts struct {
|
|||
getActivityEntry *sql.Stmt
|
||||
forumEntryExists *sql.Stmt
|
||||
groupEntryExists *sql.Stmt
|
||||
getForumTopicsOffset *sql.Stmt
|
||||
getAttachment *sql.Stmt
|
||||
getForumTopics *sql.Stmt
|
||||
getProfileReplies *sql.Stmt
|
||||
|
@ -152,12 +151,6 @@ func _gen_mysql() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getForumTopicsOffset statement.")
|
||||
stmts.getForumTopicsOffset, err = db.Prepare("SELECT `tid`,`title`,`content`,`createdBy`,`is_closed`,`sticky`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`parentID`,`postCount`,`likeCount` FROM `topics` WHERE `parentID` = ? ORDER BY sticky DESC,lastReplyAt DESC,createdBy DESC LIMIT ?,?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Preparing getAttachment statement.")
|
||||
stmts.getAttachment, err = db.Prepare("SELECT `sectionID`,`sectionTable`,`originID`,`originTable`,`uploadedBy`,`path` FROM `attachments` WHERE `path` = ? AND `sectionID` = ? AND `sectionTable` = ?")
|
||||
if err != nil {
|
||||
|
|
358
gen_router.go
358
gen_router.go
|
@ -21,7 +21,7 @@ var RouteMap = map[string]interface{}{
|
|||
"routes.Overview": routes.Overview,
|
||||
"routes.CustomPage": routes.CustomPage,
|
||||
"routeForums": routeForums,
|
||||
"routeForum": routeForum,
|
||||
"routes.ViewForum": routes.ViewForum,
|
||||
"routeChangeTheme": routeChangeTheme,
|
||||
"routeShowAttachment": routeShowAttachment,
|
||||
"routeWebsockets": routeWebsockets,
|
||||
|
@ -61,10 +61,12 @@ var RouteMap = map[string]interface{}{
|
|||
"routePanelAnalyticsReferrers": routePanelAnalyticsReferrers,
|
||||
"routePanelAnalyticsRouteViews": routePanelAnalyticsRouteViews,
|
||||
"routePanelAnalyticsAgentViews": routePanelAnalyticsAgentViews,
|
||||
"routePanelAnalyticsForumViews": routePanelAnalyticsForumViews,
|
||||
"routePanelAnalyticsSystemViews": routePanelAnalyticsSystemViews,
|
||||
"routePanelAnalyticsReferrerViews": routePanelAnalyticsReferrerViews,
|
||||
"routePanelAnalyticsPosts": routePanelAnalyticsPosts,
|
||||
"routePanelAnalyticsTopics": routePanelAnalyticsTopics,
|
||||
"routePanelAnalyticsForums": routePanelAnalyticsForums,
|
||||
"routePanelGroups": routePanelGroups,
|
||||
"routePanelGroupsEdit": routePanelGroupsEdit,
|
||||
"routePanelGroupsEditPerms": routePanelGroupsEditPerms,
|
||||
|
@ -124,7 +126,7 @@ var routeMapEnum = map[string]int{
|
|||
"routes.Overview": 1,
|
||||
"routes.CustomPage": 2,
|
||||
"routeForums": 3,
|
||||
"routeForum": 4,
|
||||
"routes.ViewForum": 4,
|
||||
"routeChangeTheme": 5,
|
||||
"routeShowAttachment": 6,
|
||||
"routeWebsockets": 7,
|
||||
|
@ -164,68 +166,70 @@ var routeMapEnum = map[string]int{
|
|||
"routePanelAnalyticsReferrers": 41,
|
||||
"routePanelAnalyticsRouteViews": 42,
|
||||
"routePanelAnalyticsAgentViews": 43,
|
||||
"routePanelAnalyticsSystemViews": 44,
|
||||
"routePanelAnalyticsReferrerViews": 45,
|
||||
"routePanelAnalyticsPosts": 46,
|
||||
"routePanelAnalyticsTopics": 47,
|
||||
"routePanelGroups": 48,
|
||||
"routePanelGroupsEdit": 49,
|
||||
"routePanelGroupsEditPerms": 50,
|
||||
"routePanelGroupsEditSubmit": 51,
|
||||
"routePanelGroupsEditPermsSubmit": 52,
|
||||
"routePanelGroupsCreateSubmit": 53,
|
||||
"routePanelBackups": 54,
|
||||
"routePanelLogsMod": 55,
|
||||
"routePanelDebug": 56,
|
||||
"routePanelDashboard": 57,
|
||||
"routes.AccountEditCritical": 58,
|
||||
"routeAccountEditCriticalSubmit": 59,
|
||||
"routeAccountEditAvatar": 60,
|
||||
"routeAccountEditAvatarSubmit": 61,
|
||||
"routeAccountEditUsername": 62,
|
||||
"routeAccountEditUsernameSubmit": 63,
|
||||
"routeAccountEditEmail": 64,
|
||||
"routeAccountEditEmailTokenSubmit": 65,
|
||||
"routeProfile": 66,
|
||||
"routes.BanUserSubmit": 67,
|
||||
"routes.UnbanUser": 68,
|
||||
"routes.ActivateUser": 69,
|
||||
"routes.IPSearch": 70,
|
||||
"routes.CreateTopicSubmit": 71,
|
||||
"routes.EditTopicSubmit": 72,
|
||||
"routes.DeleteTopicSubmit": 73,
|
||||
"routes.StickTopicSubmit": 74,
|
||||
"routes.UnstickTopicSubmit": 75,
|
||||
"routes.LockTopicSubmit": 76,
|
||||
"routes.UnlockTopicSubmit": 77,
|
||||
"routes.MoveTopicSubmit": 78,
|
||||
"routeLikeTopicSubmit": 79,
|
||||
"routes.ViewTopic": 80,
|
||||
"routeCreateReplySubmit": 81,
|
||||
"routes.ReplyEditSubmit": 82,
|
||||
"routes.ReplyDeleteSubmit": 83,
|
||||
"routeReplyLikeSubmit": 84,
|
||||
"routeProfileReplyCreateSubmit": 85,
|
||||
"routes.ProfileReplyEditSubmit": 86,
|
||||
"routes.ProfileReplyDeleteSubmit": 87,
|
||||
"routes.PollVote": 88,
|
||||
"routes.PollResults": 89,
|
||||
"routes.AccountLogin": 90,
|
||||
"routes.AccountRegister": 91,
|
||||
"routeLogout": 92,
|
||||
"routes.AccountLoginSubmit": 93,
|
||||
"routes.AccountRegisterSubmit": 94,
|
||||
"routeDynamic": 95,
|
||||
"routeUploads": 96,
|
||||
"routes.StaticFile": 97,
|
||||
"BadRoute": 98,
|
||||
"routePanelAnalyticsForumViews": 44,
|
||||
"routePanelAnalyticsSystemViews": 45,
|
||||
"routePanelAnalyticsReferrerViews": 46,
|
||||
"routePanelAnalyticsPosts": 47,
|
||||
"routePanelAnalyticsTopics": 48,
|
||||
"routePanelAnalyticsForums": 49,
|
||||
"routePanelGroups": 50,
|
||||
"routePanelGroupsEdit": 51,
|
||||
"routePanelGroupsEditPerms": 52,
|
||||
"routePanelGroupsEditSubmit": 53,
|
||||
"routePanelGroupsEditPermsSubmit": 54,
|
||||
"routePanelGroupsCreateSubmit": 55,
|
||||
"routePanelBackups": 56,
|
||||
"routePanelLogsMod": 57,
|
||||
"routePanelDebug": 58,
|
||||
"routePanelDashboard": 59,
|
||||
"routes.AccountEditCritical": 60,
|
||||
"routeAccountEditCriticalSubmit": 61,
|
||||
"routeAccountEditAvatar": 62,
|
||||
"routeAccountEditAvatarSubmit": 63,
|
||||
"routeAccountEditUsername": 64,
|
||||
"routeAccountEditUsernameSubmit": 65,
|
||||
"routeAccountEditEmail": 66,
|
||||
"routeAccountEditEmailTokenSubmit": 67,
|
||||
"routeProfile": 68,
|
||||
"routes.BanUserSubmit": 69,
|
||||
"routes.UnbanUser": 70,
|
||||
"routes.ActivateUser": 71,
|
||||
"routes.IPSearch": 72,
|
||||
"routes.CreateTopicSubmit": 73,
|
||||
"routes.EditTopicSubmit": 74,
|
||||
"routes.DeleteTopicSubmit": 75,
|
||||
"routes.StickTopicSubmit": 76,
|
||||
"routes.UnstickTopicSubmit": 77,
|
||||
"routes.LockTopicSubmit": 78,
|
||||
"routes.UnlockTopicSubmit": 79,
|
||||
"routes.MoveTopicSubmit": 80,
|
||||
"routeLikeTopicSubmit": 81,
|
||||
"routes.ViewTopic": 82,
|
||||
"routeCreateReplySubmit": 83,
|
||||
"routes.ReplyEditSubmit": 84,
|
||||
"routes.ReplyDeleteSubmit": 85,
|
||||
"routeReplyLikeSubmit": 86,
|
||||
"routeProfileReplyCreateSubmit": 87,
|
||||
"routes.ProfileReplyEditSubmit": 88,
|
||||
"routes.ProfileReplyDeleteSubmit": 89,
|
||||
"routes.PollVote": 90,
|
||||
"routes.PollResults": 91,
|
||||
"routes.AccountLogin": 92,
|
||||
"routes.AccountRegister": 93,
|
||||
"routeLogout": 94,
|
||||
"routes.AccountLoginSubmit": 95,
|
||||
"routes.AccountRegisterSubmit": 96,
|
||||
"routeDynamic": 97,
|
||||
"routeUploads": 98,
|
||||
"routes.StaticFile": 99,
|
||||
"BadRoute": 100,
|
||||
}
|
||||
var reverseRouteMapEnum = map[int]string{
|
||||
0: "routeAPI",
|
||||
1: "routes.Overview",
|
||||
2: "routes.CustomPage",
|
||||
3: "routeForums",
|
||||
4: "routeForum",
|
||||
4: "routes.ViewForum",
|
||||
5: "routeChangeTheme",
|
||||
6: "routeShowAttachment",
|
||||
7: "routeWebsockets",
|
||||
|
@ -265,61 +269,63 @@ var reverseRouteMapEnum = map[int]string{
|
|||
41: "routePanelAnalyticsReferrers",
|
||||
42: "routePanelAnalyticsRouteViews",
|
||||
43: "routePanelAnalyticsAgentViews",
|
||||
44: "routePanelAnalyticsSystemViews",
|
||||
45: "routePanelAnalyticsReferrerViews",
|
||||
46: "routePanelAnalyticsPosts",
|
||||
47: "routePanelAnalyticsTopics",
|
||||
48: "routePanelGroups",
|
||||
49: "routePanelGroupsEdit",
|
||||
50: "routePanelGroupsEditPerms",
|
||||
51: "routePanelGroupsEditSubmit",
|
||||
52: "routePanelGroupsEditPermsSubmit",
|
||||
53: "routePanelGroupsCreateSubmit",
|
||||
54: "routePanelBackups",
|
||||
55: "routePanelLogsMod",
|
||||
56: "routePanelDebug",
|
||||
57: "routePanelDashboard",
|
||||
58: "routes.AccountEditCritical",
|
||||
59: "routeAccountEditCriticalSubmit",
|
||||
60: "routeAccountEditAvatar",
|
||||
61: "routeAccountEditAvatarSubmit",
|
||||
62: "routeAccountEditUsername",
|
||||
63: "routeAccountEditUsernameSubmit",
|
||||
64: "routeAccountEditEmail",
|
||||
65: "routeAccountEditEmailTokenSubmit",
|
||||
66: "routeProfile",
|
||||
67: "routes.BanUserSubmit",
|
||||
68: "routes.UnbanUser",
|
||||
69: "routes.ActivateUser",
|
||||
70: "routes.IPSearch",
|
||||
71: "routes.CreateTopicSubmit",
|
||||
72: "routes.EditTopicSubmit",
|
||||
73: "routes.DeleteTopicSubmit",
|
||||
74: "routes.StickTopicSubmit",
|
||||
75: "routes.UnstickTopicSubmit",
|
||||
76: "routes.LockTopicSubmit",
|
||||
77: "routes.UnlockTopicSubmit",
|
||||
78: "routes.MoveTopicSubmit",
|
||||
79: "routeLikeTopicSubmit",
|
||||
80: "routes.ViewTopic",
|
||||
81: "routeCreateReplySubmit",
|
||||
82: "routes.ReplyEditSubmit",
|
||||
83: "routes.ReplyDeleteSubmit",
|
||||
84: "routeReplyLikeSubmit",
|
||||
85: "routeProfileReplyCreateSubmit",
|
||||
86: "routes.ProfileReplyEditSubmit",
|
||||
87: "routes.ProfileReplyDeleteSubmit",
|
||||
88: "routes.PollVote",
|
||||
89: "routes.PollResults",
|
||||
90: "routes.AccountLogin",
|
||||
91: "routes.AccountRegister",
|
||||
92: "routeLogout",
|
||||
93: "routes.AccountLoginSubmit",
|
||||
94: "routes.AccountRegisterSubmit",
|
||||
95: "routeDynamic",
|
||||
96: "routeUploads",
|
||||
97: "routes.StaticFile",
|
||||
98: "BadRoute",
|
||||
44: "routePanelAnalyticsForumViews",
|
||||
45: "routePanelAnalyticsSystemViews",
|
||||
46: "routePanelAnalyticsReferrerViews",
|
||||
47: "routePanelAnalyticsPosts",
|
||||
48: "routePanelAnalyticsTopics",
|
||||
49: "routePanelAnalyticsForums",
|
||||
50: "routePanelGroups",
|
||||
51: "routePanelGroupsEdit",
|
||||
52: "routePanelGroupsEditPerms",
|
||||
53: "routePanelGroupsEditSubmit",
|
||||
54: "routePanelGroupsEditPermsSubmit",
|
||||
55: "routePanelGroupsCreateSubmit",
|
||||
56: "routePanelBackups",
|
||||
57: "routePanelLogsMod",
|
||||
58: "routePanelDebug",
|
||||
59: "routePanelDashboard",
|
||||
60: "routes.AccountEditCritical",
|
||||
61: "routeAccountEditCriticalSubmit",
|
||||
62: "routeAccountEditAvatar",
|
||||
63: "routeAccountEditAvatarSubmit",
|
||||
64: "routeAccountEditUsername",
|
||||
65: "routeAccountEditUsernameSubmit",
|
||||
66: "routeAccountEditEmail",
|
||||
67: "routeAccountEditEmailTokenSubmit",
|
||||
68: "routeProfile",
|
||||
69: "routes.BanUserSubmit",
|
||||
70: "routes.UnbanUser",
|
||||
71: "routes.ActivateUser",
|
||||
72: "routes.IPSearch",
|
||||
73: "routes.CreateTopicSubmit",
|
||||
74: "routes.EditTopicSubmit",
|
||||
75: "routes.DeleteTopicSubmit",
|
||||
76: "routes.StickTopicSubmit",
|
||||
77: "routes.UnstickTopicSubmit",
|
||||
78: "routes.LockTopicSubmit",
|
||||
79: "routes.UnlockTopicSubmit",
|
||||
80: "routes.MoveTopicSubmit",
|
||||
81: "routeLikeTopicSubmit",
|
||||
82: "routes.ViewTopic",
|
||||
83: "routeCreateReplySubmit",
|
||||
84: "routes.ReplyEditSubmit",
|
||||
85: "routes.ReplyDeleteSubmit",
|
||||
86: "routeReplyLikeSubmit",
|
||||
87: "routeProfileReplyCreateSubmit",
|
||||
88: "routes.ProfileReplyEditSubmit",
|
||||
89: "routes.ProfileReplyDeleteSubmit",
|
||||
90: "routes.PollVote",
|
||||
91: "routes.PollResults",
|
||||
92: "routes.AccountLogin",
|
||||
93: "routes.AccountRegister",
|
||||
94: "routeLogout",
|
||||
95: "routes.AccountLoginSubmit",
|
||||
96: "routes.AccountRegisterSubmit",
|
||||
97: "routeDynamic",
|
||||
98: "routeUploads",
|
||||
99: "routes.StaticFile",
|
||||
100: "BadRoute",
|
||||
}
|
||||
var osMapEnum = map[string]int{
|
||||
"unknown": 0,
|
||||
|
@ -571,7 +577,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
counters.GlobalViewCounter.Bump()
|
||||
|
||||
if prefix == "/static" {
|
||||
counters.RouteViewCounter.Bump(97)
|
||||
counters.RouteViewCounter.Bump(99)
|
||||
req.URL.Path += extraData
|
||||
routes.StaticFile(w, req)
|
||||
return
|
||||
|
@ -745,7 +751,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
case "/forum":
|
||||
counters.RouteViewCounter.Bump(4)
|
||||
err = routeForum(w,req,user,extraData)
|
||||
err = routes.ViewForum(w,req,user,extraData)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
|
@ -1052,11 +1058,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
case "/panel/analytics/agent/":
|
||||
counters.RouteViewCounter.Bump(43)
|
||||
err = routePanelAnalyticsAgentViews(w,req,user,extraData)
|
||||
case "/panel/analytics/system/":
|
||||
case "/panel/analytics/forum/":
|
||||
counters.RouteViewCounter.Bump(44)
|
||||
err = routePanelAnalyticsForumViews(w,req,user,extraData)
|
||||
case "/panel/analytics/system/":
|
||||
counters.RouteViewCounter.Bump(45)
|
||||
err = routePanelAnalyticsSystemViews(w,req,user,extraData)
|
||||
case "/panel/analytics/referrer/":
|
||||
counters.RouteViewCounter.Bump(45)
|
||||
counters.RouteViewCounter.Bump(46)
|
||||
err = routePanelAnalyticsReferrerViews(w,req,user,extraData)
|
||||
case "/panel/analytics/posts/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1065,7 +1074,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(46)
|
||||
counters.RouteViewCounter.Bump(47)
|
||||
err = routePanelAnalyticsPosts(w,req,user)
|
||||
case "/panel/analytics/topics/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1074,16 +1083,25 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(47)
|
||||
err = routePanelAnalyticsTopics(w,req,user)
|
||||
case "/panel/groups/":
|
||||
counters.RouteViewCounter.Bump(48)
|
||||
err = routePanelAnalyticsTopics(w,req,user)
|
||||
case "/panel/analytics/forums/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(49)
|
||||
err = routePanelAnalyticsForums(w,req,user)
|
||||
case "/panel/groups/":
|
||||
counters.RouteViewCounter.Bump(50)
|
||||
err = routePanelGroups(w,req,user)
|
||||
case "/panel/groups/edit/":
|
||||
counters.RouteViewCounter.Bump(49)
|
||||
counters.RouteViewCounter.Bump(51)
|
||||
err = routePanelGroupsEdit(w,req,user,extraData)
|
||||
case "/panel/groups/edit/perms/":
|
||||
counters.RouteViewCounter.Bump(50)
|
||||
counters.RouteViewCounter.Bump(52)
|
||||
err = routePanelGroupsEditPerms(w,req,user,extraData)
|
||||
case "/panel/groups/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1092,7 +1110,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(51)
|
||||
counters.RouteViewCounter.Bump(53)
|
||||
err = routePanelGroupsEditSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/edit/perms/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1101,7 +1119,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(52)
|
||||
counters.RouteViewCounter.Bump(54)
|
||||
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/create/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1110,7 +1128,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(53)
|
||||
counters.RouteViewCounter.Bump(55)
|
||||
err = routePanelGroupsCreateSubmit(w,req,user)
|
||||
case "/panel/backups/":
|
||||
err = common.SuperAdminOnly(w,req,user)
|
||||
|
@ -1119,10 +1137,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(54)
|
||||
counters.RouteViewCounter.Bump(56)
|
||||
err = routePanelBackups(w,req,user,extraData)
|
||||
case "/panel/logs/mod/":
|
||||
counters.RouteViewCounter.Bump(55)
|
||||
counters.RouteViewCounter.Bump(57)
|
||||
err = routePanelLogsMod(w,req,user)
|
||||
case "/panel/debug/":
|
||||
err = common.AdminOnly(w,req,user)
|
||||
|
@ -1131,10 +1149,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(56)
|
||||
counters.RouteViewCounter.Bump(58)
|
||||
err = routePanelDebug(w,req,user)
|
||||
default:
|
||||
counters.RouteViewCounter.Bump(57)
|
||||
counters.RouteViewCounter.Bump(59)
|
||||
err = routePanelDashboard(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1149,7 +1167,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(58)
|
||||
counters.RouteViewCounter.Bump(60)
|
||||
err = routes.AccountEditCritical(w,req,user)
|
||||
case "/user/edit/critical/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1164,7 +1182,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(59)
|
||||
counters.RouteViewCounter.Bump(61)
|
||||
err = routeAccountEditCriticalSubmit(w,req,user)
|
||||
case "/user/edit/avatar/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
|
@ -1173,7 +1191,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(60)
|
||||
counters.RouteViewCounter.Bump(62)
|
||||
err = routeAccountEditAvatar(w,req,user)
|
||||
case "/user/edit/avatar/submit/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
|
@ -1193,7 +1211,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(61)
|
||||
counters.RouteViewCounter.Bump(63)
|
||||
err = routeAccountEditAvatarSubmit(w,req,user)
|
||||
case "/user/edit/username/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
|
@ -1202,7 +1220,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(62)
|
||||
counters.RouteViewCounter.Bump(64)
|
||||
err = routeAccountEditUsername(w,req,user)
|
||||
case "/user/edit/username/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1217,7 +1235,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(63)
|
||||
counters.RouteViewCounter.Bump(65)
|
||||
err = routeAccountEditUsernameSubmit(w,req,user)
|
||||
case "/user/edit/email/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
|
@ -1226,7 +1244,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(64)
|
||||
counters.RouteViewCounter.Bump(66)
|
||||
err = routeAccountEditEmail(w,req,user)
|
||||
case "/user/edit/token/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1241,11 +1259,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(65)
|
||||
counters.RouteViewCounter.Bump(67)
|
||||
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
|
||||
default:
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(66)
|
||||
counters.RouteViewCounter.Bump(68)
|
||||
err = routeProfile(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1266,7 +1284,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(67)
|
||||
counters.RouteViewCounter.Bump(69)
|
||||
err = routes.BanUserSubmit(w,req,user,extraData)
|
||||
case "/users/unban/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1281,7 +1299,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(68)
|
||||
counters.RouteViewCounter.Bump(70)
|
||||
err = routes.UnbanUser(w,req,user,extraData)
|
||||
case "/users/activate/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1296,7 +1314,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(69)
|
||||
counters.RouteViewCounter.Bump(71)
|
||||
err = routes.ActivateUser(w,req,user,extraData)
|
||||
case "/users/ips/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
|
@ -1305,7 +1323,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(70)
|
||||
counters.RouteViewCounter.Bump(72)
|
||||
err = routes.IPSearch(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1331,7 +1349,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(71)
|
||||
counters.RouteViewCounter.Bump(73)
|
||||
err = routes.CreateTopicSubmit(w,req,user)
|
||||
case "/topic/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1346,7 +1364,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(72)
|
||||
counters.RouteViewCounter.Bump(74)
|
||||
err = routes.EditTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1362,7 +1380,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(73)
|
||||
counters.RouteViewCounter.Bump(75)
|
||||
err = routes.DeleteTopicSubmit(w,req,user)
|
||||
case "/topic/stick/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1377,7 +1395,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(74)
|
||||
counters.RouteViewCounter.Bump(76)
|
||||
err = routes.StickTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/unstick/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1392,7 +1410,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(75)
|
||||
counters.RouteViewCounter.Bump(77)
|
||||
err = routes.UnstickTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/lock/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1408,7 +1426,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(76)
|
||||
counters.RouteViewCounter.Bump(78)
|
||||
err = routes.LockTopicSubmit(w,req,user)
|
||||
case "/topic/unlock/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1423,7 +1441,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(77)
|
||||
counters.RouteViewCounter.Bump(79)
|
||||
err = routes.UnlockTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/move/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1438,7 +1456,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(78)
|
||||
counters.RouteViewCounter.Bump(80)
|
||||
err = routes.MoveTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/like/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1453,10 +1471,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(79)
|
||||
counters.RouteViewCounter.Bump(81)
|
||||
err = routeLikeTopicSubmit(w,req,user,extraData)
|
||||
default:
|
||||
counters.RouteViewCounter.Bump(80)
|
||||
counters.RouteViewCounter.Bump(82)
|
||||
err = routes.ViewTopic(w,req,user, extraData)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1482,7 +1500,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(81)
|
||||
counters.RouteViewCounter.Bump(83)
|
||||
err = routeCreateReplySubmit(w,req,user)
|
||||
case "/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1497,7 +1515,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(82)
|
||||
counters.RouteViewCounter.Bump(84)
|
||||
err = routes.ReplyEditSubmit(w,req,user,extraData)
|
||||
case "/reply/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1512,7 +1530,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(83)
|
||||
counters.RouteViewCounter.Bump(85)
|
||||
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
|
||||
case "/reply/like/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1527,7 +1545,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(84)
|
||||
counters.RouteViewCounter.Bump(86)
|
||||
err = routeReplyLikeSubmit(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1548,7 +1566,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(85)
|
||||
counters.RouteViewCounter.Bump(87)
|
||||
err = routeProfileReplyCreateSubmit(w,req,user)
|
||||
case "/profile/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1563,7 +1581,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(86)
|
||||
counters.RouteViewCounter.Bump(88)
|
||||
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
|
||||
case "/profile/reply/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1578,7 +1596,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(87)
|
||||
counters.RouteViewCounter.Bump(89)
|
||||
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1599,10 +1617,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(88)
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
err = routes.PollVote(w,req,user,extraData)
|
||||
case "/poll/results/":
|
||||
counters.RouteViewCounter.Bump(89)
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
err = routes.PollResults(w,req,user,extraData)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1611,10 +1629,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
case "/accounts":
|
||||
switch(req.URL.Path) {
|
||||
case "/accounts/login/":
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
counters.RouteViewCounter.Bump(92)
|
||||
err = routes.AccountLogin(w,req,user)
|
||||
case "/accounts/create/":
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
counters.RouteViewCounter.Bump(93)
|
||||
err = routes.AccountRegister(w,req,user)
|
||||
case "/accounts/logout/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1629,7 +1647,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(92)
|
||||
counters.RouteViewCounter.Bump(94)
|
||||
err = routeLogout(w,req,user)
|
||||
case "/accounts/login/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1638,7 +1656,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(93)
|
||||
counters.RouteViewCounter.Bump(95)
|
||||
err = routes.AccountLoginSubmit(w,req,user)
|
||||
case "/accounts/create/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1647,7 +1665,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(94)
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
err = routes.AccountRegisterSubmit(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -1664,7 +1682,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
common.NotFound(w,req,nil)
|
||||
return
|
||||
}
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
counters.RouteViewCounter.Bump(98)
|
||||
req.URL.Path += extraData
|
||||
// TODO: Find a way to propagate errors up from this?
|
||||
router.UploadHandler(w,req) // TODO: Count these views
|
||||
|
@ -1707,7 +1725,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
router.RUnlock()
|
||||
|
||||
if ok {
|
||||
counters.RouteViewCounter.Bump(95) // TODO: Be more specific about *which* dynamic route it is
|
||||
counters.RouteViewCounter.Bump(97) // TODO: Be more specific about *which* dynamic route it is
|
||||
req.URL.Path += extraData
|
||||
err = handle(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -1721,7 +1739,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
if strings.Contains(lowerPath,"admin") || strings.Contains(lowerPath,"sql") || strings.Contains(lowerPath,"manage") || strings.Contains(lowerPath,"//") || strings.Contains(lowerPath,"\\\\") || strings.Contains(lowerPath,"wp") || strings.Contains(lowerPath,"wordpress") || strings.Contains(lowerPath,"config") || strings.Contains(lowerPath,"setup") || strings.Contains(lowerPath,"install") || strings.Contains(lowerPath,"update") || strings.Contains(lowerPath,"php") {
|
||||
router.SuspiciousRequest(req)
|
||||
}
|
||||
counters.RouteViewCounter.Bump(98)
|
||||
counters.RouteViewCounter.Bump(100)
|
||||
common.NotFound(w,req,nil)
|
||||
}
|
||||
}
|
||||
|
|
4
main.go
4
main.go
|
@ -130,6 +130,10 @@ func afterDBInit() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
counters.ForumViewCounter, err = counters.NewDefaultForumViewCounter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
counters.ReferrerTracker, err = counters.NewDefaultReferrerTracker()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
286
panel_routes.go
286
panel_routes.go
|
@ -575,6 +575,12 @@ func panelAnalyticsTimeRange(rawTimeRange string) (timeRange AnalyticsTimeRange,
|
|||
timeRange.Slices = 30
|
||||
timeRange.SliceWidth = 60 * 60 * 24
|
||||
timeRange.Range = "one-month"
|
||||
case "one-week":
|
||||
timeRange.Quantity = 7
|
||||
timeRange.Unit = "day"
|
||||
timeRange.Slices = 14
|
||||
timeRange.SliceWidth = 60 * 60 * 12
|
||||
timeRange.Range = "one-week"
|
||||
case "two-days": // Two days is experimental
|
||||
timeRange.Quantity = 2
|
||||
timeRange.Unit = "day"
|
||||
|
@ -599,24 +605,9 @@ func panelAnalyticsTimeRange(rawTimeRange string) (timeRange AnalyticsTimeRange,
|
|||
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 {
|
||||
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)
|
||||
func panelAnalyticsTimeRangeToLabelList(timeRange AnalyticsTimeRange) (revLabelList []int64, labelList []int64, viewMap map[int64]int64) {
|
||||
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)
|
||||
|
@ -625,9 +616,26 @@ func routePanelAnalyticsViews(w http.ResponseWriter, r *http.Request, user commo
|
|||
for _, value := range revLabelList {
|
||||
labelList = append(labelList, value)
|
||||
}
|
||||
return revLabelList, labelList, viewMap
|
||||
}
|
||||
|
||||
func routePanelAnalyticsViews(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")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.js")
|
||||
|
||||
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsViews")
|
||||
common.DebugLog("in routePanelAnalyticsViews")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks").Columns("count, createdAt").Where("route = ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
|
@ -679,28 +687,16 @@ func routePanelAnalyticsRouteViews(w http.ResponseWriter, r *http.Request, user
|
|||
}
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.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)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsRouteViews")
|
||||
common.DebugLog("in routePanelAnalyticsRouteViews")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Validate the route is valid
|
||||
|
@ -753,28 +749,16 @@ func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user
|
|||
}
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.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)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsAgentViews")
|
||||
common.DebugLog("in routePanelAnalyticsAgentViews")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
|
@ -829,6 +813,79 @@ func routePanelAnalyticsAgentViews(w http.ResponseWriter, r *http.Request, user
|
|||
return panelRenderTemplate("panel_analytics_agent_views", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsForumViews(w http.ResponseWriter, r *http.Request, user common.User, sfid 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")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.js")
|
||||
|
||||
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
common.DebugLog("in routePanelAnalyticsForumViews")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
rows, err := acc.Select("viewchunks_forums").Columns("count, createdAt").Where("forum = ?").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query(sfid)
|
||||
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)
|
||||
}
|
||||
|
||||
var unixCreatedAt = createdAt.Unix()
|
||||
// TODO: Bulk log this
|
||||
if common.Dev.SuperDebug {
|
||||
log.Print("count: ", count)
|
||||
log.Print("createdAt: ", createdAt)
|
||||
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)
|
||||
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", sfid, forum.Name, graph, timeRange.Range}
|
||||
return panelRenderTemplate("panel_analytics_forum_views", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user common.User, system string) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
@ -836,28 +893,16 @@ func routePanelAnalyticsSystemViews(w http.ResponseWriter, r *http.Request, user
|
|||
}
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.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)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsSystemViews")
|
||||
common.DebugLog("in routePanelAnalyticsSystemViews")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
|
@ -918,28 +963,16 @@ func routePanelAnalyticsReferrerViews(w http.ResponseWriter, r *http.Request, us
|
|||
}
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.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)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsReferrerViews")
|
||||
common.DebugLog("in routePanelAnalyticsReferrerViews")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
// TODO: Verify the agent is valid
|
||||
|
@ -994,28 +1027,16 @@ func routePanelAnalyticsTopics(w http.ResponseWriter, r *http.Request, user comm
|
|||
}
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.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)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsTopics")
|
||||
common.DebugLog("in routePanelAnalyticsTopics")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("topicchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
|
@ -1071,28 +1092,16 @@ func routePanelAnalyticsPosts(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")
|
||||
headerVars.Scripts = append(headerVars.Scripts, "analytics.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)
|
||||
}
|
||||
revLabelList, labelList, viewMap := panelAnalyticsTimeRangeToLabelList(timeRange)
|
||||
|
||||
var viewList []int64
|
||||
log.Print("in routePanelAnalyticsPosts")
|
||||
common.DebugLog("in routePanelAnalyticsPosts")
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query()
|
||||
|
@ -1140,6 +1149,67 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo
|
|||
return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsForums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
var forumMap = make(map[string]int)
|
||||
|
||||
timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange"))
|
||||
if err != nil {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
rows, err := acc.Select("viewchunks_forums").Columns("count, forum").Where("forum != ''").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 int
|
||||
var forum string
|
||||
err := rows.Scan(&count, &forum)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Bulk log this
|
||||
if common.Dev.SuperDebug {
|
||||
log.Print("count: ", count)
|
||||
log.Print("forum: ", forum)
|
||||
}
|
||||
forumMap[forum] += count
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Sort this slice
|
||||
var forumItems []common.PanelAnalyticsAgentsItem
|
||||
for sfid, count := range forumMap {
|
||||
fid, err := strconv.Atoi(sfid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
forumItems = append(forumItems, common.PanelAnalyticsAgentsItem{
|
||||
Agent: sfid,
|
||||
FriendlyAgent: forum.Name,
|
||||
Count: count,
|
||||
})
|
||||
}
|
||||
|
||||
pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", forumItems, timeRange.Range}
|
||||
return panelRenderTemplate("panel_analytics_forums", w, r, user, &pi)
|
||||
}
|
||||
|
||||
func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*addHook(() => {
|
||||
|
||||
})*/
|
||||
|
||||
function buildStatsChart(rawLabels, seriesData, timeRange) {
|
||||
let labels = [];
|
||||
if(timeRange=="one-month") {
|
||||
labels = ["today","01 days"];
|
||||
for(let i = 2; i < 30; i++) {
|
||||
let label = "0" + i + " days";
|
||||
if(label.length > "01 days".length) label = label.substr(1);
|
||||
labels.push(label);
|
||||
}
|
||||
} else if(timeRange=="one-week") {
|
||||
labels = ["today"];
|
||||
for(let i = 2; i < 14; i++) {
|
||||
if (i%2==0) labels.push("");
|
||||
else labels.push(Math.floor(i/2) + " days");
|
||||
}
|
||||
} else {
|
||||
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()
|
||||
seriesData = seriesData.reverse();
|
||||
|
||||
Chartist.Line('.ct_chart', {
|
||||
labels: labels,
|
||||
series: [seriesData],
|
||||
}, {
|
||||
height: '250px',
|
||||
});
|
||||
}
|
|
@ -1,14 +1,27 @@
|
|||
'use strict';
|
||||
var form_vars = {};
|
||||
var formVars = {};
|
||||
var alertList = [];
|
||||
var alertCount = 0;
|
||||
var conn;
|
||||
var selectedTopics = [];
|
||||
var attachItemCallback = function(){}
|
||||
var hooks = {
|
||||
"start_init": [],
|
||||
"end_init": [],
|
||||
};
|
||||
|
||||
// Topic move
|
||||
var forumToMoveTo = 0;
|
||||
|
||||
function runHook(name, ...args) {
|
||||
if(!(name in hooks)) return;
|
||||
|
||||
let hook = hooks[name];
|
||||
for (const callback in hook) {
|
||||
callback(...args);
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
@ -203,6 +216,7 @@ function runWebSockets() {
|
|||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
runHook("start_init");
|
||||
if(window["WebSocket"]) runWebSockets();
|
||||
else conn = false;
|
||||
|
||||
|
@ -311,7 +325,7 @@ $(document).ready(function(){
|
|||
var fieldType = this.getAttribute("data-type");
|
||||
if(fieldType=="list") {
|
||||
var fieldValue = this.getAttribute("data-value");
|
||||
if(fieldName in form_vars) var it = form_vars[fieldName];
|
||||
if(fieldName in formVars) var it = formVars[fieldName];
|
||||
else var it = ['No','Yes'];
|
||||
var itLen = it.length;
|
||||
var out = "";
|
||||
|
@ -624,4 +638,6 @@ $(document).ready(function(){
|
|||
});
|
||||
})
|
||||
});
|
||||
|
||||
runHook("end_init");
|
||||
});
|
||||
|
|
|
@ -247,8 +247,6 @@ func writeSelects(adapter qgen.Adapter) error {
|
|||
|
||||
build.Select("groupEntryExists").Table("users_groups").Columns("gid").Where("name = ''").Orderby("gid ASC").Limit("0,1").Parse()
|
||||
|
||||
build.Select("getForumTopicsOffset").Table("topics").Columns("tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount").Where("parentID = ?").Orderby("sticky DESC, lastReplyAt DESC, createdBy DESC").Limit("?,?").Parse()
|
||||
|
||||
build.Select("getAttachment").Table("attachments").Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path").Where("path = ? AND sectionID = ? AND sectionTable = ?").Parse()
|
||||
|
||||
return nil
|
||||
|
|
|
@ -6,7 +6,7 @@ func routes() {
|
|||
addRoute(View("routes.Overview", "/overview/"))
|
||||
addRoute(View("routes.CustomPage", "/pages/", "extraData"))
|
||||
addRoute(View("routeForums", "/forums/" /*,"&forums"*/))
|
||||
addRoute(View("routeForum", "/forum/", "extraData"))
|
||||
addRoute(View("routes.ViewForum", "/forum/", "extraData"))
|
||||
addRoute(AnonAction("routeChangeTheme", "/theme/"))
|
||||
addRoute(
|
||||
View("routeShowAttachment", "/attachs/", "extraData").Before("ParseForm"),
|
||||
|
@ -170,10 +170,12 @@ func buildPanelRoutes() {
|
|||
View("routePanelAnalyticsReferrers", "/panel/analytics/referrers/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"),
|
||||
View("routePanelAnalyticsAgentViews", "/panel/analytics/agent/", "extraData"),
|
||||
View("routePanelAnalyticsForumViews", "/panel/analytics/forum/", "extraData"),
|
||||
View("routePanelAnalyticsSystemViews", "/panel/analytics/system/", "extraData"),
|
||||
View("routePanelAnalyticsReferrerViews", "/panel/analytics/referrer/", "extraData"),
|
||||
View("routePanelAnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsTopics", "/panel/analytics/topics/").Before("ParseForm"),
|
||||
View("routePanelAnalyticsForums", "/panel/analytics/forums/").Before("ParseForm"),
|
||||
|
||||
View("routePanelGroups", "/panel/groups/"),
|
||||
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),
|
||||
|
|
96
routes.go
96
routes.go
|
@ -44,102 +44,6 @@ func routeUploads() {
|
|||
func BadRoute() {
|
||||
}
|
||||
|
||||
func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
|
||||
// SEO URLs...
|
||||
halves := strings.Split(sfid, ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
}
|
||||
fid, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return common.PreError("The provided ForumID is not a valid number.", w, r)
|
||||
}
|
||||
|
||||
headerVars, ferr := common.ForumUserCheck(w, r, &user, fid)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
headerVars.Zone = "view_forum"
|
||||
|
||||
// TODO: Fix this double-check
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.NotFound(w, r, headerVars)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Does forum.TopicCount take the deleted items into consideration for guests? We don't have soft-delete yet, only hard-delete
|
||||
offset, page, lastPage := common.PageOffset(forum.TopicCount, page, common.Config.ItemsPerPage)
|
||||
|
||||
// TODO: Move this to *Forum
|
||||
rows, err := stmts.getForumTopicsOffset.Query(fid, offset, common.Config.ItemsPerPage)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// TODO: Use something other than TopicsRow as we don't need to store the forum name and link on each and every topic item?
|
||||
var topicList []*common.TopicsRow
|
||||
var reqUserList = make(map[int]bool)
|
||||
for rows.Next() {
|
||||
var topicItem = common.TopicsRow{ID: 0}
|
||||
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
topicItem.Link = common.BuildTopicURL(common.NameToSlug(topicItem.Title), topicItem.ID)
|
||||
topicItem.RelativeLastReplyAt = common.RelativeTime(topicItem.LastReplyAt)
|
||||
|
||||
common.RunVhook("forum_trow_assign", &topicItem, &forum)
|
||||
topicList = append(topicList, &topicItem)
|
||||
reqUserList[topicItem.CreatedBy] = true
|
||||
reqUserList[topicItem.LastReplyBy] = true
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Convert the user ID map to a slice, then bulk load the users
|
||||
var idSlice = make([]int, len(reqUserList))
|
||||
var i int
|
||||
for userID := range reqUserList {
|
||||
idSlice[i] = userID
|
||||
i++
|
||||
}
|
||||
|
||||
// TODO: What if a user is deleted via the Control Panel?
|
||||
userList, err := common.Users.BulkGetMap(idSlice)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Second pass to the add the user data
|
||||
// TODO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||
for _, topicItem := range topicList {
|
||||
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||
}
|
||||
|
||||
pageList := common.Paginate(forum.TopicCount, common.Config.ItemsPerPage, 5)
|
||||
pi := common.ForumPage{forum.Name, user, headerVars, topicList, forum, pageList, page, lastPage}
|
||||
if common.RunPreRenderHook("pre_render_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "forum", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func routeForums(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, ferr := common.UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"../common"
|
||||
"../common/counters"
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
type ForumStmts struct {
|
||||
getTopics *sql.Stmt
|
||||
}
|
||||
|
||||
var forumStmts ForumStmts
|
||||
|
||||
// TODO: Move these DbInits into *Forum as Topics()
|
||||
func init() {
|
||||
common.DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||
forumStmts = ForumStmts{
|
||||
getTopics: acc.Select("topics").Columns("tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount").Where("parentID = ?").Orderby("sticky DESC, lastReplyAt DESC, createdBy DESC").Limit("?,?").Prepare(),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
}
|
||||
|
||||
func ViewForum(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
|
||||
// SEO URLs...
|
||||
halves := strings.Split(sfid, ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
}
|
||||
fid, err := strconv.Atoi(halves[1])
|
||||
if err != nil {
|
||||
return common.PreError("The provided ForumID is not a valid number.", w, r)
|
||||
}
|
||||
|
||||
headerVars, ferr := common.ForumUserCheck(w, r, &user, fid)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
if !user.Perms.ViewTopic {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
headerVars.Zone = "view_forum"
|
||||
|
||||
// TODO: Fix this double-check
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == sql.ErrNoRows {
|
||||
return common.NotFound(w, r, headerVars)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Does forum.TopicCount take the deleted items into consideration for guests? We don't have soft-delete yet, only hard-delete
|
||||
offset, page, lastPage := common.PageOffset(forum.TopicCount, page, common.Config.ItemsPerPage)
|
||||
|
||||
// TODO: Move this to *Forum
|
||||
rows, err := forumStmts.getTopics.Query(fid, offset, common.Config.ItemsPerPage)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// TODO: Use something other than TopicsRow as we don't need to store the forum name and link on each and every topic item?
|
||||
var topicList []*common.TopicsRow
|
||||
var reqUserList = make(map[int]bool)
|
||||
for rows.Next() {
|
||||
var topicItem = common.TopicsRow{ID: 0}
|
||||
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.IsClosed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
topicItem.Link = common.BuildTopicURL(common.NameToSlug(topicItem.Title), topicItem.ID)
|
||||
topicItem.RelativeLastReplyAt = common.RelativeTime(topicItem.LastReplyAt)
|
||||
|
||||
common.RunVhook("forum_trow_assign", &topicItem, &forum)
|
||||
topicList = append(topicList, &topicItem)
|
||||
reqUserList[topicItem.CreatedBy] = true
|
||||
reqUserList[topicItem.LastReplyBy] = true
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Convert the user ID map to a slice, then bulk load the users
|
||||
var idSlice = make([]int, len(reqUserList))
|
||||
var i int
|
||||
for userID := range reqUserList {
|
||||
idSlice[i] = userID
|
||||
i++
|
||||
}
|
||||
|
||||
// TODO: What if a user is deleted via the Control Panel?
|
||||
userList, err := common.Users.BulkGetMap(idSlice)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Second pass to the add the user data
|
||||
// TODO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||
for _, topicItem := range topicList {
|
||||
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||
}
|
||||
|
||||
pageList := common.Paginate(forum.TopicCount, common.Config.ItemsPerPage, 5)
|
||||
pi := common.ForumPage{forum.Name, user, headerVars, topicList, forum, pageList, page, lastPage}
|
||||
if common.RunPreRenderHook("pre_render_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "forum", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
counters.ForumViewCounter.Bump(forum.ID)
|
||||
return nil
|
||||
}
|
|
@ -191,6 +191,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user common.User, urlBit
|
|||
return common.InternalError(err, w, r)
|
||||
}
|
||||
counters.TopicViewCounter.Bump(topic.ID) // TODO: Move this into the router?
|
||||
counters.ForumViewCounter.Bump(topic.ParentID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<script>
|
||||
var form_vars = {'perm_preset': ['can_moderate','can_post','read_only','no_access','default','custom']};
|
||||
var formVars = {'perm_preset': ['can_moderate','can_post','read_only','no_access','default','custom']};
|
||||
</script>
|
||||
|
||||
<main class="colstack_right">
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
<div class="rowitem passive submenu">
|
||||
<a href="/panel/analytics/topics/">Topics</a>
|
||||
</div>
|
||||
<div class="rowitem passive submenu">
|
||||
<a href="/panel/analytics/forums/">Forums</a>
|
||||
</div>
|
||||
<div class="rowitem passive submenu">
|
||||
<a href="/panel/analytics/routes/">Routes</a>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>{{.FriendlyAgent}} Views</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -17,35 +18,17 @@
|
|||
</div>
|
||||
</form>
|
||||
<div id="panel_analytics_views" class="colstack_graph_holder">
|
||||
<div class="ct-chart"></div>
|
||||
<div class="ct_chart"></div>
|
||||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>User Agents</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forum/{{.Agent}}" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<a>{{.FriendlyAgent}} Views</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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_views" class="colstack_graph_holder">
|
||||
<div class="ct_chart"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<script>
|
||||
let rawLabels = [{{range .PrimaryGraph.Labels}}
|
||||
{{.}},{{end}}
|
||||
];
|
||||
let seriesData = [{{range .PrimaryGraph.Series}}
|
||||
{{.}},{{end}}
|
||||
];
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
|
@ -0,0 +1,30 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_analytics_right" class="colstack_right">
|
||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forums/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<a>Forums</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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_routes" class="colstack_item rowlist">
|
||||
{{range .ItemList}}
|
||||
<div class="rowitem panel_compactrow editable_parent">
|
||||
<a href="/panel/analytics/forum/{{.Agent}}" class="panel_upshift">{{.FriendlyAgent}}</a>
|
||||
<span class="panel_compacttext to_right">{{.Count}} views</span>
|
||||
</div>
|
||||
{{else}}<div class="rowitem passive rowmsg">No forum view counts could be found in the selected time range</div>{{end}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
|
@ -8,6 +8,7 @@
|
|||
<a>Post Counts</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -33,30 +34,12 @@
|
|||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>{{.Agent}} Views</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -17,35 +18,17 @@
|
|||
</div>
|
||||
</form>
|
||||
<div id="panel_analytics_referrers" class="colstack_graph_holder">
|
||||
<div class="ct-chart"></div>
|
||||
<div class="ct_chart"></div>
|
||||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>Referrers</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>{{.Route}} Views</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -17,7 +18,7 @@
|
|||
</div>
|
||||
</form>
|
||||
<div id="panel_analytics_views" class="colstack_graph_holder">
|
||||
<div class="ct-chart"></div>
|
||||
<div class="ct_chart"></div>
|
||||
</div>
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a>Details</a></div>
|
||||
|
@ -33,30 +34,12 @@
|
|||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>Routes</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>{{.FriendlyAgent}} Views</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -17,35 +18,17 @@
|
|||
</div>
|
||||
</form>
|
||||
<div id="panel_analytics_systems" class="colstack_graph_holder">
|
||||
<div class="ct-chart"></div>
|
||||
<div class="ct_chart"></div>
|
||||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>Operating Systems</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<a>Topic Counts</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -33,30 +34,12 @@
|
|||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/views/" method="get">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem">
|
||||
<a>Views</a>
|
||||
<a>Requests</a>
|
||||
<select class="timeRangeSelector to_right" name="timeRange">
|
||||
<option val="one-month"{{if eq .TimeRange "one-month"}} selected{{end}}>1 month</option>
|
||||
<option val="one-week"{{if eq .TimeRange "one-week"}} selected{{end}}>1 week</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>
|
||||
|
@ -33,30 +34,12 @@
|
|||
</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',
|
||||
});
|
||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}");
|
||||
</script>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<script>var form_vars = {
|
||||
<script>var formVars = {
|
||||
'forum_active': ['Hide','Show'],
|
||||
'forum_preset': ['all','announce','members','staff','admins','archive','custom']};
|
||||
</script>
|
||||
|
|
|
@ -196,6 +196,30 @@
|
|||
.ct-point:hover {
|
||||
stroke: hsl(359,98%,30%) !important;
|
||||
}
|
||||
/*.ct-chart-line {
|
||||
height: 300px !important;
|
||||
}*/
|
||||
/*.ct-label.ct-horizontal {
|
||||
position: fixed;
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
transform-origin: 100% 0;
|
||||
transform: translate(-100%) rotate(-90deg);
|
||||
white-space: nowrap;
|
||||
/*padding-right: 48px;/
|
||||
top: 8px;
|
||||
}*/
|
||||
/*.ct-label.ct-horizontal {
|
||||
white-space: nowrap;
|
||||
}*/
|
||||
/*.ct-label.ct-horizontal {
|
||||
position: fixed;
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
transform-origin: 100% 0;
|
||||
transform: translate(-100%) rotate(-45deg);
|
||||
white-space: nowrap;
|
||||
}*/
|
||||
.timeRangeSelector {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue