Added Chartist as a dependency.
Fixed a XSS exploit. Centralised the post escaping logic. Began work on the Analytics UI.
This commit is contained in:
parent
ac9623ab6b
commit
547254c4a1
@ -96,6 +96,7 @@ var PreRenderHooks = map[string][]func(http.ResponseWriter, *http.Request, *User
|
||||
"pre_render_panel_forums": nil,
|
||||
"pre_render_panel_delete_forum": nil,
|
||||
"pre_render_panel_edit_forum": nil,
|
||||
"pre_render_panel_analytics": nil,
|
||||
"pre_render_panel_settings": nil,
|
||||
"pre_render_panel_setting": nil,
|
||||
"pre_render_panel_word_filters": nil,
|
||||
|
@ -3,6 +3,7 @@ package common
|
||||
import (
|
||||
//"fmt"
|
||||
"bytes"
|
||||
"html"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -164,6 +165,7 @@ func shortcodeToUnicode(msg string) string {
|
||||
return msg
|
||||
}
|
||||
|
||||
// TODO: Write a test for this
|
||||
func PreparseMessage(msg string) string {
|
||||
msg = strings.Replace(msg, "<p><br>", "\n\n", -1)
|
||||
msg = strings.Replace(msg, "<p>", "\n\n", -1)
|
||||
@ -172,6 +174,7 @@ func PreparseMessage(msg string) string {
|
||||
if Sshooks["preparse_preassign"] != nil {
|
||||
msg = RunSshook("preparse_preassign", msg)
|
||||
}
|
||||
msg = html.EscapeString(msg)
|
||||
return shortcodeToUnicode(msg)
|
||||
}
|
||||
|
||||
@ -317,13 +320,6 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||
outbytes = append(outbytes, uidBit...)
|
||||
outbytes = append(outbytes, UrlClose...)
|
||||
lastItem = i
|
||||
|
||||
//log.Print(string(msgbytes))
|
||||
//log.Print(msgbytes)
|
||||
//log.Print("msgbytes[lastItem - 1]: ", msgbytes[lastItem - 1])
|
||||
//log.Print("lastItem - 1: ", lastItem - 1)
|
||||
//log.Print("msgbytes[lastItem]: ", msgbytes[lastItem])
|
||||
//log.Print("lastItem: ", lastItem)
|
||||
} else if msgbytes[i] == 'h' || msgbytes[i] == 'f' || msgbytes[i] == 'g' {
|
||||
//log.Print("IN hfg")
|
||||
if msgbytes[i+1] == 't' && msgbytes[i+2] == 't' && msgbytes[i+3] == 'p' {
|
||||
|
39
common/statistics.go
Normal file
39
common/statistics.go
Normal file
@ -0,0 +1,39 @@
|
||||
package common
|
||||
|
||||
// EXPERIMENTAL
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var StatStore StatStoreInt
|
||||
|
||||
type StatStoreInt interface {
|
||||
LookupInt(name string, duration int, unit string) (int, error)
|
||||
}
|
||||
|
||||
type DefaultStatStore struct {
|
||||
}
|
||||
|
||||
func NewDefaultStatStore() *DefaultStatStore {
|
||||
return &DefaultStatStore{}
|
||||
}
|
||||
|
||||
func (store *DefaultStatStore) LookupInt(name string, duration int, unit string) (int, error) {
|
||||
switch name {
|
||||
case "postCount":
|
||||
return store.countTable("replies", duration, unit)
|
||||
}
|
||||
return 0, errors.New("The requested stat doesn't exist")
|
||||
}
|
||||
|
||||
func (store *DefaultStatStore) countTable(table string, duration int, unit string) (stat int, err error) {
|
||||
/*acc := qgen.Builder.Accumulator()
|
||||
counter := acc.Count("replies").DateCutoff("createdAt", 1, "day").Prepare()
|
||||
if acc.FirstError() != nil {
|
||||
return 0, acc.FirstError()
|
||||
}
|
||||
err := counter.QueryRow().Scan(&stat)*/
|
||||
return stat, err
|
||||
}
|
||||
|
||||
//stmts.todaysPostCount, err = db.Prepare("select count(*) from replies where createdAt BETWEEN (utc_timestamp() - interval 1 day) and utc_timestamp()")
|
@ -8,7 +8,6 @@ package common
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"html"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -238,7 +237,7 @@ func (topic *Topic) Delete() error {
|
||||
|
||||
func (topic *Topic) Update(name string, content string) error {
|
||||
content = PreparseMessage(content)
|
||||
parsedContent := ParseMessage(html.EscapeString(content), topic.ParentID, "forums")
|
||||
parsedContent := ParseMessage(content, topic.ParentID, "forums")
|
||||
_, err := topicStmts.edit.Exec(name, content, parsedContent, topic.ID)
|
||||
topic.cacheRemove()
|
||||
return err
|
||||
|
@ -254,6 +254,7 @@ func RouteCreateGuildSubmit(w http.ResponseWriter, r *http.Request, user common.
|
||||
|
||||
var guildActive = true
|
||||
var guildName = html.EscapeString(r.PostFormValue("group_name"))
|
||||
// TODO: Allow Markdown / BBCode / Limited HTML in the description?
|
||||
var guildDesc = html.EscapeString(r.PostFormValue("group_desc"))
|
||||
var gprivacy = r.PostFormValue("group_privacy")
|
||||
|
||||
|
156
gen_router.go
156
gen_router.go
@ -49,6 +49,7 @@ var RouteMap = map[string]interface{}{
|
||||
"routePanelUsers": routePanelUsers,
|
||||
"routePanelUsersEdit": routePanelUsersEdit,
|
||||
"routePanelUsersEditSubmit": routePanelUsersEditSubmit,
|
||||
"routePanelAnalyticsViews": routePanelAnalyticsViews,
|
||||
"routePanelGroups": routePanelGroups,
|
||||
"routePanelGroupsEdit": routePanelGroupsEdit,
|
||||
"routePanelGroupsEditPerms": routePanelGroupsEditPerms,
|
||||
@ -112,31 +113,32 @@ var routeMapEnum = map[string]int{
|
||||
"routePanelUsers": 31,
|
||||
"routePanelUsersEdit": 32,
|
||||
"routePanelUsersEditSubmit": 33,
|
||||
"routePanelGroups": 34,
|
||||
"routePanelGroupsEdit": 35,
|
||||
"routePanelGroupsEditPerms": 36,
|
||||
"routePanelGroupsEditSubmit": 37,
|
||||
"routePanelGroupsEditPermsSubmit": 38,
|
||||
"routePanelGroupsCreateSubmit": 39,
|
||||
"routePanelBackups": 40,
|
||||
"routePanelLogsMod": 41,
|
||||
"routePanelDebug": 42,
|
||||
"routePanel": 43,
|
||||
"routeAccountEditCritical": 44,
|
||||
"routeAccountEditCriticalSubmit": 45,
|
||||
"routeAccountEditAvatar": 46,
|
||||
"routeAccountEditAvatarSubmit": 47,
|
||||
"routeAccountEditUsername": 48,
|
||||
"routeAccountEditUsernameSubmit": 49,
|
||||
"routeAccountEditEmail": 50,
|
||||
"routeAccountEditEmailTokenSubmit": 51,
|
||||
"routeProfile": 52,
|
||||
"routeBanSubmit": 53,
|
||||
"routeUnban": 54,
|
||||
"routeActivate": 55,
|
||||
"routeIps": 56,
|
||||
"routeDynamic": 57,
|
||||
"routeUploads": 58,
|
||||
"routePanelAnalyticsViews": 34,
|
||||
"routePanelGroups": 35,
|
||||
"routePanelGroupsEdit": 36,
|
||||
"routePanelGroupsEditPerms": 37,
|
||||
"routePanelGroupsEditSubmit": 38,
|
||||
"routePanelGroupsEditPermsSubmit": 39,
|
||||
"routePanelGroupsCreateSubmit": 40,
|
||||
"routePanelBackups": 41,
|
||||
"routePanelLogsMod": 42,
|
||||
"routePanelDebug": 43,
|
||||
"routePanel": 44,
|
||||
"routeAccountEditCritical": 45,
|
||||
"routeAccountEditCriticalSubmit": 46,
|
||||
"routeAccountEditAvatar": 47,
|
||||
"routeAccountEditAvatarSubmit": 48,
|
||||
"routeAccountEditUsername": 49,
|
||||
"routeAccountEditUsernameSubmit": 50,
|
||||
"routeAccountEditEmail": 51,
|
||||
"routeAccountEditEmailTokenSubmit": 52,
|
||||
"routeProfile": 53,
|
||||
"routeBanSubmit": 54,
|
||||
"routeUnban": 55,
|
||||
"routeActivate": 56,
|
||||
"routeIps": 57,
|
||||
"routeDynamic": 58,
|
||||
"routeUploads": 59,
|
||||
}
|
||||
var reverseRouteMapEnum = map[int]string{
|
||||
0: "routeAPI",
|
||||
@ -173,31 +175,32 @@ var reverseRouteMapEnum = map[int]string{
|
||||
31: "routePanelUsers",
|
||||
32: "routePanelUsersEdit",
|
||||
33: "routePanelUsersEditSubmit",
|
||||
34: "routePanelGroups",
|
||||
35: "routePanelGroupsEdit",
|
||||
36: "routePanelGroupsEditPerms",
|
||||
37: "routePanelGroupsEditSubmit",
|
||||
38: "routePanelGroupsEditPermsSubmit",
|
||||
39: "routePanelGroupsCreateSubmit",
|
||||
40: "routePanelBackups",
|
||||
41: "routePanelLogsMod",
|
||||
42: "routePanelDebug",
|
||||
43: "routePanel",
|
||||
44: "routeAccountEditCritical",
|
||||
45: "routeAccountEditCriticalSubmit",
|
||||
46: "routeAccountEditAvatar",
|
||||
47: "routeAccountEditAvatarSubmit",
|
||||
48: "routeAccountEditUsername",
|
||||
49: "routeAccountEditUsernameSubmit",
|
||||
50: "routeAccountEditEmail",
|
||||
51: "routeAccountEditEmailTokenSubmit",
|
||||
52: "routeProfile",
|
||||
53: "routeBanSubmit",
|
||||
54: "routeUnban",
|
||||
55: "routeActivate",
|
||||
56: "routeIps",
|
||||
57: "routeDynamic",
|
||||
58: "routeUploads",
|
||||
34: "routePanelAnalyticsViews",
|
||||
35: "routePanelGroups",
|
||||
36: "routePanelGroupsEdit",
|
||||
37: "routePanelGroupsEditPerms",
|
||||
38: "routePanelGroupsEditSubmit",
|
||||
39: "routePanelGroupsEditPermsSubmit",
|
||||
40: "routePanelGroupsCreateSubmit",
|
||||
41: "routePanelBackups",
|
||||
42: "routePanelLogsMod",
|
||||
43: "routePanelDebug",
|
||||
44: "routePanel",
|
||||
45: "routeAccountEditCritical",
|
||||
46: "routeAccountEditCriticalSubmit",
|
||||
47: "routeAccountEditAvatar",
|
||||
48: "routeAccountEditAvatarSubmit",
|
||||
49: "routeAccountEditUsername",
|
||||
50: "routeAccountEditUsernameSubmit",
|
||||
51: "routeAccountEditEmail",
|
||||
52: "routeAccountEditEmailTokenSubmit",
|
||||
53: "routeProfile",
|
||||
54: "routeBanSubmit",
|
||||
55: "routeUnban",
|
||||
56: "routeActivate",
|
||||
57: "routeIps",
|
||||
58: "routeDynamic",
|
||||
59: "routeUploads",
|
||||
}
|
||||
|
||||
// TODO: Stop spilling these into the package scope?
|
||||
@ -565,14 +568,17 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
common.RouteViewCounter.Bump(33)
|
||||
err = routePanelUsersEditSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/":
|
||||
case "/panel/analytics/views/":
|
||||
common.RouteViewCounter.Bump(34)
|
||||
err = routePanelAnalyticsViews(w,req,user)
|
||||
case "/panel/groups/":
|
||||
common.RouteViewCounter.Bump(35)
|
||||
err = routePanelGroups(w,req,user)
|
||||
case "/panel/groups/edit/":
|
||||
common.RouteViewCounter.Bump(35)
|
||||
common.RouteViewCounter.Bump(36)
|
||||
err = routePanelGroupsEdit(w,req,user,extraData)
|
||||
case "/panel/groups/edit/perms/":
|
||||
common.RouteViewCounter.Bump(36)
|
||||
common.RouteViewCounter.Bump(37)
|
||||
err = routePanelGroupsEditPerms(w,req,user,extraData)
|
||||
case "/panel/groups/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -581,7 +587,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(37)
|
||||
common.RouteViewCounter.Bump(38)
|
||||
err = routePanelGroupsEditSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/edit/perms/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -590,7 +596,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(38)
|
||||
common.RouteViewCounter.Bump(39)
|
||||
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
|
||||
case "/panel/groups/create/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -599,7 +605,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(39)
|
||||
common.RouteViewCounter.Bump(40)
|
||||
err = routePanelGroupsCreateSubmit(w,req,user)
|
||||
case "/panel/backups/":
|
||||
err = common.SuperAdminOnly(w,req,user)
|
||||
@ -608,10 +614,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(40)
|
||||
common.RouteViewCounter.Bump(41)
|
||||
err = routePanelBackups(w,req,user,extraData)
|
||||
case "/panel/logs/mod/":
|
||||
common.RouteViewCounter.Bump(41)
|
||||
common.RouteViewCounter.Bump(42)
|
||||
err = routePanelLogsMod(w,req,user)
|
||||
case "/panel/debug/":
|
||||
err = common.AdminOnly(w,req,user)
|
||||
@ -620,10 +626,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(42)
|
||||
common.RouteViewCounter.Bump(43)
|
||||
err = routePanelDebug(w,req,user)
|
||||
default:
|
||||
common.RouteViewCounter.Bump(43)
|
||||
common.RouteViewCounter.Bump(44)
|
||||
err = routePanel(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -638,7 +644,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(44)
|
||||
common.RouteViewCounter.Bump(45)
|
||||
err = routeAccountEditCritical(w,req,user)
|
||||
case "/user/edit/critical/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -653,7 +659,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(45)
|
||||
common.RouteViewCounter.Bump(46)
|
||||
err = routeAccountEditCriticalSubmit(w,req,user)
|
||||
case "/user/edit/avatar/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -662,7 +668,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(46)
|
||||
common.RouteViewCounter.Bump(47)
|
||||
err = routeAccountEditAvatar(w,req,user)
|
||||
case "/user/edit/avatar/submit/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -671,7 +677,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(47)
|
||||
common.RouteViewCounter.Bump(48)
|
||||
err = routeAccountEditAvatarSubmit(w,req,user)
|
||||
case "/user/edit/username/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -680,7 +686,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(48)
|
||||
common.RouteViewCounter.Bump(49)
|
||||
err = routeAccountEditUsername(w,req,user)
|
||||
case "/user/edit/username/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -695,7 +701,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(49)
|
||||
common.RouteViewCounter.Bump(50)
|
||||
err = routeAccountEditUsernameSubmit(w,req,user)
|
||||
case "/user/edit/email/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -704,7 +710,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(50)
|
||||
common.RouteViewCounter.Bump(51)
|
||||
err = routeAccountEditEmail(w,req,user)
|
||||
case "/user/edit/token/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -719,11 +725,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(51)
|
||||
common.RouteViewCounter.Bump(52)
|
||||
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
|
||||
default:
|
||||
req.URL.Path += extraData
|
||||
common.RouteViewCounter.Bump(52)
|
||||
common.RouteViewCounter.Bump(53)
|
||||
err = routeProfile(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -744,7 +750,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(53)
|
||||
common.RouteViewCounter.Bump(54)
|
||||
err = routeBanSubmit(w,req,user,extraData)
|
||||
case "/users/unban/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -759,7 +765,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(54)
|
||||
common.RouteViewCounter.Bump(55)
|
||||
err = routeUnban(w,req,user,extraData)
|
||||
case "/users/activate/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
@ -774,7 +780,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(55)
|
||||
common.RouteViewCounter.Bump(56)
|
||||
err = routeActivate(w,req,user,extraData)
|
||||
case "/users/ips/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
@ -783,7 +789,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
common.RouteViewCounter.Bump(56)
|
||||
common.RouteViewCounter.Bump(57)
|
||||
err = routeIps(w,req,user)
|
||||
}
|
||||
if err != nil {
|
||||
@ -800,7 +806,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
common.NotFound(w,req)
|
||||
return
|
||||
}
|
||||
common.RouteViewCounter.Bump(58)
|
||||
common.RouteViewCounter.Bump(59)
|
||||
req.URL.Path += extraData
|
||||
// TODO: Find a way to propagate errors up from this?
|
||||
router.UploadHandler(w,req) // TODO: Count these views
|
||||
@ -844,7 +850,7 @@ 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
|
||||
common.RouteViewCounter.Bump(58) // TODO: Be more specific about *which* dynamic route it is
|
||||
req.URL.Path += extraData
|
||||
err = handle(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -73,6 +73,7 @@
|
||||
"panel-forums":"Forum Manager",
|
||||
"panel-delete-forum":"Delete Forum",
|
||||
"panel-edit-forum":"Forum Editor",
|
||||
"panel-analytics":"Analytics",
|
||||
"panel-settings":"Setting Manager",
|
||||
"panel-edit-setting":"Edit Setting",
|
||||
"panel-word-filters":"Word Filter Manager",
|
||||
|
@ -133,7 +133,8 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user common.
|
||||
}
|
||||
|
||||
topicName := html.EscapeString(r.PostFormValue("topic-name"))
|
||||
content := html.EscapeString(common.PreparseMessage(r.PostFormValue("topic-content")))
|
||||
content := common.PreparseMessage(r.PostFormValue("topic-content"))
|
||||
// TODO: Fully parse the post and store it in the parsed column
|
||||
tid, err := common.Topics.Create(fid, topicName, content, user.ID, user.LastIP)
|
||||
if err != nil {
|
||||
switch err {
|
||||
@ -333,7 +334,8 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user common.User)
|
||||
}
|
||||
}
|
||||
|
||||
content := common.PreparseMessage(html.EscapeString(r.PostFormValue("reply-content")))
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and put that in the parsed column
|
||||
_, err = common.Rstore.Create(topic, content, user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
@ -527,7 +529,8 @@ func routeProfileReplyCreate(w http.ResponseWriter, r *http.Request, user common
|
||||
return common.LocalError("Invalid UID", w, r, user)
|
||||
}
|
||||
|
||||
content := html.EscapeString(common.PreparseMessage(r.PostFormValue("reply-content")))
|
||||
content := common.PreparseMessage(r.PostFormValue("reply-content"))
|
||||
// TODO: Fully parse the post and store it in the parsed column
|
||||
_, err = common.Prstore.Create(uid, content, user.ID, user.LastIP)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
@ -726,7 +729,6 @@ func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
|
||||
err := r.ParseMultipartForm(int64(common.Megabyte))
|
||||
if err != nil {
|
||||
return common.LocalError("Upload failed", w, r, user)
|
||||
@ -786,8 +788,8 @@ func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
|
||||
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your avatar was successfully updated")
|
||||
|
||||
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
|
||||
if common.PreRenderHooks["pre_render_account_own_edit_avatar"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
|
||||
@ -807,7 +809,7 @@ func routeAccountEditUsername(w http.ResponseWriter, r *http.Request, user commo
|
||||
return ferr
|
||||
}
|
||||
|
||||
pi := common.Page{"Edit common.Username", user, headerVars, tList, user.Name}
|
||||
pi := common.Page{"Edit Username", user, headerVars, tList, user.Name}
|
||||
if common.PreRenderHooks["pre_render_account_own_edit_username"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
||||
return nil
|
||||
@ -834,7 +836,7 @@ func routeAccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user
|
||||
user.Name = newUsername
|
||||
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your username was successfully updated")
|
||||
pi := common.Page{"Edit common.Username", user, headerVars, tList, nil}
|
||||
pi := common.Page{"Edit Username", user, headerVars, tList, nil}
|
||||
if common.PreRenderHooks["pre_render_account_own_edit_username"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
||||
return nil
|
||||
@ -885,10 +887,10 @@ func routeAccountEditEmail(w http.ResponseWriter, r *http.Request, user common.U
|
||||
email.Primary = true
|
||||
emailList = append(emailList, email)
|
||||
}
|
||||
|
||||
if !common.Site.EnableEmails {
|
||||
headerVars.NoticeList = append(headerVars.NoticeList, "The mail system is currently disabled.")
|
||||
}
|
||||
|
||||
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
|
||||
if common.PreRenderHooks["pre_render_account_own_edit_email"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_account_own_edit_email", w, r, &user, &pi) {
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
//"log"
|
||||
//"fmt"
|
||||
"encoding/json"
|
||||
"html"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -45,7 +44,8 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||
}
|
||||
|
||||
topicName := r.PostFormValue("topic_name")
|
||||
topicContent := html.EscapeString(r.PostFormValue("topic_content"))
|
||||
topicContent := common.PreparseMessage(r.PostFormValue("topic_content"))
|
||||
// TODO: Fully parse the post and store it in the parsed column
|
||||
err = topic.Update(topicName, topicContent)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
@ -352,7 +352,7 @@ func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
|
||||
content := html.EscapeString(common.PreparseMessage(r.PostFormValue("edit_item")))
|
||||
content := common.PreparseMessage(r.PostFormValue("edit_item"))
|
||||
_, err = stmts.editReply.Exec(content, common.ParseMessage(content, fid, "forums"), rid)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
@ -457,7 +457,7 @@ func routeProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user co
|
||||
return common.NoPermissionsJSQ(w, r, user, isJs)
|
||||
}
|
||||
|
||||
content := html.EscapeString(common.PreparseMessage(r.PostFormValue("edit_item")))
|
||||
content := common.PreparseMessage(r.PostFormValue("edit_item"))
|
||||
_, err = stmts.editProfileReply.Exec(content, common.ParseMessage(content, 0, ""), rid)
|
||||
if err != nil {
|
||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -75,36 +76,34 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||
}
|
||||
|
||||
// TODO: Add a stat store for this?
|
||||
var postCount int
|
||||
err = stmts.todaysPostCount.QueryRow().Scan(&postCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
var intErr error
|
||||
var extractStat = func(stmt *sql.Stmt) (stat int) {
|
||||
err := stmt.QueryRow().Scan(&stat)
|
||||
if err != nil && err != ErrNoRows {
|
||||
intErr = err
|
||||
}
|
||||
return stat
|
||||
}
|
||||
|
||||
var postCount = extractStat(stmts.todaysPostCount)
|
||||
var postInterval = "day"
|
||||
var postColour = greaterThanSwitch(postCount, 5, 25)
|
||||
|
||||
var topicCount int
|
||||
err = stmts.todaysTopicCount.QueryRow().Scan(&topicCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
var topicCount = extractStat(stmts.todaysTopicCount)
|
||||
var topicInterval = "day"
|
||||
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
||||
|
||||
var reportCount int
|
||||
err = stmts.todaysReportCount.QueryRow().Scan(&reportCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
var reportCount = extractStat(stmts.todaysReportCount)
|
||||
var reportInterval = "week"
|
||||
|
||||
var newUserCount int
|
||||
err = stmts.todaysNewUserCount.QueryRow().Scan(&newUserCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
var newUserCount = extractStat(stmts.todaysNewUserCount)
|
||||
var newUserInterval = "week"
|
||||
|
||||
// Did any of the extractStats fail?
|
||||
if intErr != nil {
|
||||
return common.InternalError(intErr, w, r)
|
||||
}
|
||||
|
||||
var gridElements = []common.GridElement{
|
||||
common.GridElement{"dash-version", "v" + version.String(), 0, "grid_istat stat_green", "", "", "Gosora is up-to-date :)"},
|
||||
common.GridElement{"dash-cpu", "CPU: " + cpustr, 1, "grid_istat " + cpuColour, "", "", "The global CPU usage of this server"},
|
||||
@ -115,6 +114,7 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||
uonline := wsHub.userCount()
|
||||
gonline := wsHub.guestCount()
|
||||
totonline := uonline + gonline
|
||||
reqCount := 0
|
||||
|
||||
var onlineColour = greaterThanSwitch(totonline, 3, 10)
|
||||
var onlineGuestsColour = greaterThanSwitch(gonline, 1, 10)
|
||||
@ -127,19 +127,22 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||
gridElements = append(gridElements, common.GridElement{"dash-totonline", strconv.Itoa(totonline) + totunit + " online", 3, "grid_stat " + onlineColour, "", "", "The number of people who are currently online"})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-gonline", strconv.Itoa(gonline) + gunit + " guests online", 4, "grid_stat " + onlineGuestsColour, "", "", "The number of guests who are currently online"})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-uonline", strconv.Itoa(uonline) + uunit + " users online", 5, "grid_stat " + onlineUsersColour, "", "", "The number of logged-in users who are currently online"})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-reqs", strconv.Itoa(reqCount) + " reqs / second", 7, "grid_stat grid_end_group " + topicColour, "", "", "The number of requests over the last 24 hours"})
|
||||
}
|
||||
|
||||
gridElements = append(gridElements, common.GridElement{"dash-postsperday", strconv.Itoa(postCount) + " posts / " + postInterval, 6, "grid_stat " + postColour, "", "", "The number of new posts over the last 24 hours"})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-topicsperday", strconv.Itoa(topicCount) + " topics / " + topicInterval, 7, "grid_stat " + topicColour, "", "", "The number of new topics over the last 24 hours"})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-totonlineperday", "20 online / day", 8, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The people online over the last 24 hours"*/})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-totonlineperday", "20 online / day", 8, "grid_stat stat_disabled", "", "", "Coming Soon!" /*, "The people online over the last 24 hours"*/})
|
||||
|
||||
gridElements = append(gridElements, common.GridElement{"dash-searches", "8 searches / week", 9, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of searches over the last 7 days"*/})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-newusers", strconv.Itoa(newUserCount) + " new users / " + newUserInterval, 10, "grid_stat", "", "", "The number of new users over the last 7 days"})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-reports", strconv.Itoa(reportCount) + " reports / " + reportInterval, 11, "grid_stat", "", "", "The number of reports over the last 7 days"})
|
||||
|
||||
gridElements = append(gridElements, common.GridElement{"dash-minperuser", "2 minutes / user / week", 12, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of number of minutes spent by each active user over the last 7 days"*/})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-visitorsperweek", "2 visitors / week", 13, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of unique visitors we've had over the last 7 days"*/})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-postsperuser", "5 posts / user / week", 14, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of posts made by each active user over the past week"*/})
|
||||
if false {
|
||||
gridElements = append(gridElements, common.GridElement{"dash-minperuser", "2 minutes / user / week", 12, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of number of minutes spent by each active user over the last 7 days"*/})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-visitorsperweek", "2 visitors / week", 13, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of unique visitors we've had over the last 7 days"*/})
|
||||
gridElements = append(gridElements, common.GridElement{"dash-postsperuser", "5 posts / user / week", 14, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of posts made by each active user over the past week"*/})
|
||||
}
|
||||
|
||||
pi := common.PanelDashboardPage{common.GetTitlePhrase("panel-dashboard"), user, headerVars, stats, "dashboard", gridElements}
|
||||
if common.PreRenderHooks["pre_render_panel_dashboard"] != nil {
|
||||
@ -422,6 +425,25 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
|
||||
return 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
|
||||
}
|
||||
|
||||
pi := common.PanelPage{common.GetTitlePhrase("panel-analytics"), user, headerVars, stats, "analytics", tList, nil}
|
||||
if common.PreRenderHooks["pre_render_panel_analytics"] != nil {
|
||||
if common.RunPreRenderHook("pre_render_panel_analytics", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err := common.Templates.ExecuteTemplate(w, "panel-analytics-views.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
|
615
public/chartist/chartist.css
Normal file
615
public/chartist/chartist.css
Normal file
@ -0,0 +1,615 @@
|
||||
.ct-label {
|
||||
fill: rgba(0, 0, 0, 0.4);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1; }
|
||||
|
||||
.ct-chart-line .ct-label,
|
||||
.ct-chart-bar .ct-label {
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex; }
|
||||
|
||||
.ct-chart-pie .ct-label,
|
||||
.ct-chart-donut .ct-label {
|
||||
dominant-baseline: central; }
|
||||
|
||||
.ct-label.ct-horizontal.ct-start {
|
||||
-webkit-box-align: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
-ms-flex-align: flex-end;
|
||||
align-items: flex-end;
|
||||
-webkit-box-pack: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
-ms-flex-pack: flex-start;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-label.ct-horizontal.ct-end {
|
||||
-webkit-box-align: flex-start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: flex-start;
|
||||
align-items: flex-start;
|
||||
-webkit-box-pack: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
-ms-flex-pack: flex-start;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-label.ct-vertical.ct-start {
|
||||
-webkit-box-align: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
-ms-flex-align: flex-end;
|
||||
align-items: flex-end;
|
||||
-webkit-box-pack: flex-end;
|
||||
-webkit-justify-content: flex-end;
|
||||
-ms-flex-pack: flex-end;
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
text-anchor: end; }
|
||||
|
||||
.ct-label.ct-vertical.ct-end {
|
||||
-webkit-box-align: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
-ms-flex-align: flex-end;
|
||||
align-items: flex-end;
|
||||
-webkit-box-pack: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
-ms-flex-pack: flex-start;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-chart-bar .ct-label.ct-horizontal.ct-start {
|
||||
-webkit-box-align: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
-ms-flex-align: flex-end;
|
||||
align-items: flex-end;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-chart-bar .ct-label.ct-horizontal.ct-end {
|
||||
-webkit-box-align: flex-start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: flex-start;
|
||||
align-items: flex-start;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start {
|
||||
-webkit-box-align: flex-end;
|
||||
-webkit-align-items: flex-end;
|
||||
-ms-flex-align: flex-end;
|
||||
align-items: flex-end;
|
||||
-webkit-box-pack: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
-ms-flex-pack: flex-start;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end {
|
||||
-webkit-box-align: flex-start;
|
||||
-webkit-align-items: flex-start;
|
||||
-ms-flex-align: flex-start;
|
||||
align-items: flex-start;
|
||||
-webkit-box-pack: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
-ms-flex-pack: flex-start;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
text-anchor: start; }
|
||||
|
||||
.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start {
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: flex-end;
|
||||
-webkit-justify-content: flex-end;
|
||||
-ms-flex-pack: flex-end;
|
||||
justify-content: flex-end;
|
||||
text-align: right;
|
||||
text-anchor: end; }
|
||||
|
||||
.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end {
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: flex-start;
|
||||
-webkit-justify-content: flex-start;
|
||||
-ms-flex-pack: flex-start;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
text-anchor: end; }
|
||||
|
||||
.ct-grid {
|
||||
stroke: rgba(0, 0, 0, 0.2);
|
||||
stroke-width: 1px;
|
||||
stroke-dasharray: 2px; }
|
||||
|
||||
.ct-grid-background {
|
||||
fill: none; }
|
||||
|
||||
.ct-point {
|
||||
stroke-width: 10px;
|
||||
stroke-linecap: round; }
|
||||
|
||||
.ct-line {
|
||||
fill: none;
|
||||
stroke-width: 4px; }
|
||||
|
||||
.ct-area {
|
||||
stroke: none;
|
||||
fill-opacity: 0.1; }
|
||||
|
||||
.ct-bar {
|
||||
fill: none;
|
||||
stroke-width: 10px; }
|
||||
|
||||
.ct-slice-donut {
|
||||
fill: none;
|
||||
stroke-width: 60px; }
|
||||
|
||||
.ct-series-a .ct-point, .ct-series-a .ct-line, .ct-series-a .ct-bar, .ct-series-a .ct-slice-donut {
|
||||
stroke: #d70206; }
|
||||
|
||||
.ct-series-a .ct-slice-pie, .ct-series-a .ct-slice-donut-solid, .ct-series-a .ct-area {
|
||||
fill: #d70206; }
|
||||
|
||||
.ct-series-b .ct-point, .ct-series-b .ct-line, .ct-series-b .ct-bar, .ct-series-b .ct-slice-donut {
|
||||
stroke: #f05b4f; }
|
||||
|
||||
.ct-series-b .ct-slice-pie, .ct-series-b .ct-slice-donut-solid, .ct-series-b .ct-area {
|
||||
fill: #f05b4f; }
|
||||
|
||||
.ct-series-c .ct-point, .ct-series-c .ct-line, .ct-series-c .ct-bar, .ct-series-c .ct-slice-donut {
|
||||
stroke: #f4c63d; }
|
||||
|
||||
.ct-series-c .ct-slice-pie, .ct-series-c .ct-slice-donut-solid, .ct-series-c .ct-area {
|
||||
fill: #f4c63d; }
|
||||
|
||||
.ct-series-d .ct-point, .ct-series-d .ct-line, .ct-series-d .ct-bar, .ct-series-d .ct-slice-donut {
|
||||
stroke: #d17905; }
|
||||
|
||||
.ct-series-d .ct-slice-pie, .ct-series-d .ct-slice-donut-solid, .ct-series-d .ct-area {
|
||||
fill: #d17905; }
|
||||
|
||||
.ct-series-e .ct-point, .ct-series-e .ct-line, .ct-series-e .ct-bar, .ct-series-e .ct-slice-donut {
|
||||
stroke: #453d3f; }
|
||||
|
||||
.ct-series-e .ct-slice-pie, .ct-series-e .ct-slice-donut-solid, .ct-series-e .ct-area {
|
||||
fill: #453d3f; }
|
||||
|
||||
.ct-series-f .ct-point, .ct-series-f .ct-line, .ct-series-f .ct-bar, .ct-series-f .ct-slice-donut {
|
||||
stroke: #59922b; }
|
||||
|
||||
.ct-series-f .ct-slice-pie, .ct-series-f .ct-slice-donut-solid, .ct-series-f .ct-area {
|
||||
fill: #59922b; }
|
||||
|
||||
.ct-series-g .ct-point, .ct-series-g .ct-line, .ct-series-g .ct-bar, .ct-series-g .ct-slice-donut {
|
||||
stroke: #0544d3; }
|
||||
|
||||
.ct-series-g .ct-slice-pie, .ct-series-g .ct-slice-donut-solid, .ct-series-g .ct-area {
|
||||
fill: #0544d3; }
|
||||
|
||||
.ct-series-h .ct-point, .ct-series-h .ct-line, .ct-series-h .ct-bar, .ct-series-h .ct-slice-donut {
|
||||
stroke: #6b0392; }
|
||||
|
||||
.ct-series-h .ct-slice-pie, .ct-series-h .ct-slice-donut-solid, .ct-series-h .ct-area {
|
||||
fill: #6b0392; }
|
||||
|
||||
.ct-series-i .ct-point, .ct-series-i .ct-line, .ct-series-i .ct-bar, .ct-series-i .ct-slice-donut {
|
||||
stroke: #f05b4f; }
|
||||
|
||||
.ct-series-i .ct-slice-pie, .ct-series-i .ct-slice-donut-solid, .ct-series-i .ct-area {
|
||||
fill: #f05b4f; }
|
||||
|
||||
.ct-series-j .ct-point, .ct-series-j .ct-line, .ct-series-j .ct-bar, .ct-series-j .ct-slice-donut {
|
||||
stroke: #dda458; }
|
||||
|
||||
.ct-series-j .ct-slice-pie, .ct-series-j .ct-slice-donut-solid, .ct-series-j .ct-area {
|
||||
fill: #dda458; }
|
||||
|
||||
.ct-series-k .ct-point, .ct-series-k .ct-line, .ct-series-k .ct-bar, .ct-series-k .ct-slice-donut {
|
||||
stroke: #eacf7d; }
|
||||
|
||||
.ct-series-k .ct-slice-pie, .ct-series-k .ct-slice-donut-solid, .ct-series-k .ct-area {
|
||||
fill: #eacf7d; }
|
||||
|
||||
.ct-series-l .ct-point, .ct-series-l .ct-line, .ct-series-l .ct-bar, .ct-series-l .ct-slice-donut {
|
||||
stroke: #86797d; }
|
||||
|
||||
.ct-series-l .ct-slice-pie, .ct-series-l .ct-slice-donut-solid, .ct-series-l .ct-area {
|
||||
fill: #86797d; }
|
||||
|
||||
.ct-series-m .ct-point, .ct-series-m .ct-line, .ct-series-m .ct-bar, .ct-series-m .ct-slice-donut {
|
||||
stroke: #b2c326; }
|
||||
|
||||
.ct-series-m .ct-slice-pie, .ct-series-m .ct-slice-donut-solid, .ct-series-m .ct-area {
|
||||
fill: #b2c326; }
|
||||
|
||||
.ct-series-n .ct-point, .ct-series-n .ct-line, .ct-series-n .ct-bar, .ct-series-n .ct-slice-donut {
|
||||
stroke: #6188e2; }
|
||||
|
||||
.ct-series-n .ct-slice-pie, .ct-series-n .ct-slice-donut-solid, .ct-series-n .ct-area {
|
||||
fill: #6188e2; }
|
||||
|
||||
.ct-series-o .ct-point, .ct-series-o .ct-line, .ct-series-o .ct-bar, .ct-series-o .ct-slice-donut {
|
||||
stroke: #a748ca; }
|
||||
|
||||
.ct-series-o .ct-slice-pie, .ct-series-o .ct-slice-donut-solid, .ct-series-o .ct-area {
|
||||
fill: #a748ca; }
|
||||
|
||||
.ct-square {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-square:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 100%; }
|
||||
.ct-square:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-square > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-minor-second {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-minor-second:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 93.75%; }
|
||||
.ct-minor-second:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-minor-second > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-second {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-second:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 88.8888888889%; }
|
||||
.ct-major-second:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-second > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-minor-third {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-minor-third:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 83.3333333333%; }
|
||||
.ct-minor-third:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-minor-third > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-third {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-third:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 80%; }
|
||||
.ct-major-third:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-third > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-perfect-fourth {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-perfect-fourth:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 75%; }
|
||||
.ct-perfect-fourth:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-perfect-fourth > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-perfect-fifth {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-perfect-fifth:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 66.6666666667%; }
|
||||
.ct-perfect-fifth:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-perfect-fifth > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-minor-sixth {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-minor-sixth:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 62.5%; }
|
||||
.ct-minor-sixth:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-minor-sixth > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-golden-section {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-golden-section:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 61.804697157%; }
|
||||
.ct-golden-section:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-golden-section > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-sixth {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-sixth:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 60%; }
|
||||
.ct-major-sixth:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-sixth > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-minor-seventh {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-minor-seventh:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 56.25%; }
|
||||
.ct-minor-seventh:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-minor-seventh > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-seventh {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-seventh:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 53.3333333333%; }
|
||||
.ct-major-seventh:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-seventh > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-octave {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-octave:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 50%; }
|
||||
.ct-octave:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-octave > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-tenth {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-tenth:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 40%; }
|
||||
.ct-major-tenth:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-tenth > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-eleventh {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-eleventh:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 37.5%; }
|
||||
.ct-major-eleventh:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-eleventh > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-major-twelfth {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-major-twelfth:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 33.3333333333%; }
|
||||
.ct-major-twelfth:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-major-twelfth > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
.ct-double-octave {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.ct-double-octave:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: 25%; }
|
||||
.ct-double-octave:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both; }
|
||||
.ct-double-octave > svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0; }
|
||||
|
||||
/*# sourceMappingURL=chartist.css.map */
|
10
public/chartist/chartist.css.map
Normal file
10
public/chartist/chartist.css.map
Normal file
File diff suppressed because one or more lines are too long
4488
public/chartist/chartist.js
Normal file
4488
public/chartist/chartist.js
Normal file
File diff suppressed because it is too large
Load Diff
1
public/chartist/chartist.min.css
vendored
Normal file
1
public/chartist/chartist.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
10
public/chartist/chartist.min.js
vendored
Normal file
10
public/chartist/chartist.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/chartist/chartist.min.js.map
Normal file
1
public/chartist/chartist.min.js.map
Normal file
File diff suppressed because one or more lines are too long
241
public/chartist/scss/chartist.scss
Normal file
241
public/chartist/scss/chartist.scss
Normal file
@ -0,0 +1,241 @@
|
||||
@import "settings/chartist-settings";
|
||||
|
||||
@mixin ct-responsive-svg-container($width: 100%, $ratio: $ct-container-ratio) {
|
||||
display: block;
|
||||
position: relative;
|
||||
width: $width;
|
||||
|
||||
&:before {
|
||||
display: block;
|
||||
float: left;
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
padding-bottom: $ratio * 100%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
> svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ct-align-justify($ct-text-align: $ct-text-align, $ct-text-justify: $ct-text-justify) {
|
||||
-webkit-box-align: $ct-text-align;
|
||||
-webkit-align-items: $ct-text-align;
|
||||
-ms-flex-align: $ct-text-align;
|
||||
align-items: $ct-text-align;
|
||||
-webkit-box-pack: $ct-text-justify;
|
||||
-webkit-justify-content: $ct-text-justify;
|
||||
-ms-flex-pack: $ct-text-justify;
|
||||
justify-content: $ct-text-justify;
|
||||
// Fallback to text-align for non-flex browsers
|
||||
@if($ct-text-justify == 'flex-start') {
|
||||
text-align: left;
|
||||
} @else if ($ct-text-justify == 'flex-end') {
|
||||
text-align: right;
|
||||
} @else {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ct-flex() {
|
||||
// Fallback to block
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@mixin ct-chart-label($ct-text-color: $ct-text-color, $ct-text-size: $ct-text-size, $ct-text-line-height: $ct-text-line-height) {
|
||||
fill: $ct-text-color;
|
||||
color: $ct-text-color;
|
||||
font-size: $ct-text-size;
|
||||
line-height: $ct-text-line-height;
|
||||
}
|
||||
|
||||
@mixin ct-chart-grid($ct-grid-color: $ct-grid-color, $ct-grid-width: $ct-grid-width, $ct-grid-dasharray: $ct-grid-dasharray) {
|
||||
stroke: $ct-grid-color;
|
||||
stroke-width: $ct-grid-width;
|
||||
|
||||
@if ($ct-grid-dasharray) {
|
||||
stroke-dasharray: $ct-grid-dasharray;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ct-chart-point($ct-point-size: $ct-point-size, $ct-point-shape: $ct-point-shape) {
|
||||
stroke-width: $ct-point-size;
|
||||
stroke-linecap: $ct-point-shape;
|
||||
}
|
||||
|
||||
@mixin ct-chart-line($ct-line-width: $ct-line-width, $ct-line-dasharray: $ct-line-dasharray) {
|
||||
fill: none;
|
||||
stroke-width: $ct-line-width;
|
||||
|
||||
@if ($ct-line-dasharray) {
|
||||
stroke-dasharray: $ct-line-dasharray;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ct-chart-area($ct-area-opacity: $ct-area-opacity) {
|
||||
stroke: none;
|
||||
fill-opacity: $ct-area-opacity;
|
||||
}
|
||||
|
||||
@mixin ct-chart-bar($ct-bar-width: $ct-bar-width) {
|
||||
fill: none;
|
||||
stroke-width: $ct-bar-width;
|
||||
}
|
||||
|
||||
@mixin ct-chart-donut($ct-donut-width: $ct-donut-width) {
|
||||
fill: none;
|
||||
stroke-width: $ct-donut-width;
|
||||
}
|
||||
|
||||
@mixin ct-chart-series-color($color) {
|
||||
.#{$ct-class-point}, .#{$ct-class-line}, .#{$ct-class-bar}, .#{$ct-class-slice-donut} {
|
||||
stroke: $color;
|
||||
}
|
||||
|
||||
.#{$ct-class-slice-pie}, .#{$ct-class-slice-donut-solid}, .#{$ct-class-area} {
|
||||
fill: $color;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ct-chart($ct-container-ratio: $ct-container-ratio, $ct-text-color: $ct-text-color, $ct-text-size: $ct-text-size, $ct-grid-color: $ct-grid-color, $ct-grid-width: $ct-grid-width, $ct-grid-dasharray: $ct-grid-dasharray, $ct-point-size: $ct-point-size, $ct-point-shape: $ct-point-shape, $ct-line-width: $ct-line-width, $ct-bar-width: $ct-bar-width, $ct-donut-width: $ct-donut-width, $ct-series-names: $ct-series-names, $ct-series-colors: $ct-series-colors) {
|
||||
|
||||
.#{$ct-class-label} {
|
||||
@include ct-chart-label($ct-text-color, $ct-text-size);
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-line} .#{$ct-class-label},
|
||||
.#{$ct-class-chart-bar} .#{$ct-class-label} {
|
||||
@include ct-flex();
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-pie} .#{$ct-class-label},
|
||||
.#{$ct-class-chart-donut} .#{$ct-class-label} {
|
||||
dominant-baseline: central;
|
||||
}
|
||||
|
||||
.#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-start} {
|
||||
@include ct-align-justify(flex-end, flex-start);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-end} {
|
||||
@include ct-align-justify(flex-start, flex-start);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-start} {
|
||||
@include ct-align-justify(flex-end, flex-end);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
.#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-end} {
|
||||
@include ct-align-justify(flex-end, flex-start);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-bar} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-start} {
|
||||
@include ct-align-justify(flex-end, center);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-bar} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-end} {
|
||||
@include ct-align-justify(flex-start, center);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-start} {
|
||||
@include ct-align-justify(flex-end, flex-start);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-end} {
|
||||
@include ct-align-justify(flex-start, flex-start);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: start;
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-start} {
|
||||
//@include ct-chart-label($ct-text-color, $ct-text-size, center, $ct-vertical-text-justify);
|
||||
@include ct-align-justify(center, flex-end);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
.#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-end} {
|
||||
@include ct-align-justify(center, flex-start);
|
||||
// Fallback for browsers that don't support foreignObjects
|
||||
text-anchor: end;
|
||||
}
|
||||
|
||||
.#{$ct-class-grid} {
|
||||
@include ct-chart-grid($ct-grid-color, $ct-grid-width, $ct-grid-dasharray);
|
||||
}
|
||||
|
||||
.#{$ct-class-grid-background} {
|
||||
fill: $ct-grid-background-fill;
|
||||
}
|
||||
|
||||
.#{$ct-class-point} {
|
||||
@include ct-chart-point($ct-point-size, $ct-point-shape);
|
||||
}
|
||||
|
||||
.#{$ct-class-line} {
|
||||
@include ct-chart-line($ct-line-width);
|
||||
}
|
||||
|
||||
.#{$ct-class-area} {
|
||||
@include ct-chart-area();
|
||||
}
|
||||
|
||||
.#{$ct-class-bar} {
|
||||
@include ct-chart-bar($ct-bar-width);
|
||||
}
|
||||
|
||||
.#{$ct-class-slice-donut} {
|
||||
@include ct-chart-donut($ct-donut-width);
|
||||
}
|
||||
|
||||
@if $ct-include-colored-series {
|
||||
@for $i from 0 to length($ct-series-names) {
|
||||
.#{$ct-class-series}-#{nth($ct-series-names, $i + 1)} {
|
||||
$color: nth($ct-series-colors, $i + 1);
|
||||
|
||||
@include ct-chart-series-color($color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if $ct-include-classes {
|
||||
@include ct-chart();
|
||||
|
||||
@if $ct-include-alternative-responsive-containers {
|
||||
@for $i from 0 to length($ct-scales-names) {
|
||||
.#{nth($ct-scales-names, $i + 1)} {
|
||||
@include ct-responsive-svg-container($ratio: nth($ct-scales, $i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
88
public/chartist/scss/settings/_chartist-settings.scss
Normal file
88
public/chartist/scss/settings/_chartist-settings.scss
Normal file
@ -0,0 +1,88 @@
|
||||
// Scales for responsive SVG containers
|
||||
$ct-scales: ((1), (15/16), (8/9), (5/6), (4/5), (3/4), (2/3), (5/8), (1/1.618), (3/5), (9/16), (8/15), (1/2), (2/5), (3/8), (1/3), (1/4)) !default;
|
||||
$ct-scales-names: (ct-square, ct-minor-second, ct-major-second, ct-minor-third, ct-major-third, ct-perfect-fourth, ct-perfect-fifth, ct-minor-sixth, ct-golden-section, ct-major-sixth, ct-minor-seventh, ct-major-seventh, ct-octave, ct-major-tenth, ct-major-eleventh, ct-major-twelfth, ct-double-octave) !default;
|
||||
|
||||
// Class names to be used when generating CSS
|
||||
$ct-class-chart: ct-chart !default;
|
||||
$ct-class-chart-line: ct-chart-line !default;
|
||||
$ct-class-chart-bar: ct-chart-bar !default;
|
||||
$ct-class-horizontal-bars: ct-horizontal-bars !default;
|
||||
$ct-class-chart-pie: ct-chart-pie !default;
|
||||
$ct-class-chart-donut: ct-chart-donut !default;
|
||||
$ct-class-label: ct-label !default;
|
||||
$ct-class-series: ct-series !default;
|
||||
$ct-class-line: ct-line !default;
|
||||
$ct-class-point: ct-point !default;
|
||||
$ct-class-area: ct-area !default;
|
||||
$ct-class-bar: ct-bar !default;
|
||||
$ct-class-slice-pie: ct-slice-pie !default;
|
||||
$ct-class-slice-donut: ct-slice-donut !default;
|
||||
$ct-class-slice-donut-solid: ct-slice-donut-solid !default;
|
||||
$ct-class-grid: ct-grid !default;
|
||||
$ct-class-grid-background: ct-grid-background !default;
|
||||
$ct-class-vertical: ct-vertical !default;
|
||||
$ct-class-horizontal: ct-horizontal !default;
|
||||
$ct-class-start: ct-start !default;
|
||||
$ct-class-end: ct-end !default;
|
||||
|
||||
// Container ratio
|
||||
$ct-container-ratio: (1/1.618) !default;
|
||||
|
||||
// Text styles for labels
|
||||
$ct-text-color: rgba(0, 0, 0, 0.4) !default;
|
||||
$ct-text-size: 0.75rem !default;
|
||||
$ct-text-align: flex-start !default;
|
||||
$ct-text-justify: flex-start !default;
|
||||
$ct-text-line-height: 1;
|
||||
|
||||
// Grid styles
|
||||
$ct-grid-color: rgba(0, 0, 0, 0.2) !default;
|
||||
$ct-grid-dasharray: 2px !default;
|
||||
$ct-grid-width: 1px !default;
|
||||
$ct-grid-background-fill: none !default;
|
||||
|
||||
// Line chart properties
|
||||
$ct-line-width: 4px !default;
|
||||
$ct-line-dasharray: false !default;
|
||||
$ct-point-size: 10px !default;
|
||||
// Line chart point, can be either round or square
|
||||
$ct-point-shape: round !default;
|
||||
// Area fill transparency between 0 and 1
|
||||
$ct-area-opacity: 0.1 !default;
|
||||
|
||||
// Bar chart bar width
|
||||
$ct-bar-width: 10px !default;
|
||||
|
||||
// Donut width (If donut width is to big it can cause issues where the shape gets distorted)
|
||||
$ct-donut-width: 60px !default;
|
||||
|
||||
// If set to true it will include the default classes and generate CSS output. If you're planning to use the mixins you
|
||||
// should set this property to false
|
||||
$ct-include-classes: true !default;
|
||||
|
||||
// If this is set to true the CSS will contain colored series. You can extend or change the color with the
|
||||
// properties below
|
||||
$ct-include-colored-series: $ct-include-classes !default;
|
||||
|
||||
// If set to true this will include all responsive container variations using the scales defined at the top of the script
|
||||
$ct-include-alternative-responsive-containers: $ct-include-classes !default;
|
||||
|
||||
// Series names and colors. This can be extended or customized as desired. Just add more series and colors.
|
||||
$ct-series-names: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) !default;
|
||||
$ct-series-colors: (
|
||||
#d70206,
|
||||
#f05b4f,
|
||||
#f4c63d,
|
||||
#d17905,
|
||||
#453d3f,
|
||||
#59922b,
|
||||
#0544d3,
|
||||
#6b0392,
|
||||
#f05b4f,
|
||||
#dda458,
|
||||
#eacf7d,
|
||||
#86797d,
|
||||
#b2c326,
|
||||
#6188e2,
|
||||
#a748ca
|
||||
) !default;
|
@ -122,6 +122,9 @@ func (count *accCountBuilder) Limit(limit string) *accCountBuilder {
|
||||
return count
|
||||
}
|
||||
|
||||
// TODO: Add QueryRow for this and use it in statistics.go
|
||||
func (count *accCountBuilder) Prepare() *sql.Stmt {
|
||||
return count.build.SimpleCount(count.table, count.where, count.limit)
|
||||
}
|
||||
|
||||
// TODO: Add a Sum builder for summing viewchunks up into one number for the dashboard?
|
||||
|
@ -89,6 +89,8 @@ func buildPanelRoutes() {
|
||||
View("routePanelUsersEdit", "/panel/users/edit/", "extraData"),
|
||||
Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"),
|
||||
|
||||
View("routePanelAnalyticsViews", "/panel/analytics/views/"),
|
||||
|
||||
View("routePanelGroups", "/panel/groups/"),
|
||||
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),
|
||||
View("routePanelGroupsEditPerms", "/panel/groups/edit/perms/", "extraData"),
|
||||
|
@ -624,6 +624,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||
var replyList []common.ReplyUser
|
||||
|
||||
// SEO URLs...
|
||||
// TODO: Do a 301 if it's the wrong username? Do a canonical too?
|
||||
halves := strings.Split(r.URL.Path[len("/user/"):], ".")
|
||||
if len(halves) < 2 {
|
||||
halves = append(halves, halves[0])
|
||||
|
163
template_list.go
163
template_list.go
@ -137,111 +137,115 @@ var topic_26 = []byte(`</p>
|
||||
<textarea name="topic_content" class="show_on_edit topic_content_input">`)
|
||||
var topic_27 = []byte(`</textarea>
|
||||
|
||||
<span class="controls">
|
||||
<span class="controls" aria-label="Controls and Author Information">
|
||||
|
||||
<a href="`)
|
||||
var topic_28 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_29 = []byte(`</a>
|
||||
`)
|
||||
var topic_30 = []byte(`<a href="/topic/like/submit/`)
|
||||
var topic_31 = []byte(`" class="mod_button" title="Love it" style="color:#202020;">
|
||||
var topic_31 = []byte(`" class="mod_button" title="Love it" `)
|
||||
var topic_32 = []byte(`aria-label="Unlike this topic"`)
|
||||
var topic_33 = []byte(`aria-label="Like this topic"`)
|
||||
var topic_34 = []byte(` style="color:#202020;">
|
||||
<button class="username like_label"`)
|
||||
var topic_32 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_33 = []byte(`></button></a>`)
|
||||
var topic_34 = []byte(`<a href='/topic/edit/`)
|
||||
var topic_35 = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic"><button class="username edit_label"></button></a>`)
|
||||
var topic_36 = []byte(`<a href='/topic/delete/submit/`)
|
||||
var topic_37 = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username trash_label"></button></a>`)
|
||||
var topic_38 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
|
||||
var topic_39 = []byte(`' style="font-weight:normal;" title="Unlock Topic"><button class="username unlock_label"></button></a>`)
|
||||
var topic_40 = []byte(`<a href='/topic/lock/submit/`)
|
||||
var topic_41 = []byte(`' class="mod_button" style="font-weight:normal;" title="Lock Topic"><button class="username lock_label"></button></a>`)
|
||||
var topic_42 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
|
||||
var topic_43 = []byte(`' style="font-weight:normal;" title="Unpin Topic"><button class="username unpin_label"></button></a>`)
|
||||
var topic_44 = []byte(`<a href='/topic/stick/submit/`)
|
||||
var topic_45 = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username pin_label"></button></a>`)
|
||||
var topic_46 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_47 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
|
||||
var topic_48 = []byte(`
|
||||
var topic_35 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_36 = []byte(`></button></a>`)
|
||||
var topic_37 = []byte(`<a href='/topic/edit/`)
|
||||
var topic_38 = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>`)
|
||||
var topic_39 = []byte(`<a href='/topic/delete/submit/`)
|
||||
var topic_40 = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>`)
|
||||
var topic_41 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
|
||||
var topic_42 = []byte(`' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>`)
|
||||
var topic_43 = []byte(`<a href='/topic/lock/submit/`)
|
||||
var topic_44 = []byte(`' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>`)
|
||||
var topic_45 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
|
||||
var topic_46 = []byte(`' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>`)
|
||||
var topic_47 = []byte(`<a href='/topic/stick/submit/`)
|
||||
var topic_48 = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>`)
|
||||
var topic_49 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_50 = []byte(`' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is `)
|
||||
var topic_51 = []byte(`"><button class="username ip_label"></button></a>`)
|
||||
var topic_52 = []byte(`
|
||||
<a href="/report/submit/`)
|
||||
var topic_49 = []byte(`?session=`)
|
||||
var topic_50 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username flag_label"></button></a>
|
||||
var topic_53 = []byte(`?session=`)
|
||||
var topic_54 = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
|
||||
|
||||
`)
|
||||
var topic_51 = []byte(`<a class="username hide_on_micro like_count">`)
|
||||
var topic_52 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_53 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_54 = []byte(`</a>`)
|
||||
var topic_55 = []byte(`<a class="username hide_on_micro level">`)
|
||||
var topic_56 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_57 = []byte(`
|
||||
var topic_55 = []byte(`<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">`)
|
||||
var topic_56 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_57 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_58 = []byte(`</a>`)
|
||||
var topic_59 = []byte(`<a class="username hide_on_micro level" aria-label="The poster's level">`)
|
||||
var topic_60 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_61 = []byte(`
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="rowblock post_container" aria-label="The current page for this topic" style="overflow: hidden;">`)
|
||||
var topic_58 = []byte(`
|
||||
var topic_62 = []byte(`
|
||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item action_item">
|
||||
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
|
||||
var topic_59 = []byte(`</span>
|
||||
var topic_63 = []byte(`</span>
|
||||
<span itemprop="text">`)
|
||||
var topic_60 = []byte(`</span>
|
||||
var topic_64 = []byte(`</span>
|
||||
</article>
|
||||
`)
|
||||
var topic_61 = []byte(`
|
||||
var topic_65 = []byte(`
|
||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item `)
|
||||
var topic_62 = []byte(`" style="background-image: url(`)
|
||||
var topic_63 = []byte(`), url(/static/`)
|
||||
var topic_64 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_65 = []byte(`-1`)
|
||||
var topic_66 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
var topic_66 = []byte(`" style="background-image: url(`)
|
||||
var topic_67 = []byte(`), url(/static/`)
|
||||
var topic_68 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||
var topic_69 = []byte(`-1`)
|
||||
var topic_70 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
||||
`)
|
||||
var topic_67 = []byte(`
|
||||
var topic_71 = []byte(`
|
||||
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">`)
|
||||
var topic_68 = []byte(`</p>
|
||||
var topic_72 = []byte(`</p>
|
||||
|
||||
<span class="controls">
|
||||
|
||||
<a href="`)
|
||||
var topic_69 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_70 = []byte(`</a>
|
||||
var topic_73 = []byte(`" class="username real_username" rel="author">`)
|
||||
var topic_74 = []byte(`</a>
|
||||
`)
|
||||
var topic_71 = []byte(`<a href="/reply/like/submit/`)
|
||||
var topic_72 = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"`)
|
||||
var topic_73 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_74 = []byte(`></button></a>`)
|
||||
var topic_75 = []byte(`<a href="/reply/edit/submit/`)
|
||||
var topic_76 = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>`)
|
||||
var topic_77 = []byte(`<a href="/reply/delete/submit/`)
|
||||
var topic_78 = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
|
||||
var topic_79 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_80 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
|
||||
var topic_81 = []byte(`
|
||||
var topic_75 = []byte(`<a href="/reply/like/submit/`)
|
||||
var topic_76 = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username like_label"`)
|
||||
var topic_77 = []byte(` style="background-color:#D6FFD6;"`)
|
||||
var topic_78 = []byte(`></button></a>`)
|
||||
var topic_79 = []byte(`<a href="/reply/edit/submit/`)
|
||||
var topic_80 = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item edit_label"></button></a>`)
|
||||
var topic_81 = []byte(`<a href="/reply/delete/submit/`)
|
||||
var topic_82 = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>`)
|
||||
var topic_83 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
||||
var topic_84 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
|
||||
var topic_85 = []byte(`
|
||||
<a href="/report/submit/`)
|
||||
var topic_82 = []byte(`?session=`)
|
||||
var topic_83 = []byte(`&type=reply" class="mod_button report_item" title="Flag Reply"><button class="username report_item flag_label"></button></a>
|
||||
var topic_86 = []byte(`?session=`)
|
||||
var topic_87 = []byte(`&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
|
||||
|
||||
`)
|
||||
var topic_84 = []byte(`<a class="username hide_on_micro like_count">`)
|
||||
var topic_85 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_86 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_87 = []byte(`</a>`)
|
||||
var topic_88 = []byte(`<a class="username hide_on_micro level">`)
|
||||
var topic_89 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_90 = []byte(`
|
||||
var topic_88 = []byte(`<a class="username hide_on_micro like_count">`)
|
||||
var topic_89 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
||||
var topic_90 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||
var topic_91 = []byte(`</a>`)
|
||||
var topic_92 = []byte(`<a class="username hide_on_micro level">`)
|
||||
var topic_93 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||
var topic_94 = []byte(`
|
||||
|
||||
</span>
|
||||
</article>
|
||||
`)
|
||||
var topic_91 = []byte(`</div>
|
||||
var topic_95 = []byte(`</div>
|
||||
|
||||
`)
|
||||
var topic_92 = []byte(`
|
||||
var topic_96 = []byte(`
|
||||
<div class="rowblock topic_reply_form quick_create_form">
|
||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||
<input form="reply_form" name="tid" value='`)
|
||||
var topic_93 = []byte(`' type="hidden" />
|
||||
var topic_97 = []byte(`' type="hidden" />
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem">
|
||||
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
|
||||
@ -251,16 +255,16 @@ var topic_93 = []byte(`' type="hidden" />
|
||||
<div class="formitem">
|
||||
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
|
||||
`)
|
||||
var topic_94 = []byte(`
|
||||
var topic_98 = []byte(`
|
||||
<input name="upload_files" form="reply_form" id="upload_files" multiple type="file" style="display: none;" />
|
||||
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||
<div id="upload_file_dock"></div>`)
|
||||
var topic_95 = []byte(`
|
||||
var topic_99 = []byte(`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var topic_96 = []byte(`
|
||||
var topic_100 = []byte(`
|
||||
|
||||
</main>
|
||||
|
||||
@ -719,23 +723,24 @@ window.addEventListener("hashchange", handle_profile_hashbit, false)
|
||||
|
||||
`)
|
||||
var forums_0 = []byte(`
|
||||
<main>
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock opthead">
|
||||
<div class="rowitem"><h1>Forums</h1></div>
|
||||
<div class="rowitem"><h1 itemprop="name">Forums</h1></div>
|
||||
</div>
|
||||
<div class="rowblock forum_list">
|
||||
`)
|
||||
var forums_1 = []byte(`<div class="rowitem `)
|
||||
var forums_2 = []byte(`datarow`)
|
||||
var forums_3 = []byte(`">
|
||||
var forums_2 = []byte(`datarow `)
|
||||
var forums_3 = []byte(`"itemprop="itemListElement" itemscope
|
||||
itemtype="http://schema.org/ListItem">
|
||||
<span class="forum_left shift_left">
|
||||
<a href="`)
|
||||
var forums_4 = []byte(`">`)
|
||||
var forums_4 = []byte(`" itemprop="item">`)
|
||||
var forums_5 = []byte(`</a>
|
||||
`)
|
||||
var forums_6 = []byte(`
|
||||
<br /><span class="rowsmall">`)
|
||||
<br /><span class="rowsmall" itemprop="description">`)
|
||||
var forums_7 = []byte(`</span>
|
||||
`)
|
||||
var forums_8 = []byte(`
|
||||
@ -772,12 +777,12 @@ var forums_22 = []byte(`
|
||||
</main>
|
||||
`)
|
||||
var topics_0 = []byte(`
|
||||
<main>
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
|
||||
<div class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem topic_list_title`)
|
||||
var topics_1 = []byte(` has_opt`)
|
||||
var topics_2 = []byte(`"><h1>All Topics</h1></div>
|
||||
var topics_2 = []byte(`"><h1 itemprop="name">All Topics</h1></div>
|
||||
`)
|
||||
var topics_3 = []byte(`
|
||||
<div class="pre_opt auto_hide"></div>
|
||||
@ -874,7 +879,7 @@ var topics_27 = []byte(`'s Avatar" title="`)
|
||||
var topics_28 = []byte(`'s Avatar" /></a>
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var topics_29 = []byte(`"><span>`)
|
||||
var topics_29 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var topics_30 = []byte(`</span></a> `)
|
||||
var topics_31 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||
var topics_32 = []byte(`">`)
|
||||
@ -932,12 +937,12 @@ var forum_6 = []byte(`?page=`)
|
||||
var forum_7 = []byte(`">></a></div>`)
|
||||
var forum_8 = []byte(`
|
||||
|
||||
<main>
|
||||
<main itemscope itemtype="http://schema.org/ItemList">
|
||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||
<div class="rowitem forum_title`)
|
||||
var forum_9 = []byte(` has_opt`)
|
||||
var forum_10 = []byte(`">
|
||||
<h1>`)
|
||||
<h1 itemprop="name">`)
|
||||
var forum_11 = []byte(`</h1>
|
||||
</div>
|
||||
`)
|
||||
@ -1026,7 +1031,7 @@ var forum_32 = []byte(`'s Avatar" title="`)
|
||||
var forum_33 = []byte(`'s Avatar" /></a>
|
||||
<span class="topic_inner_left">
|
||||
<a class="rowtopic" href="`)
|
||||
var forum_34 = []byte(`"><span>`)
|
||||
var forum_34 = []byte(`" itemprop="itemListElement"><span>`)
|
||||
var forum_35 = []byte(`</span></a>
|
||||
<br /><a class="rowsmall starter" href="`)
|
||||
var forum_36 = []byte(`">`)
|
||||
|
@ -3,9 +3,9 @@
|
||||
// Code generated by Gosora. More below:
|
||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
@ -149,151 +149,159 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_31)
|
||||
if tmpl_topic_vars.Topic.Liked {
|
||||
w.Write(topic_32)
|
||||
}
|
||||
} else {
|
||||
w.Write(topic_33)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
|
||||
w.Write(topic_34)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
if tmpl_topic_vars.Topic.Liked {
|
||||
w.Write(topic_35)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
|
||||
w.Write(topic_36)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
|
||||
w.Write(topic_37)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_38)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
|
||||
w.Write(topic_39)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_40)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.CloseTopic {
|
||||
if tmpl_topic_vars.Topic.IsClosed {
|
||||
w.Write(topic_38)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_39)
|
||||
} else {
|
||||
w.Write(topic_40)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_41)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_42)
|
||||
} else {
|
||||
w.Write(topic_43)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_44)
|
||||
}
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.PinTopic {
|
||||
if tmpl_topic_vars.Topic.Sticky {
|
||||
w.Write(topic_42)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_43)
|
||||
} else {
|
||||
w.Write(topic_44)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_45)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_46)
|
||||
} else {
|
||||
w.Write(topic_47)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_48)
|
||||
}
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
||||
w.Write(topic_46)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_47)
|
||||
}
|
||||
w.Write(topic_48)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_49)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_50)
|
||||
if tmpl_topic_vars.Topic.LikeCount > 0 {
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||
w.Write(topic_51)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
|
||||
w.Write(topic_52)
|
||||
}
|
||||
if tmpl_topic_vars.Topic.Tag != "" {
|
||||
w.Write(topic_52)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_53)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_54)
|
||||
} else {
|
||||
if tmpl_topic_vars.Topic.LikeCount > 0 {
|
||||
w.Write(topic_55)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.LikeCount)))
|
||||
w.Write(topic_56)
|
||||
}
|
||||
if tmpl_topic_vars.Topic.Tag != "" {
|
||||
w.Write(topic_57)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
|
||||
w.Write(topic_58)
|
||||
} else {
|
||||
w.Write(topic_59)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.Level)))
|
||||
w.Write(topic_60)
|
||||
}
|
||||
w.Write(topic_61)
|
||||
if len(tmpl_topic_vars.ItemList) != 0 {
|
||||
for _, item := range tmpl_topic_vars.ItemList {
|
||||
if item.ActionType != "" {
|
||||
w.Write(topic_58)
|
||||
w.Write([]byte(item.ActionIcon))
|
||||
w.Write(topic_59)
|
||||
w.Write([]byte(item.ActionType))
|
||||
w.Write(topic_60)
|
||||
} else {
|
||||
w.Write(topic_61)
|
||||
w.Write([]byte(item.ClassName))
|
||||
w.Write(topic_62)
|
||||
w.Write([]byte(item.Avatar))
|
||||
w.Write([]byte(item.ActionIcon))
|
||||
w.Write(topic_63)
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write([]byte(item.ActionType))
|
||||
w.Write(topic_64)
|
||||
if item.ContentLines <= 5 {
|
||||
} else {
|
||||
w.Write(topic_65)
|
||||
}
|
||||
w.Write([]byte(item.ClassName))
|
||||
w.Write(topic_66)
|
||||
w.Write([]byte(item.Avatar))
|
||||
w.Write(topic_67)
|
||||
w.Write([]byte(item.ContentHtml))
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||
w.Write(topic_68)
|
||||
w.Write([]byte(item.UserLink))
|
||||
if item.ContentLines <= 5 {
|
||||
w.Write(topic_69)
|
||||
w.Write([]byte(item.CreatedByName))
|
||||
}
|
||||
w.Write(topic_70)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||
w.Write(topic_71)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write([]byte(item.ContentHtml))
|
||||
w.Write(topic_72)
|
||||
if item.Liked {
|
||||
w.Write([]byte(item.UserLink))
|
||||
w.Write(topic_73)
|
||||
}
|
||||
w.Write([]byte(item.CreatedByName))
|
||||
w.Write(topic_74)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditReply {
|
||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||
w.Write(topic_75)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_76)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
|
||||
if item.Liked {
|
||||
w.Write(topic_77)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
}
|
||||
w.Write(topic_78)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
||||
if tmpl_topic_vars.CurrentUser.Perms.EditReply {
|
||||
w.Write(topic_79)
|
||||
w.Write([]byte(item.IPAddress))
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_80)
|
||||
}
|
||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
|
||||
w.Write(topic_81)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_82)
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_83)
|
||||
if item.LikeCount > 0 {
|
||||
w.Write(topic_84)
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topic_85)
|
||||
}
|
||||
if item.Tag != "" {
|
||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
||||
w.Write(topic_83)
|
||||
w.Write([]byte(item.IPAddress))
|
||||
w.Write(topic_84)
|
||||
}
|
||||
w.Write(topic_85)
|
||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||
w.Write(topic_86)
|
||||
w.Write([]byte(item.Tag))
|
||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||
w.Write(topic_87)
|
||||
} else {
|
||||
if item.LikeCount > 0 {
|
||||
w.Write(topic_88)
|
||||
w.Write([]byte(strconv.Itoa(item.Level)))
|
||||
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||
w.Write(topic_89)
|
||||
}
|
||||
if item.Tag != "" {
|
||||
w.Write(topic_90)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Write([]byte(item.Tag))
|
||||
w.Write(topic_91)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
|
||||
} else {
|
||||
w.Write(topic_92)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write([]byte(strconv.Itoa(item.Level)))
|
||||
w.Write(topic_93)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
|
||||
}
|
||||
w.Write(topic_94)
|
||||
}
|
||||
w.Write(topic_95)
|
||||
}
|
||||
}
|
||||
w.Write(topic_95)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
|
||||
w.Write(topic_96)
|
||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||
w.Write(topic_97)
|
||||
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
|
||||
w.Write(topic_98)
|
||||
}
|
||||
w.Write(topic_99)
|
||||
}
|
||||
w.Write(topic_100)
|
||||
w.Write(footer_0)
|
||||
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
|
||||
w.Write(footer_1)
|
||||
|
12
templates/panel-analytics-views.html
Normal file
12
templates/panel-analytics-views.html
Normal file
@ -0,0 +1,12 @@
|
||||
{{template "header.html" . }}
|
||||
<div class="colstack panel_stack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a>Views</a></div>
|
||||
</div>
|
||||
<div id="panel_analytics" class="colstack_graph_holder">
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
@ -27,25 +27,25 @@
|
||||
<p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">{{.Topic.ContentHTML}}</p>
|
||||
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
|
||||
|
||||
<span class="controls">
|
||||
<span class="controls" aria-label="Controls and Author Information">
|
||||
|
||||
<a href="{{.Topic.UserLink}}" class="username real_username" rel="author">{{.Topic.CreatedByName}}</a>
|
||||
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="mod_button" title="Love it" style="color:#202020;">
|
||||
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="mod_button" title="Love it" {{if .Topic.Liked}}aria-label="Unlike this topic"{{else}}aria-label="Like this topic"{{end}} style="color:#202020;">
|
||||
<button class="username like_label"{{if .Topic.Liked}} style="background-color:#D6FFD6;"{{end}}></button></a>{{end}}
|
||||
|
||||
{{if .CurrentUser.Perms.EditTopic}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic"><button class="username edit_label"></button></a>{{end}}
|
||||
{{if .CurrentUser.Perms.EditTopic}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic" aria-label="Edit this topic"><button class="username edit_label"></button></a>{{end}}
|
||||
|
||||
{{if .CurrentUser.Perms.DeleteTopic}}<a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username trash_label"></button></a>{{end}}
|
||||
{{if .CurrentUser.Perms.DeleteTopic}}<a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic" aria-label="Delete this topic"><button class="username trash_label"></button></a>{{end}}
|
||||
|
||||
{{if .CurrentUser.Perms.CloseTopic}}{{if .Topic.IsClosed}}<a class="mod_button" href='/topic/unlock/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unlock Topic"><button class="username unlock_label"></button></a>{{else}}<a href='/topic/lock/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Lock Topic"><button class="username lock_label"></button></a>{{end}}{{end}}
|
||||
{{if .CurrentUser.Perms.CloseTopic}}{{if .Topic.IsClosed}}<a class="mod_button" href='/topic/unlock/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>{{else}}<a href='/topic/lock/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Lock Topic" aria-label="Lock this topic"><button class="username lock_label"></button></a>{{end}}{{end}}
|
||||
|
||||
{{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic"><button class="username unpin_label"></button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username pin_label"></button></a>{{end}}{{end}}
|
||||
{{if .CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.Topic.IPAddress}}' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>{{end}}
|
||||
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username flag_label"></button></a>
|
||||
{{if .CurrentUser.Perms.PinTopic}}{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic" aria-label="Pin this topic"><button class="username pin_label"></button></a>{{end}}{{end}}
|
||||
{{if .CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.Topic.IPAddress}}' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is {{.Topic.IPAddress}}"><button class="username ip_label"></button></a>{{end}}
|
||||
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag this topic" aria-label="Flag this topic" rel="nofollow"><button class="username flag_label"></button></a>
|
||||
|
||||
{{if .Topic.LikeCount}}<a class="username hide_on_micro like_count">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}}
|
||||
{{if .Topic.LikeCount}}<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}}
|
||||
|
||||
{{if .Topic.Tag}}<a class="username hide_on_micro user_tag">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro level">{{.Topic.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>{{end}}
|
||||
{{if .Topic.Tag}}<a class="username hide_on_micro user_tag">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro level" aria-label="The poster's level">{{.Topic.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>{{end}}
|
||||
|
||||
</span>
|
||||
</div>
|
||||
@ -70,7 +70,7 @@
|
||||
|
||||
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Reply"><button class="username delete_item trash_label"></button></a>{{end}}
|
||||
{{if $.CurrentUser.Perms.ViewIPs}}<a class="mod_button" href='/users/ips/?ip={{.IPAddress}}' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>{{end}}
|
||||
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="Flag Reply"><button class="username report_item flag_label"></button></a>
|
||||
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button report_item" title="Flag this reply" aria-label="Flag this reply" rel="nofollow"><button class="username report_item flag_label"></button></a>
|
||||
|
||||
{{if .LikeCount}}<a class="username hide_on_micro like_count">{{.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>{{end}}
|
||||
|
||||
|
@ -1128,7 +1128,7 @@ select, input, textarea, button {
|
||||
text-align: center;
|
||||
}
|
||||
/* TODO: Move these to panel.css */
|
||||
#dash-version:before, #dash-cpu:before, #dash-ram:before {
|
||||
#dash-version:before, #dash-cpu:before, #dash-ram:before, #dash-totonline:before, #dash-gonline:before, #dash-uonline:before, #dash-reqs:before, #dash-postsperday:before, #dash-topicsperday:before {
|
||||
display: inline-block;
|
||||
background: var(--tinted-background-color);
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
@ -1147,6 +1147,15 @@ select, input, textarea, button {
|
||||
#dash-ram:before {
|
||||
content: "\f233";
|
||||
}
|
||||
#dash-totonline:before, #dash-gonline:before, #dash-uonline:before {
|
||||
content: "\f007";
|
||||
}
|
||||
#dash-reqs:before {
|
||||
content: "\f080";
|
||||
}
|
||||
#dash-postsperday:before, #dash-topicsperday:before {
|
||||
content: "\f27b";
|
||||
}
|
||||
|
||||
@media(min-width: 721px) {
|
||||
.hide_on_big {
|
||||
|
@ -6,6 +6,8 @@
|
||||
}
|
||||
.colstack_left {
|
||||
background-color: hsl(0,0%,90%);
|
||||
margin-top: -0.5px;
|
||||
border-right: 1px solid var(--element-border-color);
|
||||
}
|
||||
.colstack_left .colstack_head {
|
||||
margin-top: -1px;
|
||||
@ -21,9 +23,6 @@
|
||||
.colstack_left .rowmenu .passive:last-child {
|
||||
border-bottom: 0.5px solid var(--element-border-color) !important;
|
||||
}
|
||||
/*.colstack_left > *:not(.colstack_head):last-child {
|
||||
border-bottom: 0.5px solid var(--element-border-color) !important;
|
||||
}*/
|
||||
.submenu {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
@ -121,7 +121,6 @@ func (hub *WSHub) pushAlert(targetUser int, asid int, event string, elementType
|
||||
}
|
||||
|
||||
func (hub *WSHub) pushAlerts(users []int, asid int, event string, elementType string, actorID int, targetUserID int, elementID int) error {
|
||||
//log.Print("In pushAlerts")
|
||||
var wsUsers []*WSUser
|
||||
hub.users.RLock()
|
||||
// We don't want to keep a lock on this for too long, so we'll accept some nil pointers
|
||||
@ -139,18 +138,15 @@ func (hub *WSHub) pushAlerts(users []int, asid int, event string, elementType st
|
||||
continue
|
||||
}
|
||||
|
||||
//log.Print("Building alert")
|
||||
alert, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
//log.Print("Getting WS Writer")
|
||||
w, err := wsUser.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
w.Write([]byte(alert))
|
||||
w.Close()
|
||||
}
|
||||
@ -294,6 +290,26 @@ func adminStatsTicker() {
|
||||
|
||||
var totunit, uunit, gunit string
|
||||
|
||||
lessThanSwitch := func(number int, lowerBound int, midBound int) string {
|
||||
switch {
|
||||
case number < lowerBound:
|
||||
return "stat_green"
|
||||
case number < midBound:
|
||||
return "stat_orange"
|
||||
}
|
||||
return "stat_red"
|
||||
}
|
||||
|
||||
greaterThanSwitch := func(number int, lowerBound int, midBound int) string {
|
||||
switch {
|
||||
case number > midBound:
|
||||
return "stat_green"
|
||||
case number > lowerBound:
|
||||
return "stat_orange"
|
||||
}
|
||||
return "stat_red"
|
||||
}
|
||||
|
||||
AdminStatLoop:
|
||||
for {
|
||||
adminStatsMutex.RLock()
|
||||
@ -308,6 +324,7 @@ AdminStatLoop:
|
||||
uonline := wsHub.userCount()
|
||||
gonline := wsHub.guestCount()
|
||||
totonline := uonline + gonline
|
||||
reqCount := 0
|
||||
|
||||
// It's far more likely that the CPU Usage will change than the other stats, so we'll optimise them separately...
|
||||
noStatUpdates = (uonline == lastUonline && gonline == lastGonline && totonline == lastTotonline)
|
||||
@ -318,29 +335,9 @@ AdminStatLoop:
|
||||
}
|
||||
|
||||
if !noStatUpdates {
|
||||
if totonline > 10 {
|
||||
onlineColour = "stat_green"
|
||||
} else if totonline > 3 {
|
||||
onlineColour = "stat_orange"
|
||||
} else {
|
||||
onlineColour = "stat_red"
|
||||
}
|
||||
|
||||
if gonline > 10 {
|
||||
onlineGuestsColour = "stat_green"
|
||||
} else if gonline > 1 {
|
||||
onlineGuestsColour = "stat_orange"
|
||||
} else {
|
||||
onlineGuestsColour = "stat_red"
|
||||
}
|
||||
|
||||
if uonline > 5 {
|
||||
onlineUsersColour = "stat_green"
|
||||
} else if uonline > 1 {
|
||||
onlineUsersColour = "stat_orange"
|
||||
} else {
|
||||
onlineUsersColour = "stat_red"
|
||||
}
|
||||
onlineColour = greaterThanSwitch(totonline, 3, 10)
|
||||
onlineGuestsColour = greaterThanSwitch(gonline, 1, 10)
|
||||
onlineUsersColour = greaterThanSwitch(uonline, 1, 5)
|
||||
|
||||
totonline, totunit = common.ConvertFriendlyUnit(totonline)
|
||||
uonline, uunit = common.ConvertFriendlyUnit(uonline)
|
||||
@ -384,13 +381,7 @@ AdminStatLoop:
|
||||
ramstr = fmt.Sprintf("%.1f", usedCount) + " / " + totstr + totalUnit
|
||||
|
||||
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
||||
if ramperc < 50 {
|
||||
ramColour = "stat_green"
|
||||
} else if ramperc < 75 {
|
||||
ramColour = "stat_orange"
|
||||
} else {
|
||||
ramColour = "stat_red"
|
||||
}
|
||||
ramColour = lessThanSwitch(int(ramperc), 50, 75)
|
||||
}
|
||||
}
|
||||
|
||||
@ -401,7 +392,6 @@ AdminStatLoop:
|
||||
for watcher := range watchers {
|
||||
w, err := watcher.conn.NextWriter(websocket.TextMessage)
|
||||
if err != nil {
|
||||
//log.Print(err.Error())
|
||||
adminStatsMutex.Lock()
|
||||
delete(adminStatsWatchers, watcher)
|
||||
adminStatsMutex.Unlock()
|
||||
@ -413,10 +403,12 @@ AdminStatLoop:
|
||||
w.Write([]byte("set #dash-totonline <span>" + strconv.Itoa(totonline) + totunit + " online</span>\r"))
|
||||
w.Write([]byte("set #dash-gonline <span>" + strconv.Itoa(gonline) + gunit + " guests online</span>\r"))
|
||||
w.Write([]byte("set #dash-uonline <span>" + strconv.Itoa(uonline) + uunit + " users online</span>\r"))
|
||||
w.Write([]byte("set #dash-reqs <span>" + strconv.Itoa(reqCount) + " reqs / second</span>\r"))
|
||||
|
||||
w.Write([]byte("set-class #dash-totonline grid_item grid_stat " + onlineColour + "\r"))
|
||||
w.Write([]byte("set-class #dash-gonline grid_item grid_stat " + onlineGuestsColour + "\r"))
|
||||
w.Write([]byte("set-class #dash-uonline grid_item grid_stat " + onlineUsersColour + "\r"))
|
||||
//w.Write([]byte("set-class #dash-reqs grid_item grid_stat grid_end_group \r"))
|
||||
}
|
||||
|
||||
w.Write([]byte("set #dash-cpu <span>CPU: " + cpustr + "%</span>\r"))
|
||||
|
Loading…
Reference in New Issue
Block a user