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_forums": nil,
|
||||||
"pre_render_panel_delete_forum": nil,
|
"pre_render_panel_delete_forum": nil,
|
||||||
"pre_render_panel_edit_forum": nil,
|
"pre_render_panel_edit_forum": nil,
|
||||||
|
"pre_render_panel_analytics": nil,
|
||||||
"pre_render_panel_settings": nil,
|
"pre_render_panel_settings": nil,
|
||||||
"pre_render_panel_setting": nil,
|
"pre_render_panel_setting": nil,
|
||||||
"pre_render_panel_word_filters": nil,
|
"pre_render_panel_word_filters": nil,
|
||||||
|
|
|
@ -3,6 +3,7 @@ package common
|
||||||
import (
|
import (
|
||||||
//"fmt"
|
//"fmt"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"html"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -164,6 +165,7 @@ func shortcodeToUnicode(msg string) string {
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Write a test for this
|
||||||
func PreparseMessage(msg string) string {
|
func PreparseMessage(msg string) string {
|
||||||
msg = strings.Replace(msg, "<p><br>", "\n\n", -1)
|
msg = strings.Replace(msg, "<p><br>", "\n\n", -1)
|
||||||
msg = strings.Replace(msg, "<p>", "\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 {
|
if Sshooks["preparse_preassign"] != nil {
|
||||||
msg = RunSshook("preparse_preassign", msg)
|
msg = RunSshook("preparse_preassign", msg)
|
||||||
}
|
}
|
||||||
|
msg = html.EscapeString(msg)
|
||||||
return shortcodeToUnicode(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, uidBit...)
|
||||||
outbytes = append(outbytes, UrlClose...)
|
outbytes = append(outbytes, UrlClose...)
|
||||||
lastItem = i
|
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' {
|
} else if msgbytes[i] == 'h' || msgbytes[i] == 'f' || msgbytes[i] == 'g' {
|
||||||
//log.Print("IN hfg")
|
//log.Print("IN hfg")
|
||||||
if msgbytes[i+1] == 't' && msgbytes[i+2] == 't' && msgbytes[i+3] == 'p' {
|
if msgbytes[i+1] == 't' && msgbytes[i+2] == 't' && msgbytes[i+3] == 'p' {
|
||||||
|
|
|
@ -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 (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"html"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -238,7 +237,7 @@ func (topic *Topic) Delete() error {
|
||||||
|
|
||||||
func (topic *Topic) Update(name string, content string) error {
|
func (topic *Topic) Update(name string, content string) error {
|
||||||
content = PreparseMessage(content)
|
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)
|
_, err := topicStmts.edit.Exec(name, content, parsedContent, topic.ID)
|
||||||
topic.cacheRemove()
|
topic.cacheRemove()
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -254,6 +254,7 @@ func RouteCreateGuildSubmit(w http.ResponseWriter, r *http.Request, user common.
|
||||||
|
|
||||||
var guildActive = true
|
var guildActive = true
|
||||||
var guildName = html.EscapeString(r.PostFormValue("group_name"))
|
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 guildDesc = html.EscapeString(r.PostFormValue("group_desc"))
|
||||||
var gprivacy = r.PostFormValue("group_privacy")
|
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,
|
"routePanelUsers": routePanelUsers,
|
||||||
"routePanelUsersEdit": routePanelUsersEdit,
|
"routePanelUsersEdit": routePanelUsersEdit,
|
||||||
"routePanelUsersEditSubmit": routePanelUsersEditSubmit,
|
"routePanelUsersEditSubmit": routePanelUsersEditSubmit,
|
||||||
|
"routePanelAnalyticsViews": routePanelAnalyticsViews,
|
||||||
"routePanelGroups": routePanelGroups,
|
"routePanelGroups": routePanelGroups,
|
||||||
"routePanelGroupsEdit": routePanelGroupsEdit,
|
"routePanelGroupsEdit": routePanelGroupsEdit,
|
||||||
"routePanelGroupsEditPerms": routePanelGroupsEditPerms,
|
"routePanelGroupsEditPerms": routePanelGroupsEditPerms,
|
||||||
|
@ -112,31 +113,32 @@ var routeMapEnum = map[string]int{
|
||||||
"routePanelUsers": 31,
|
"routePanelUsers": 31,
|
||||||
"routePanelUsersEdit": 32,
|
"routePanelUsersEdit": 32,
|
||||||
"routePanelUsersEditSubmit": 33,
|
"routePanelUsersEditSubmit": 33,
|
||||||
"routePanelGroups": 34,
|
"routePanelAnalyticsViews": 34,
|
||||||
"routePanelGroupsEdit": 35,
|
"routePanelGroups": 35,
|
||||||
"routePanelGroupsEditPerms": 36,
|
"routePanelGroupsEdit": 36,
|
||||||
"routePanelGroupsEditSubmit": 37,
|
"routePanelGroupsEditPerms": 37,
|
||||||
"routePanelGroupsEditPermsSubmit": 38,
|
"routePanelGroupsEditSubmit": 38,
|
||||||
"routePanelGroupsCreateSubmit": 39,
|
"routePanelGroupsEditPermsSubmit": 39,
|
||||||
"routePanelBackups": 40,
|
"routePanelGroupsCreateSubmit": 40,
|
||||||
"routePanelLogsMod": 41,
|
"routePanelBackups": 41,
|
||||||
"routePanelDebug": 42,
|
"routePanelLogsMod": 42,
|
||||||
"routePanel": 43,
|
"routePanelDebug": 43,
|
||||||
"routeAccountEditCritical": 44,
|
"routePanel": 44,
|
||||||
"routeAccountEditCriticalSubmit": 45,
|
"routeAccountEditCritical": 45,
|
||||||
"routeAccountEditAvatar": 46,
|
"routeAccountEditCriticalSubmit": 46,
|
||||||
"routeAccountEditAvatarSubmit": 47,
|
"routeAccountEditAvatar": 47,
|
||||||
"routeAccountEditUsername": 48,
|
"routeAccountEditAvatarSubmit": 48,
|
||||||
"routeAccountEditUsernameSubmit": 49,
|
"routeAccountEditUsername": 49,
|
||||||
"routeAccountEditEmail": 50,
|
"routeAccountEditUsernameSubmit": 50,
|
||||||
"routeAccountEditEmailTokenSubmit": 51,
|
"routeAccountEditEmail": 51,
|
||||||
"routeProfile": 52,
|
"routeAccountEditEmailTokenSubmit": 52,
|
||||||
"routeBanSubmit": 53,
|
"routeProfile": 53,
|
||||||
"routeUnban": 54,
|
"routeBanSubmit": 54,
|
||||||
"routeActivate": 55,
|
"routeUnban": 55,
|
||||||
"routeIps": 56,
|
"routeActivate": 56,
|
||||||
"routeDynamic": 57,
|
"routeIps": 57,
|
||||||
"routeUploads": 58,
|
"routeDynamic": 58,
|
||||||
|
"routeUploads": 59,
|
||||||
}
|
}
|
||||||
var reverseRouteMapEnum = map[int]string{
|
var reverseRouteMapEnum = map[int]string{
|
||||||
0: "routeAPI",
|
0: "routeAPI",
|
||||||
|
@ -173,31 +175,32 @@ var reverseRouteMapEnum = map[int]string{
|
||||||
31: "routePanelUsers",
|
31: "routePanelUsers",
|
||||||
32: "routePanelUsersEdit",
|
32: "routePanelUsersEdit",
|
||||||
33: "routePanelUsersEditSubmit",
|
33: "routePanelUsersEditSubmit",
|
||||||
34: "routePanelGroups",
|
34: "routePanelAnalyticsViews",
|
||||||
35: "routePanelGroupsEdit",
|
35: "routePanelGroups",
|
||||||
36: "routePanelGroupsEditPerms",
|
36: "routePanelGroupsEdit",
|
||||||
37: "routePanelGroupsEditSubmit",
|
37: "routePanelGroupsEditPerms",
|
||||||
38: "routePanelGroupsEditPermsSubmit",
|
38: "routePanelGroupsEditSubmit",
|
||||||
39: "routePanelGroupsCreateSubmit",
|
39: "routePanelGroupsEditPermsSubmit",
|
||||||
40: "routePanelBackups",
|
40: "routePanelGroupsCreateSubmit",
|
||||||
41: "routePanelLogsMod",
|
41: "routePanelBackups",
|
||||||
42: "routePanelDebug",
|
42: "routePanelLogsMod",
|
||||||
43: "routePanel",
|
43: "routePanelDebug",
|
||||||
44: "routeAccountEditCritical",
|
44: "routePanel",
|
||||||
45: "routeAccountEditCriticalSubmit",
|
45: "routeAccountEditCritical",
|
||||||
46: "routeAccountEditAvatar",
|
46: "routeAccountEditCriticalSubmit",
|
||||||
47: "routeAccountEditAvatarSubmit",
|
47: "routeAccountEditAvatar",
|
||||||
48: "routeAccountEditUsername",
|
48: "routeAccountEditAvatarSubmit",
|
||||||
49: "routeAccountEditUsernameSubmit",
|
49: "routeAccountEditUsername",
|
||||||
50: "routeAccountEditEmail",
|
50: "routeAccountEditUsernameSubmit",
|
||||||
51: "routeAccountEditEmailTokenSubmit",
|
51: "routeAccountEditEmail",
|
||||||
52: "routeProfile",
|
52: "routeAccountEditEmailTokenSubmit",
|
||||||
53: "routeBanSubmit",
|
53: "routeProfile",
|
||||||
54: "routeUnban",
|
54: "routeBanSubmit",
|
||||||
55: "routeActivate",
|
55: "routeUnban",
|
||||||
56: "routeIps",
|
56: "routeActivate",
|
||||||
57: "routeDynamic",
|
57: "routeIps",
|
||||||
58: "routeUploads",
|
58: "routeDynamic",
|
||||||
|
59: "routeUploads",
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Stop spilling these into the package scope?
|
// 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)
|
common.RouteViewCounter.Bump(33)
|
||||||
err = routePanelUsersEditSubmit(w,req,user,extraData)
|
err = routePanelUsersEditSubmit(w,req,user,extraData)
|
||||||
case "/panel/groups/":
|
case "/panel/analytics/views/":
|
||||||
common.RouteViewCounter.Bump(34)
|
common.RouteViewCounter.Bump(34)
|
||||||
|
err = routePanelAnalyticsViews(w,req,user)
|
||||||
|
case "/panel/groups/":
|
||||||
|
common.RouteViewCounter.Bump(35)
|
||||||
err = routePanelGroups(w,req,user)
|
err = routePanelGroups(w,req,user)
|
||||||
case "/panel/groups/edit/":
|
case "/panel/groups/edit/":
|
||||||
common.RouteViewCounter.Bump(35)
|
common.RouteViewCounter.Bump(36)
|
||||||
err = routePanelGroupsEdit(w,req,user,extraData)
|
err = routePanelGroupsEdit(w,req,user,extraData)
|
||||||
case "/panel/groups/edit/perms/":
|
case "/panel/groups/edit/perms/":
|
||||||
common.RouteViewCounter.Bump(36)
|
common.RouteViewCounter.Bump(37)
|
||||||
err = routePanelGroupsEditPerms(w,req,user,extraData)
|
err = routePanelGroupsEditPerms(w,req,user,extraData)
|
||||||
case "/panel/groups/edit/submit/":
|
case "/panel/groups/edit/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -581,7 +587,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(37)
|
common.RouteViewCounter.Bump(38)
|
||||||
err = routePanelGroupsEditSubmit(w,req,user,extraData)
|
err = routePanelGroupsEditSubmit(w,req,user,extraData)
|
||||||
case "/panel/groups/edit/perms/submit/":
|
case "/panel/groups/edit/perms/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -590,7 +596,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(38)
|
common.RouteViewCounter.Bump(39)
|
||||||
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
|
err = routePanelGroupsEditPermsSubmit(w,req,user,extraData)
|
||||||
case "/panel/groups/create/":
|
case "/panel/groups/create/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -599,7 +605,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(39)
|
common.RouteViewCounter.Bump(40)
|
||||||
err = routePanelGroupsCreateSubmit(w,req,user)
|
err = routePanelGroupsCreateSubmit(w,req,user)
|
||||||
case "/panel/backups/":
|
case "/panel/backups/":
|
||||||
err = common.SuperAdminOnly(w,req,user)
|
err = common.SuperAdminOnly(w,req,user)
|
||||||
|
@ -608,10 +614,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(40)
|
common.RouteViewCounter.Bump(41)
|
||||||
err = routePanelBackups(w,req,user,extraData)
|
err = routePanelBackups(w,req,user,extraData)
|
||||||
case "/panel/logs/mod/":
|
case "/panel/logs/mod/":
|
||||||
common.RouteViewCounter.Bump(41)
|
common.RouteViewCounter.Bump(42)
|
||||||
err = routePanelLogsMod(w,req,user)
|
err = routePanelLogsMod(w,req,user)
|
||||||
case "/panel/debug/":
|
case "/panel/debug/":
|
||||||
err = common.AdminOnly(w,req,user)
|
err = common.AdminOnly(w,req,user)
|
||||||
|
@ -620,10 +626,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(42)
|
common.RouteViewCounter.Bump(43)
|
||||||
err = routePanelDebug(w,req,user)
|
err = routePanelDebug(w,req,user)
|
||||||
default:
|
default:
|
||||||
common.RouteViewCounter.Bump(43)
|
common.RouteViewCounter.Bump(44)
|
||||||
err = routePanel(w,req,user)
|
err = routePanel(w,req,user)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -638,7 +644,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(44)
|
common.RouteViewCounter.Bump(45)
|
||||||
err = routeAccountEditCritical(w,req,user)
|
err = routeAccountEditCritical(w,req,user)
|
||||||
case "/user/edit/critical/submit/":
|
case "/user/edit/critical/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -653,7 +659,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(45)
|
common.RouteViewCounter.Bump(46)
|
||||||
err = routeAccountEditCriticalSubmit(w,req,user)
|
err = routeAccountEditCriticalSubmit(w,req,user)
|
||||||
case "/user/edit/avatar/":
|
case "/user/edit/avatar/":
|
||||||
err = common.MemberOnly(w,req,user)
|
err = common.MemberOnly(w,req,user)
|
||||||
|
@ -662,7 +668,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(46)
|
common.RouteViewCounter.Bump(47)
|
||||||
err = routeAccountEditAvatar(w,req,user)
|
err = routeAccountEditAvatar(w,req,user)
|
||||||
case "/user/edit/avatar/submit/":
|
case "/user/edit/avatar/submit/":
|
||||||
err = common.MemberOnly(w,req,user)
|
err = common.MemberOnly(w,req,user)
|
||||||
|
@ -671,7 +677,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(47)
|
common.RouteViewCounter.Bump(48)
|
||||||
err = routeAccountEditAvatarSubmit(w,req,user)
|
err = routeAccountEditAvatarSubmit(w,req,user)
|
||||||
case "/user/edit/username/":
|
case "/user/edit/username/":
|
||||||
err = common.MemberOnly(w,req,user)
|
err = common.MemberOnly(w,req,user)
|
||||||
|
@ -680,7 +686,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(48)
|
common.RouteViewCounter.Bump(49)
|
||||||
err = routeAccountEditUsername(w,req,user)
|
err = routeAccountEditUsername(w,req,user)
|
||||||
case "/user/edit/username/submit/":
|
case "/user/edit/username/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -695,7 +701,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(49)
|
common.RouteViewCounter.Bump(50)
|
||||||
err = routeAccountEditUsernameSubmit(w,req,user)
|
err = routeAccountEditUsernameSubmit(w,req,user)
|
||||||
case "/user/edit/email/":
|
case "/user/edit/email/":
|
||||||
err = common.MemberOnly(w,req,user)
|
err = common.MemberOnly(w,req,user)
|
||||||
|
@ -704,7 +710,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(50)
|
common.RouteViewCounter.Bump(51)
|
||||||
err = routeAccountEditEmail(w,req,user)
|
err = routeAccountEditEmail(w,req,user)
|
||||||
case "/user/edit/token/":
|
case "/user/edit/token/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -719,11 +725,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(51)
|
common.RouteViewCounter.Bump(52)
|
||||||
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
|
err = routeAccountEditEmailTokenSubmit(w,req,user,extraData)
|
||||||
default:
|
default:
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
common.RouteViewCounter.Bump(52)
|
common.RouteViewCounter.Bump(53)
|
||||||
err = routeProfile(w,req,user)
|
err = routeProfile(w,req,user)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -744,7 +750,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(53)
|
common.RouteViewCounter.Bump(54)
|
||||||
err = routeBanSubmit(w,req,user,extraData)
|
err = routeBanSubmit(w,req,user,extraData)
|
||||||
case "/users/unban/":
|
case "/users/unban/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -759,7 +765,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(54)
|
common.RouteViewCounter.Bump(55)
|
||||||
err = routeUnban(w,req,user,extraData)
|
err = routeUnban(w,req,user,extraData)
|
||||||
case "/users/activate/":
|
case "/users/activate/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
|
@ -774,7 +780,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(55)
|
common.RouteViewCounter.Bump(56)
|
||||||
err = routeActivate(w,req,user,extraData)
|
err = routeActivate(w,req,user,extraData)
|
||||||
case "/users/ips/":
|
case "/users/ips/":
|
||||||
err = common.MemberOnly(w,req,user)
|
err = common.MemberOnly(w,req,user)
|
||||||
|
@ -783,7 +789,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
common.RouteViewCounter.Bump(56)
|
common.RouteViewCounter.Bump(57)
|
||||||
err = routeIps(w,req,user)
|
err = routeIps(w,req,user)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -800,7 +806,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
common.NotFound(w,req)
|
common.NotFound(w,req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
common.RouteViewCounter.Bump(58)
|
common.RouteViewCounter.Bump(59)
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
// TODO: Find a way to propagate errors up from this?
|
// TODO: Find a way to propagate errors up from this?
|
||||||
router.UploadHandler(w,req) // TODO: Count these views
|
router.UploadHandler(w,req) // TODO: Count these views
|
||||||
|
@ -844,7 +850,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
router.RUnlock()
|
router.RUnlock()
|
||||||
|
|
||||||
if ok {
|
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
|
req.URL.Path += extraData
|
||||||
err = handle(w,req,user)
|
err = handle(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
"panel-forums":"Forum Manager",
|
"panel-forums":"Forum Manager",
|
||||||
"panel-delete-forum":"Delete Forum",
|
"panel-delete-forum":"Delete Forum",
|
||||||
"panel-edit-forum":"Forum Editor",
|
"panel-edit-forum":"Forum Editor",
|
||||||
|
"panel-analytics":"Analytics",
|
||||||
"panel-settings":"Setting Manager",
|
"panel-settings":"Setting Manager",
|
||||||
"panel-edit-setting":"Edit Setting",
|
"panel-edit-setting":"Edit Setting",
|
||||||
"panel-word-filters":"Word Filter Manager",
|
"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"))
|
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)
|
tid, err := common.Topics.Create(fid, topicName, content, user.ID, user.LastIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
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)
|
_, err = common.Rstore.Create(topic, content, user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
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)
|
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)
|
_, err = common.Prstore.Create(uid, content, user.ID, user.LastIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
|
@ -726,7 +729,6 @@ func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user c
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
err := r.ParseMultipartForm(int64(common.Megabyte))
|
err := r.ParseMultipartForm(int64(common.Megabyte))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError("Upload failed", w, r, user)
|
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)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
|
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
|
||||||
|
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your avatar was successfully updated")
|
headerVars.NoticeList = append(headerVars.NoticeList, "Your avatar was successfully updated")
|
||||||
|
|
||||||
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
|
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
|
||||||
if common.PreRenderHooks["pre_render_account_own_edit_avatar"] != nil {
|
if common.PreRenderHooks["pre_render_account_own_edit_avatar"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
|
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
|
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.PreRenderHooks["pre_render_account_own_edit_username"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -834,7 +836,7 @@ func routeAccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user
|
||||||
user.Name = newUsername
|
user.Name = newUsername
|
||||||
|
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your username was successfully updated")
|
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.PreRenderHooks["pre_render_account_own_edit_username"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -885,10 +887,10 @@ func routeAccountEditEmail(w http.ResponseWriter, r *http.Request, user common.U
|
||||||
email.Primary = true
|
email.Primary = true
|
||||||
emailList = append(emailList, email)
|
emailList = append(emailList, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !common.Site.EnableEmails {
|
if !common.Site.EnableEmails {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, "The mail system is currently disabled.")
|
headerVars.NoticeList = append(headerVars.NoticeList, "The mail system is currently disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
|
pi := common.Page{"Email Manager", user, headerVars, emailList, nil}
|
||||||
if common.PreRenderHooks["pre_render_account_own_edit_email"] != nil {
|
if common.PreRenderHooks["pre_render_account_own_edit_email"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_account_own_edit_email", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_account_own_edit_email", w, r, &user, &pi) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
//"log"
|
//"log"
|
||||||
//"fmt"
|
//"fmt"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"html"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -45,7 +44,8 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
}
|
}
|
||||||
|
|
||||||
topicName := r.PostFormValue("topic_name")
|
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)
|
err = topic.Update(topicName, topicContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
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)
|
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)
|
_, err = stmts.editReply.Exec(content, common.ParseMessage(content, fid, "forums"), rid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
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)
|
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)
|
_, err = stmts.editProfileReply.Exec(content, common.ParseMessage(content, 0, ""), rid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -75,36 +76,34 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add a stat store for this?
|
// TODO: Add a stat store for this?
|
||||||
var postCount int
|
var intErr error
|
||||||
err = stmts.todaysPostCount.QueryRow().Scan(&postCount)
|
var extractStat = func(stmt *sql.Stmt) (stat int) {
|
||||||
|
err := stmt.QueryRow().Scan(&stat)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
intErr = err
|
||||||
}
|
}
|
||||||
|
return stat
|
||||||
|
}
|
||||||
|
|
||||||
|
var postCount = extractStat(stmts.todaysPostCount)
|
||||||
var postInterval = "day"
|
var postInterval = "day"
|
||||||
var postColour = greaterThanSwitch(postCount, 5, 25)
|
var postColour = greaterThanSwitch(postCount, 5, 25)
|
||||||
|
|
||||||
var topicCount int
|
var topicCount = extractStat(stmts.todaysTopicCount)
|
||||||
err = stmts.todaysTopicCount.QueryRow().Scan(&topicCount)
|
|
||||||
if err != nil && err != ErrNoRows {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
var topicInterval = "day"
|
var topicInterval = "day"
|
||||||
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
||||||
|
|
||||||
var reportCount int
|
var reportCount = extractStat(stmts.todaysReportCount)
|
||||||
err = stmts.todaysReportCount.QueryRow().Scan(&reportCount)
|
|
||||||
if err != nil && err != ErrNoRows {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
var reportInterval = "week"
|
var reportInterval = "week"
|
||||||
|
|
||||||
var newUserCount int
|
var newUserCount = extractStat(stmts.todaysNewUserCount)
|
||||||
err = stmts.todaysNewUserCount.QueryRow().Scan(&newUserCount)
|
|
||||||
if err != nil && err != ErrNoRows {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
var newUserInterval = "week"
|
var newUserInterval = "week"
|
||||||
|
|
||||||
|
// Did any of the extractStats fail?
|
||||||
|
if intErr != nil {
|
||||||
|
return common.InternalError(intErr, w, r)
|
||||||
|
}
|
||||||
|
|
||||||
var gridElements = []common.GridElement{
|
var gridElements = []common.GridElement{
|
||||||
common.GridElement{"dash-version", "v" + version.String(), 0, "grid_istat stat_green", "", "", "Gosora is up-to-date :)"},
|
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"},
|
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()
|
uonline := wsHub.userCount()
|
||||||
gonline := wsHub.guestCount()
|
gonline := wsHub.guestCount()
|
||||||
totonline := uonline + gonline
|
totonline := uonline + gonline
|
||||||
|
reqCount := 0
|
||||||
|
|
||||||
var onlineColour = greaterThanSwitch(totonline, 3, 10)
|
var onlineColour = greaterThanSwitch(totonline, 3, 10)
|
||||||
var onlineGuestsColour = greaterThanSwitch(gonline, 1, 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-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-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-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-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-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-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-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-reports", strconv.Itoa(reportCount) + " reports / " + reportInterval, 11, "grid_stat", "", "", "The number of reports over the last 7 days"})
|
||||||
|
|
||||||
|
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-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-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"*/})
|
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}
|
pi := common.PanelDashboardPage{common.GetTitlePhrase("panel-dashboard"), user, headerVars, stats, "dashboard", gridElements}
|
||||||
if common.PreRenderHooks["pre_render_panel_dashboard"] != nil {
|
if common.PreRenderHooks["pre_render_panel_dashboard"] != nil {
|
||||||
|
@ -422,6 +425,25 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
|
||||||
return nil
|
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 {
|
func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
|
|
|
@ -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 */
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add QueryRow for this and use it in statistics.go
|
||||||
func (count *accCountBuilder) Prepare() *sql.Stmt {
|
func (count *accCountBuilder) Prepare() *sql.Stmt {
|
||||||
return count.build.SimpleCount(count.table, count.where, count.limit)
|
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"),
|
View("routePanelUsersEdit", "/panel/users/edit/", "extraData"),
|
||||||
Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"),
|
Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"),
|
||||||
|
|
||||||
|
View("routePanelAnalyticsViews", "/panel/analytics/views/"),
|
||||||
|
|
||||||
View("routePanelGroups", "/panel/groups/"),
|
View("routePanelGroups", "/panel/groups/"),
|
||||||
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),
|
View("routePanelGroupsEdit", "/panel/groups/edit/", "extraData"),
|
||||||
View("routePanelGroupsEditPerms", "/panel/groups/edit/perms/", "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
|
var replyList []common.ReplyUser
|
||||||
|
|
||||||
// SEO URLs...
|
// SEO URLs...
|
||||||
|
// TODO: Do a 301 if it's the wrong username? Do a canonical too?
|
||||||
halves := strings.Split(r.URL.Path[len("/user/"):], ".")
|
halves := strings.Split(r.URL.Path[len("/user/"):], ".")
|
||||||
if len(halves) < 2 {
|
if len(halves) < 2 {
|
||||||
halves = append(halves, halves[0])
|
halves = append(halves, halves[0])
|
||||||
|
|
161
template_list.go
161
template_list.go
|
@ -137,111 +137,115 @@ var topic_26 = []byte(`</p>
|
||||||
<textarea name="topic_content" class="show_on_edit topic_content_input">`)
|
<textarea name="topic_content" class="show_on_edit topic_content_input">`)
|
||||||
var topic_27 = []byte(`</textarea>
|
var topic_27 = []byte(`</textarea>
|
||||||
|
|
||||||
<span class="controls">
|
<span class="controls" aria-label="Controls and Author Information">
|
||||||
|
|
||||||
<a href="`)
|
<a href="`)
|
||||||
var topic_28 = []byte(`" class="username real_username" rel="author">`)
|
var topic_28 = []byte(`" class="username real_username" rel="author">`)
|
||||||
var topic_29 = []byte(`</a>
|
var topic_29 = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var topic_30 = []byte(`<a href="/topic/like/submit/`)
|
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"`)
|
<button class="username like_label"`)
|
||||||
var topic_32 = []byte(` style="background-color:#D6FFD6;"`)
|
var topic_35 = []byte(` style="background-color:#D6FFD6;"`)
|
||||||
var topic_33 = []byte(`></button></a>`)
|
var topic_36 = []byte(`></button></a>`)
|
||||||
var topic_34 = []byte(`<a href='/topic/edit/`)
|
var topic_37 = []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_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_36 = []byte(`<a href='/topic/delete/submit/`)
|
var topic_39 = []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_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_38 = []byte(`<a class="mod_button" href='/topic/unlock/submit/`)
|
var topic_41 = []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_42 = []byte(`' style="font-weight:normal;" title="Unlock Topic" aria-label="Unlock this topic"><button class="username unlock_label"></button></a>`)
|
||||||
var topic_40 = []byte(`<a href='/topic/lock/submit/`)
|
var topic_43 = []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_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_42 = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
|
var topic_45 = []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_46 = []byte(`' style="font-weight:normal;" title="Unpin Topic" aria-label="Unpin this topic"><button class="username unpin_label"></button></a>`)
|
||||||
var topic_44 = []byte(`<a href='/topic/stick/submit/`)
|
var topic_47 = []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_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_46 = []byte(`<a class="mod_button" href='/users/ips/?ip=`)
|
var topic_49 = []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_50 = []byte(`' style="font-weight:normal;" title="View IP" aria-label="The poster's IP is `)
|
||||||
var topic_48 = []byte(`
|
var topic_51 = []byte(`"><button class="username ip_label"></button></a>`)
|
||||||
|
var topic_52 = []byte(`
|
||||||
<a href="/report/submit/`)
|
<a href="/report/submit/`)
|
||||||
var topic_49 = []byte(`?session=`)
|
var topic_53 = []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_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_55 = []byte(`<a class="username hide_on_micro like_count" aria-label="The number of likes on this topic">`)
|
||||||
var topic_52 = []byte(`</a><a class="username hide_on_micro like_count_label" title="Like Count"></a>`)
|
var topic_56 = []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_57 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||||
var topic_54 = []byte(`</a>`)
|
var topic_58 = []byte(`</a>`)
|
||||||
var topic_55 = []byte(`<a class="username hide_on_micro level">`)
|
var topic_59 = []byte(`<a class="username hide_on_micro level" aria-label="The poster's level">`)
|
||||||
var topic_56 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
var topic_60 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||||
var topic_57 = []byte(`
|
var topic_61 = []byte(`
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<div class="rowblock post_container" aria-label="The current page for this topic" style="overflow: hidden;">`)
|
<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">
|
<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;">`)
|
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
|
||||||
var topic_59 = []byte(`</span>
|
var topic_63 = []byte(`</span>
|
||||||
<span itemprop="text">`)
|
<span itemprop="text">`)
|
||||||
var topic_60 = []byte(`</span>
|
var topic_64 = []byte(`</span>
|
||||||
</article>
|
</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 `)
|
<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_66 = []byte(`" style="background-image: url(`)
|
||||||
var topic_63 = []byte(`), url(/static/`)
|
var topic_67 = []byte(`), url(/static/`)
|
||||||
var topic_64 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
var topic_68 = []byte(`/post-avatar-bg.jpg);background-position: 0px `)
|
||||||
var topic_65 = []byte(`-1`)
|
var topic_69 = []byte(`-1`)
|
||||||
var topic_66 = []byte(`0px;background-repeat:no-repeat, repeat-y;">
|
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;">`)
|
<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">
|
<span class="controls">
|
||||||
|
|
||||||
<a href="`)
|
<a href="`)
|
||||||
var topic_69 = []byte(`" class="username real_username" rel="author">`)
|
var topic_73 = []byte(`" class="username real_username" rel="author">`)
|
||||||
var topic_70 = []byte(`</a>
|
var topic_74 = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var topic_71 = []byte(`<a href="/reply/like/submit/`)
|
var topic_75 = []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_76 = []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_77 = []byte(` style="background-color:#D6FFD6;"`)
|
||||||
var topic_74 = []byte(`></button></a>`)
|
var topic_78 = []byte(`></button></a>`)
|
||||||
var topic_75 = []byte(`<a href="/reply/edit/submit/`)
|
var topic_79 = []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_80 = []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_81 = []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_82 = []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_83 = []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_84 = []byte(`' style="font-weight:normal;" title="View IP"><button class="username ip_label"></button></a>`)
|
||||||
var topic_81 = []byte(`
|
var topic_85 = []byte(`
|
||||||
<a href="/report/submit/`)
|
<a href="/report/submit/`)
|
||||||
var topic_82 = []byte(`?session=`)
|
var topic_86 = []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_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_88 = []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_89 = []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_90 = []byte(`<a class="username hide_on_micro user_tag">`)
|
||||||
var topic_87 = []byte(`</a>`)
|
var topic_91 = []byte(`</a>`)
|
||||||
var topic_88 = []byte(`<a class="username hide_on_micro level">`)
|
var topic_92 = []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_93 = []byte(`</a><a class="username hide_on_micro level_label" style="float:right;" title="Level"></a>`)
|
||||||
var topic_90 = []byte(`
|
var topic_94 = []byte(`
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</article>
|
</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">
|
<div class="rowblock topic_reply_form quick_create_form">
|
||||||
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
<form id="reply_form" enctype="multipart/form-data" action="/reply/create/" method="post"></form>
|
||||||
<input form="reply_form" name="tid" value='`)
|
<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="formrow real_first_child">
|
||||||
<div class="formitem">
|
<div class="formitem">
|
||||||
<textarea id="input_content" form="reply_form" name="reply-content" placeholder="Insert reply here" required></textarea>
|
<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">
|
<div class="formitem">
|
||||||
<button form="reply_form" name="reply-button" class="formbutton">Create Reply</button>
|
<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;" />
|
<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>
|
<label for="upload_files" class="formbutton add_file_button">Add File</label>
|
||||||
<div id="upload_file_dock"></div>`)
|
<div id="upload_file_dock"></div>`)
|
||||||
var topic_95 = []byte(`
|
var topic_99 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var topic_96 = []byte(`
|
var topic_100 = []byte(`
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
@ -719,23 +723,24 @@ window.addEventListener("hashchange", handle_profile_hashbit, false)
|
||||||
|
|
||||||
`)
|
`)
|
||||||
var forums_0 = []byte(`
|
var forums_0 = []byte(`
|
||||||
<main>
|
<main itemscope itemtype="http://schema.org/ItemList">
|
||||||
|
|
||||||
<div class="rowblock opthead">
|
<div class="rowblock opthead">
|
||||||
<div class="rowitem"><h1>Forums</h1></div>
|
<div class="rowitem"><h1 itemprop="name">Forums</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowblock forum_list">
|
<div class="rowblock forum_list">
|
||||||
`)
|
`)
|
||||||
var forums_1 = []byte(`<div class="rowitem `)
|
var forums_1 = []byte(`<div class="rowitem `)
|
||||||
var forums_2 = []byte(`datarow `)
|
var forums_2 = []byte(`datarow `)
|
||||||
var forums_3 = []byte(`">
|
var forums_3 = []byte(`"itemprop="itemListElement" itemscope
|
||||||
|
itemtype="http://schema.org/ListItem">
|
||||||
<span class="forum_left shift_left">
|
<span class="forum_left shift_left">
|
||||||
<a href="`)
|
<a href="`)
|
||||||
var forums_4 = []byte(`">`)
|
var forums_4 = []byte(`" itemprop="item">`)
|
||||||
var forums_5 = []byte(`</a>
|
var forums_5 = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var forums_6 = []byte(`
|
var forums_6 = []byte(`
|
||||||
<br /><span class="rowsmall">`)
|
<br /><span class="rowsmall" itemprop="description">`)
|
||||||
var forums_7 = []byte(`</span>
|
var forums_7 = []byte(`</span>
|
||||||
`)
|
`)
|
||||||
var forums_8 = []byte(`
|
var forums_8 = []byte(`
|
||||||
|
@ -772,12 +777,12 @@ var forums_22 = []byte(`
|
||||||
</main>
|
</main>
|
||||||
`)
|
`)
|
||||||
var topics_0 = []byte(`
|
var topics_0 = []byte(`
|
||||||
<main>
|
<main itemscope itemtype="http://schema.org/ItemList">
|
||||||
|
|
||||||
<div class="rowblock rowhead topic_list_title_block">
|
<div class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem topic_list_title`)
|
<div class="rowitem topic_list_title`)
|
||||||
var topics_1 = []byte(` has_opt`)
|
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(`
|
var topics_3 = []byte(`
|
||||||
<div class="pre_opt auto_hide"></div>
|
<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>
|
var topics_28 = []byte(`'s Avatar" /></a>
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="`)
|
<a class="rowtopic" href="`)
|
||||||
var topics_29 = []byte(`"><span>`)
|
var topics_29 = []byte(`" itemprop="itemListElement"><span>`)
|
||||||
var topics_30 = []byte(`</span></a> `)
|
var topics_30 = []byte(`</span></a> `)
|
||||||
var topics_31 = []byte(`<a class="rowsmall parent_forum" href="`)
|
var topics_31 = []byte(`<a class="rowsmall parent_forum" href="`)
|
||||||
var topics_32 = []byte(`">`)
|
var topics_32 = []byte(`">`)
|
||||||
|
@ -932,12 +937,12 @@ var forum_6 = []byte(`?page=`)
|
||||||
var forum_7 = []byte(`">></a></div>`)
|
var forum_7 = []byte(`">></a></div>`)
|
||||||
var forum_8 = []byte(`
|
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 id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem forum_title`)
|
<div class="rowitem forum_title`)
|
||||||
var forum_9 = []byte(` has_opt`)
|
var forum_9 = []byte(` has_opt`)
|
||||||
var forum_10 = []byte(`">
|
var forum_10 = []byte(`">
|
||||||
<h1>`)
|
<h1 itemprop="name">`)
|
||||||
var forum_11 = []byte(`</h1>
|
var forum_11 = []byte(`</h1>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
|
@ -1026,7 +1031,7 @@ var forum_32 = []byte(`'s Avatar" title="`)
|
||||||
var forum_33 = []byte(`'s Avatar" /></a>
|
var forum_33 = []byte(`'s Avatar" /></a>
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="`)
|
<a class="rowtopic" href="`)
|
||||||
var forum_34 = []byte(`"><span>`)
|
var forum_34 = []byte(`" itemprop="itemListElement"><span>`)
|
||||||
var forum_35 = []byte(`</span></a>
|
var forum_35 = []byte(`</span></a>
|
||||||
<br /><a class="rowsmall starter" href="`)
|
<br /><a class="rowsmall starter" href="`)
|
||||||
var forum_36 = []byte(`">`)
|
var forum_36 = []byte(`">`)
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
// Code generated by Gosora. More below:
|
// 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. */
|
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||||
package main
|
package main
|
||||||
import "net/http"
|
|
||||||
import "./common"
|
import "./common"
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -149,151 +149,159 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
||||||
w.Write(topic_31)
|
w.Write(topic_31)
|
||||||
if tmpl_topic_vars.Topic.Liked {
|
if tmpl_topic_vars.Topic.Liked {
|
||||||
w.Write(topic_32)
|
w.Write(topic_32)
|
||||||
}
|
} else {
|
||||||
w.Write(topic_33)
|
w.Write(topic_33)
|
||||||
}
|
}
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.EditTopic {
|
|
||||||
w.Write(topic_34)
|
w.Write(topic_34)
|
||||||
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
|
if tmpl_topic_vars.Topic.Liked {
|
||||||
w.Write(topic_35)
|
w.Write(topic_35)
|
||||||
}
|
}
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteTopic {
|
|
||||||
w.Write(topic_36)
|
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(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.CurrentUser.Perms.CloseTopic {
|
||||||
if tmpl_topic_vars.Topic.IsClosed {
|
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(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.CurrentUser.Perms.PinTopic {
|
||||||
if tmpl_topic_vars.Topic.Sticky {
|
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(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 {
|
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(topic_49)
|
||||||
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
w.Write([]byte(tmpl_topic_vars.Topic.IPAddress))
|
||||||
w.Write(topic_50)
|
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(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(topic_53)
|
||||||
w.Write([]byte(tmpl_topic_vars.Topic.Tag))
|
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||||
w.Write(topic_54)
|
w.Write(topic_54)
|
||||||
} else {
|
if tmpl_topic_vars.Topic.LikeCount > 0 {
|
||||||
w.Write(topic_55)
|
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)
|
w.Write(topic_56)
|
||||||
}
|
}
|
||||||
|
if tmpl_topic_vars.Topic.Tag != "" {
|
||||||
w.Write(topic_57)
|
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 {
|
if len(tmpl_topic_vars.ItemList) != 0 {
|
||||||
for _, item := range tmpl_topic_vars.ItemList {
|
for _, item := range tmpl_topic_vars.ItemList {
|
||||||
if item.ActionType != "" {
|
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(topic_62)
|
||||||
w.Write([]byte(item.Avatar))
|
w.Write([]byte(item.ActionIcon))
|
||||||
w.Write(topic_63)
|
w.Write(topic_63)
|
||||||
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
w.Write([]byte(item.ActionType))
|
||||||
w.Write(topic_64)
|
w.Write(topic_64)
|
||||||
if item.ContentLines <= 5 {
|
} else {
|
||||||
w.Write(topic_65)
|
w.Write(topic_65)
|
||||||
}
|
w.Write([]byte(item.ClassName))
|
||||||
w.Write(topic_66)
|
w.Write(topic_66)
|
||||||
|
w.Write([]byte(item.Avatar))
|
||||||
w.Write(topic_67)
|
w.Write(topic_67)
|
||||||
w.Write([]byte(item.ContentHtml))
|
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||||
w.Write(topic_68)
|
w.Write(topic_68)
|
||||||
w.Write([]byte(item.UserLink))
|
if item.ContentLines <= 5 {
|
||||||
w.Write(topic_69)
|
w.Write(topic_69)
|
||||||
w.Write([]byte(item.CreatedByName))
|
}
|
||||||
w.Write(topic_70)
|
w.Write(topic_70)
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
|
||||||
w.Write(topic_71)
|
w.Write(topic_71)
|
||||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
w.Write([]byte(item.ContentHtml))
|
||||||
w.Write(topic_72)
|
w.Write(topic_72)
|
||||||
if item.Liked {
|
w.Write([]byte(item.UserLink))
|
||||||
w.Write(topic_73)
|
w.Write(topic_73)
|
||||||
}
|
w.Write([]byte(item.CreatedByName))
|
||||||
w.Write(topic_74)
|
w.Write(topic_74)
|
||||||
}
|
if tmpl_topic_vars.CurrentUser.Perms.LikeItem {
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.EditReply {
|
|
||||||
w.Write(topic_75)
|
w.Write(topic_75)
|
||||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||||
w.Write(topic_76)
|
w.Write(topic_76)
|
||||||
}
|
if item.Liked {
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
|
|
||||||
w.Write(topic_77)
|
w.Write(topic_77)
|
||||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
}
|
||||||
w.Write(topic_78)
|
w.Write(topic_78)
|
||||||
}
|
}
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.ViewIPs {
|
if tmpl_topic_vars.CurrentUser.Perms.EditReply {
|
||||||
w.Write(topic_79)
|
w.Write(topic_79)
|
||||||
w.Write([]byte(item.IPAddress))
|
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||||
w.Write(topic_80)
|
w.Write(topic_80)
|
||||||
}
|
}
|
||||||
|
if tmpl_topic_vars.CurrentUser.Perms.DeleteReply {
|
||||||
w.Write(topic_81)
|
w.Write(topic_81)
|
||||||
w.Write([]byte(strconv.Itoa(item.ID)))
|
w.Write([]byte(strconv.Itoa(item.ID)))
|
||||||
w.Write(topic_82)
|
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(topic_86)
|
||||||
w.Write([]byte(item.Tag))
|
w.Write([]byte(tmpl_topic_vars.CurrentUser.Session))
|
||||||
w.Write(topic_87)
|
w.Write(topic_87)
|
||||||
} else {
|
if item.LikeCount > 0 {
|
||||||
w.Write(topic_88)
|
w.Write(topic_88)
|
||||||
w.Write([]byte(strconv.Itoa(item.Level)))
|
w.Write([]byte(strconv.Itoa(item.LikeCount)))
|
||||||
w.Write(topic_89)
|
w.Write(topic_89)
|
||||||
}
|
}
|
||||||
|
if item.Tag != "" {
|
||||||
w.Write(topic_90)
|
w.Write(topic_90)
|
||||||
}
|
w.Write([]byte(item.Tag))
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(topic_91)
|
w.Write(topic_91)
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.CreateReply {
|
} else {
|
||||||
w.Write(topic_92)
|
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)
|
w.Write(topic_93)
|
||||||
if tmpl_topic_vars.CurrentUser.Perms.UploadFiles {
|
}
|
||||||
w.Write(topic_94)
|
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(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(footer_0)
|
||||||
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
|
w.Write([]byte(common.BuildWidget("footer",tmpl_topic_vars.Header)))
|
||||||
w.Write(footer_1)
|
w.Write(footer_1)
|
||||||
|
|
|
@ -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>
|
<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>
|
<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>
|
<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}}
|
<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.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"><button class="username ip_label"></button></a>{{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 Topic"><button class="username flag_label"></button></a>
|
<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>
|
</span>
|
||||||
</div>
|
</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.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}}
|
{{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}}
|
{{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;
|
text-align: center;
|
||||||
}
|
}
|
||||||
/* TODO: Move these to panel.css */
|
/* 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;
|
display: inline-block;
|
||||||
background: var(--tinted-background-color);
|
background: var(--tinted-background-color);
|
||||||
font: normal normal normal 14px/1 FontAwesome;
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
@ -1147,6 +1147,15 @@ select, input, textarea, button {
|
||||||
#dash-ram:before {
|
#dash-ram:before {
|
||||||
content: "\f233";
|
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) {
|
@media(min-width: 721px) {
|
||||||
.hide_on_big {
|
.hide_on_big {
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
}
|
}
|
||||||
.colstack_left {
|
.colstack_left {
|
||||||
background-color: hsl(0,0%,90%);
|
background-color: hsl(0,0%,90%);
|
||||||
|
margin-top: -0.5px;
|
||||||
|
border-right: 1px solid var(--element-border-color);
|
||||||
}
|
}
|
||||||
.colstack_left .colstack_head {
|
.colstack_left .colstack_head {
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
|
@ -21,9 +23,6 @@
|
||||||
.colstack_left .rowmenu .passive:last-child {
|
.colstack_left .rowmenu .passive:last-child {
|
||||||
border-bottom: 0.5px solid var(--element-border-color) !important;
|
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 {
|
.submenu {
|
||||||
margin-left: 12px;
|
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 {
|
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
|
var wsUsers []*WSUser
|
||||||
hub.users.RLock()
|
hub.users.RLock()
|
||||||
// We don't want to keep a lock on this for too long, so we'll accept some nil pointers
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("Building alert")
|
|
||||||
alert, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
alert, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("Getting WS Writer")
|
|
||||||
w, err := wsUser.conn.NextWriter(websocket.TextMessage)
|
w, err := wsUser.conn.NextWriter(websocket.TextMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write([]byte(alert))
|
w.Write([]byte(alert))
|
||||||
w.Close()
|
w.Close()
|
||||||
}
|
}
|
||||||
|
@ -294,6 +290,26 @@ func adminStatsTicker() {
|
||||||
|
|
||||||
var totunit, uunit, gunit string
|
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:
|
AdminStatLoop:
|
||||||
for {
|
for {
|
||||||
adminStatsMutex.RLock()
|
adminStatsMutex.RLock()
|
||||||
|
@ -308,6 +324,7 @@ AdminStatLoop:
|
||||||
uonline := wsHub.userCount()
|
uonline := wsHub.userCount()
|
||||||
gonline := wsHub.guestCount()
|
gonline := wsHub.guestCount()
|
||||||
totonline := uonline + gonline
|
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...
|
// 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)
|
noStatUpdates = (uonline == lastUonline && gonline == lastGonline && totonline == lastTotonline)
|
||||||
|
@ -318,29 +335,9 @@ AdminStatLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if !noStatUpdates {
|
if !noStatUpdates {
|
||||||
if totonline > 10 {
|
onlineColour = greaterThanSwitch(totonline, 3, 10)
|
||||||
onlineColour = "stat_green"
|
onlineGuestsColour = greaterThanSwitch(gonline, 1, 10)
|
||||||
} else if totonline > 3 {
|
onlineUsersColour = greaterThanSwitch(uonline, 1, 5)
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
totonline, totunit = common.ConvertFriendlyUnit(totonline)
|
totonline, totunit = common.ConvertFriendlyUnit(totonline)
|
||||||
uonline, uunit = common.ConvertFriendlyUnit(uonline)
|
uonline, uunit = common.ConvertFriendlyUnit(uonline)
|
||||||
|
@ -384,13 +381,7 @@ AdminStatLoop:
|
||||||
ramstr = fmt.Sprintf("%.1f", usedCount) + " / " + totstr + totalUnit
|
ramstr = fmt.Sprintf("%.1f", usedCount) + " / " + totstr + totalUnit
|
||||||
|
|
||||||
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
||||||
if ramperc < 50 {
|
ramColour = lessThanSwitch(int(ramperc), 50, 75)
|
||||||
ramColour = "stat_green"
|
|
||||||
} else if ramperc < 75 {
|
|
||||||
ramColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
ramColour = "stat_red"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +392,6 @@ AdminStatLoop:
|
||||||
for watcher := range watchers {
|
for watcher := range watchers {
|
||||||
w, err := watcher.conn.NextWriter(websocket.TextMessage)
|
w, err := watcher.conn.NextWriter(websocket.TextMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//log.Print(err.Error())
|
|
||||||
adminStatsMutex.Lock()
|
adminStatsMutex.Lock()
|
||||||
delete(adminStatsWatchers, watcher)
|
delete(adminStatsWatchers, watcher)
|
||||||
adminStatsMutex.Unlock()
|
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-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-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-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-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-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-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"))
|
w.Write([]byte("set #dash-cpu <span>CPU: " + cpustr + "%</span>\r"))
|
||||||
|
|
Loading…
Reference in New Issue