Added the level list page.
Levels can now be localised individually. Added the level template function. Fixed the padding on alerts without avatars. The level data is now in it's own block in the profiles. Removed three level phrases and replaced them with the new Level API.
This commit is contained in:
parent
de78268b20
commit
05ab585d41
|
@ -160,6 +160,18 @@ type AccountDashPage struct {
|
|||
NextLevel int
|
||||
}
|
||||
|
||||
type LevelListItem struct {
|
||||
Level int
|
||||
Score int
|
||||
Status string
|
||||
Percentage int // 0 to 200 to fit with the CSS logic
|
||||
}
|
||||
|
||||
type LevelListPage struct {
|
||||
*Header
|
||||
Levels []LevelListItem
|
||||
}
|
||||
|
||||
type PanelStats struct {
|
||||
Users int
|
||||
Groups int
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -141,6 +142,14 @@ func SaveLangPack(langPack *LanguagePack) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func GetLevelPhrase(level int) string {
|
||||
levelPhrases := currentLangPack.Load().(*LanguagePack).Levels
|
||||
if len(levelPhrases.Levels) > 0 && level < len(levelPhrases.Levels) {
|
||||
return strings.Replace(levelPhrases.Levels[level], "{0}", strconv.Itoa(level), -1)
|
||||
}
|
||||
return strings.Replace(levelPhrases.Level, "{0}", strconv.Itoa(level), -1)
|
||||
}
|
||||
|
||||
// TODO: Merge these two maps?
|
||||
func GetGlobalPermPhrase(name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).GlobalPerms[name]
|
||||
|
|
|
@ -511,6 +511,14 @@ func InitTemplates() error {
|
|||
return GetTmplPhrase(phraseName)
|
||||
}
|
||||
|
||||
fmap["level"] = func(levelInt interface{}) interface{} {
|
||||
level, ok := levelInt.(int)
|
||||
if !ok {
|
||||
panic("levelInt is not an integer")
|
||||
}
|
||||
return GetLevelPhrase(level)
|
||||
}
|
||||
|
||||
fmap["scope"] = func(name interface{}) interface{} {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ func NewCTemplateSet() *CTemplateSet {
|
|||
"divide": true,
|
||||
"dock": true,
|
||||
"lang": true,
|
||||
"level": true,
|
||||
"scope": true,
|
||||
},
|
||||
}
|
||||
|
@ -715,6 +716,19 @@ ArgLoop:
|
|||
out = "w.Write(phrases[" + strconv.Itoa(len(c.langIndexToName)-1) + "])\n"
|
||||
literal = true
|
||||
break ArgLoop
|
||||
case "level":
|
||||
var leftParam string
|
||||
// TODO: Implement level literals
|
||||
leftOperand := node.Args[pos+1].String()
|
||||
if len(leftOperand) == 0 {
|
||||
panic("The leftoperand for function level cannot be left blank")
|
||||
}
|
||||
|
||||
leftParam, _ = c.compileIfVarsub(leftOperand, varholder, templateName, holdreflect)
|
||||
// TODO: Refactor this
|
||||
out = "w.Write([]byte(common.GetLevelPhrase(" + leftParam + ")))\n"
|
||||
literal = true
|
||||
break ArgLoop
|
||||
case "scope":
|
||||
literal = true
|
||||
break ArgLoop
|
||||
|
|
234
gen_router.go
234
gen_router.go
|
@ -113,6 +113,7 @@ var RouteMap = map[string]interface{}{
|
|||
"routes.AccountEditMFADisableSubmit": routes.AccountEditMFADisableSubmit,
|
||||
"routes.AccountEditEmail": routes.AccountEditEmail,
|
||||
"routes.AccountEditEmailTokenSubmit": routes.AccountEditEmailTokenSubmit,
|
||||
"routes.LevelList": routes.LevelList,
|
||||
"routes.ViewProfile": routes.ViewProfile,
|
||||
"routes.BanUserSubmit": routes.BanUserSubmit,
|
||||
"routes.UnbanUser": routes.UnbanUser,
|
||||
|
@ -244,43 +245,44 @@ var routeMapEnum = map[string]int{
|
|||
"routes.AccountEditMFADisableSubmit": 87,
|
||||
"routes.AccountEditEmail": 88,
|
||||
"routes.AccountEditEmailTokenSubmit": 89,
|
||||
"routes.ViewProfile": 90,
|
||||
"routes.BanUserSubmit": 91,
|
||||
"routes.UnbanUser": 92,
|
||||
"routes.ActivateUser": 93,
|
||||
"routes.IPSearch": 94,
|
||||
"routes.CreateTopicSubmit": 95,
|
||||
"routes.EditTopicSubmit": 96,
|
||||
"routes.DeleteTopicSubmit": 97,
|
||||
"routes.StickTopicSubmit": 98,
|
||||
"routes.UnstickTopicSubmit": 99,
|
||||
"routes.LockTopicSubmit": 100,
|
||||
"routes.UnlockTopicSubmit": 101,
|
||||
"routes.MoveTopicSubmit": 102,
|
||||
"routes.LikeTopicSubmit": 103,
|
||||
"routes.ViewTopic": 104,
|
||||
"routes.CreateReplySubmit": 105,
|
||||
"routes.ReplyEditSubmit": 106,
|
||||
"routes.ReplyDeleteSubmit": 107,
|
||||
"routes.ReplyLikeSubmit": 108,
|
||||
"routes.ProfileReplyCreateSubmit": 109,
|
||||
"routes.ProfileReplyEditSubmit": 110,
|
||||
"routes.ProfileReplyDeleteSubmit": 111,
|
||||
"routes.PollVote": 112,
|
||||
"routes.PollResults": 113,
|
||||
"routes.AccountLogin": 114,
|
||||
"routes.AccountRegister": 115,
|
||||
"routes.AccountLogout": 116,
|
||||
"routes.AccountLoginSubmit": 117,
|
||||
"routes.AccountLoginMFAVerify": 118,
|
||||
"routes.AccountLoginMFAVerifySubmit": 119,
|
||||
"routes.AccountRegisterSubmit": 120,
|
||||
"routes.DynamicRoute": 121,
|
||||
"routes.UploadedFile": 122,
|
||||
"routes.StaticFile": 123,
|
||||
"routes.RobotsTxt": 124,
|
||||
"routes.SitemapXml": 125,
|
||||
"routes.BadRoute": 126,
|
||||
"routes.LevelList": 90,
|
||||
"routes.ViewProfile": 91,
|
||||
"routes.BanUserSubmit": 92,
|
||||
"routes.UnbanUser": 93,
|
||||
"routes.ActivateUser": 94,
|
||||
"routes.IPSearch": 95,
|
||||
"routes.CreateTopicSubmit": 96,
|
||||
"routes.EditTopicSubmit": 97,
|
||||
"routes.DeleteTopicSubmit": 98,
|
||||
"routes.StickTopicSubmit": 99,
|
||||
"routes.UnstickTopicSubmit": 100,
|
||||
"routes.LockTopicSubmit": 101,
|
||||
"routes.UnlockTopicSubmit": 102,
|
||||
"routes.MoveTopicSubmit": 103,
|
||||
"routes.LikeTopicSubmit": 104,
|
||||
"routes.ViewTopic": 105,
|
||||
"routes.CreateReplySubmit": 106,
|
||||
"routes.ReplyEditSubmit": 107,
|
||||
"routes.ReplyDeleteSubmit": 108,
|
||||
"routes.ReplyLikeSubmit": 109,
|
||||
"routes.ProfileReplyCreateSubmit": 110,
|
||||
"routes.ProfileReplyEditSubmit": 111,
|
||||
"routes.ProfileReplyDeleteSubmit": 112,
|
||||
"routes.PollVote": 113,
|
||||
"routes.PollResults": 114,
|
||||
"routes.AccountLogin": 115,
|
||||
"routes.AccountRegister": 116,
|
||||
"routes.AccountLogout": 117,
|
||||
"routes.AccountLoginSubmit": 118,
|
||||
"routes.AccountLoginMFAVerify": 119,
|
||||
"routes.AccountLoginMFAVerifySubmit": 120,
|
||||
"routes.AccountRegisterSubmit": 121,
|
||||
"routes.DynamicRoute": 122,
|
||||
"routes.UploadedFile": 123,
|
||||
"routes.StaticFile": 124,
|
||||
"routes.RobotsTxt": 125,
|
||||
"routes.SitemapXml": 126,
|
||||
"routes.BadRoute": 127,
|
||||
}
|
||||
var reverseRouteMapEnum = map[int]string{
|
||||
0: "routes.Overview",
|
||||
|
@ -373,43 +375,44 @@ var reverseRouteMapEnum = map[int]string{
|
|||
87: "routes.AccountEditMFADisableSubmit",
|
||||
88: "routes.AccountEditEmail",
|
||||
89: "routes.AccountEditEmailTokenSubmit",
|
||||
90: "routes.ViewProfile",
|
||||
91: "routes.BanUserSubmit",
|
||||
92: "routes.UnbanUser",
|
||||
93: "routes.ActivateUser",
|
||||
94: "routes.IPSearch",
|
||||
95: "routes.CreateTopicSubmit",
|
||||
96: "routes.EditTopicSubmit",
|
||||
97: "routes.DeleteTopicSubmit",
|
||||
98: "routes.StickTopicSubmit",
|
||||
99: "routes.UnstickTopicSubmit",
|
||||
100: "routes.LockTopicSubmit",
|
||||
101: "routes.UnlockTopicSubmit",
|
||||
102: "routes.MoveTopicSubmit",
|
||||
103: "routes.LikeTopicSubmit",
|
||||
104: "routes.ViewTopic",
|
||||
105: "routes.CreateReplySubmit",
|
||||
106: "routes.ReplyEditSubmit",
|
||||
107: "routes.ReplyDeleteSubmit",
|
||||
108: "routes.ReplyLikeSubmit",
|
||||
109: "routes.ProfileReplyCreateSubmit",
|
||||
110: "routes.ProfileReplyEditSubmit",
|
||||
111: "routes.ProfileReplyDeleteSubmit",
|
||||
112: "routes.PollVote",
|
||||
113: "routes.PollResults",
|
||||
114: "routes.AccountLogin",
|
||||
115: "routes.AccountRegister",
|
||||
116: "routes.AccountLogout",
|
||||
117: "routes.AccountLoginSubmit",
|
||||
118: "routes.AccountLoginMFAVerify",
|
||||
119: "routes.AccountLoginMFAVerifySubmit",
|
||||
120: "routes.AccountRegisterSubmit",
|
||||
121: "routes.DynamicRoute",
|
||||
122: "routes.UploadedFile",
|
||||
123: "routes.StaticFile",
|
||||
124: "routes.RobotsTxt",
|
||||
125: "routes.SitemapXml",
|
||||
126: "routes.BadRoute",
|
||||
90: "routes.LevelList",
|
||||
91: "routes.ViewProfile",
|
||||
92: "routes.BanUserSubmit",
|
||||
93: "routes.UnbanUser",
|
||||
94: "routes.ActivateUser",
|
||||
95: "routes.IPSearch",
|
||||
96: "routes.CreateTopicSubmit",
|
||||
97: "routes.EditTopicSubmit",
|
||||
98: "routes.DeleteTopicSubmit",
|
||||
99: "routes.StickTopicSubmit",
|
||||
100: "routes.UnstickTopicSubmit",
|
||||
101: "routes.LockTopicSubmit",
|
||||
102: "routes.UnlockTopicSubmit",
|
||||
103: "routes.MoveTopicSubmit",
|
||||
104: "routes.LikeTopicSubmit",
|
||||
105: "routes.ViewTopic",
|
||||
106: "routes.CreateReplySubmit",
|
||||
107: "routes.ReplyEditSubmit",
|
||||
108: "routes.ReplyDeleteSubmit",
|
||||
109: "routes.ReplyLikeSubmit",
|
||||
110: "routes.ProfileReplyCreateSubmit",
|
||||
111: "routes.ProfileReplyEditSubmit",
|
||||
112: "routes.ProfileReplyDeleteSubmit",
|
||||
113: "routes.PollVote",
|
||||
114: "routes.PollResults",
|
||||
115: "routes.AccountLogin",
|
||||
116: "routes.AccountRegister",
|
||||
117: "routes.AccountLogout",
|
||||
118: "routes.AccountLoginSubmit",
|
||||
119: "routes.AccountLoginMFAVerify",
|
||||
120: "routes.AccountLoginMFAVerifySubmit",
|
||||
121: "routes.AccountRegisterSubmit",
|
||||
122: "routes.DynamicRoute",
|
||||
123: "routes.UploadedFile",
|
||||
124: "routes.StaticFile",
|
||||
125: "routes.RobotsTxt",
|
||||
126: "routes.SitemapXml",
|
||||
127: "routes.BadRoute",
|
||||
}
|
||||
var osMapEnum = map[string]int{
|
||||
"unknown": 0,
|
||||
|
@ -708,7 +711,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
counters.GlobalViewCounter.Bump()
|
||||
|
||||
if prefix == "/static" {
|
||||
counters.RouteViewCounter.Bump(123)
|
||||
counters.RouteViewCounter.Bump(124)
|
||||
req.URL.Path += extraData
|
||||
routes.StaticFile(w, req)
|
||||
return
|
||||
|
@ -1586,9 +1589,18 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
|
||||
counters.RouteViewCounter.Bump(89)
|
||||
err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData)
|
||||
case "/user/levels/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
err = routes.LevelList(w,req,user)
|
||||
default:
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(90)
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
err = routes.ViewProfile(w,req,user)
|
||||
}
|
||||
case "/users":
|
||||
|
@ -1606,7 +1618,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(91)
|
||||
counters.RouteViewCounter.Bump(92)
|
||||
err = routes.BanUserSubmit(w,req,user,extraData)
|
||||
case "/users/unban/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1621,7 +1633,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(92)
|
||||
counters.RouteViewCounter.Bump(93)
|
||||
err = routes.UnbanUser(w,req,user,extraData)
|
||||
case "/users/activate/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1636,7 +1648,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(93)
|
||||
counters.RouteViewCounter.Bump(94)
|
||||
err = routes.ActivateUser(w,req,user,extraData)
|
||||
case "/users/ips/":
|
||||
err = common.MemberOnly(w,req,user)
|
||||
|
@ -1645,7 +1657,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(94)
|
||||
counters.RouteViewCounter.Bump(95)
|
||||
err = routes.IPSearch(w,req,user)
|
||||
}
|
||||
case "/topic":
|
||||
|
@ -1668,7 +1680,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(95)
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
err = routes.CreateTopicSubmit(w,req,user)
|
||||
case "/topic/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1683,7 +1695,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(96)
|
||||
counters.RouteViewCounter.Bump(97)
|
||||
err = routes.EditTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1699,7 +1711,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
}
|
||||
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(97)
|
||||
counters.RouteViewCounter.Bump(98)
|
||||
err = routes.DeleteTopicSubmit(w,req,user)
|
||||
case "/topic/stick/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1714,7 +1726,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(98)
|
||||
counters.RouteViewCounter.Bump(99)
|
||||
err = routes.StickTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/unstick/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1729,7 +1741,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(99)
|
||||
counters.RouteViewCounter.Bump(100)
|
||||
err = routes.UnstickTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/lock/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1745,7 +1757,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
}
|
||||
|
||||
req.URL.Path += extraData
|
||||
counters.RouteViewCounter.Bump(100)
|
||||
counters.RouteViewCounter.Bump(101)
|
||||
err = routes.LockTopicSubmit(w,req,user)
|
||||
case "/topic/unlock/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1760,7 +1772,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(101)
|
||||
counters.RouteViewCounter.Bump(102)
|
||||
err = routes.UnlockTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/move/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1775,7 +1787,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(102)
|
||||
counters.RouteViewCounter.Bump(103)
|
||||
err = routes.MoveTopicSubmit(w,req,user,extraData)
|
||||
case "/topic/like/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1796,10 +1808,10 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(103)
|
||||
counters.RouteViewCounter.Bump(104)
|
||||
err = routes.LikeTopicSubmit(w,req,user,extraData)
|
||||
default:
|
||||
counters.RouteViewCounter.Bump(104)
|
||||
counters.RouteViewCounter.Bump(105)
|
||||
err = routes.ViewTopic(w,req,user, extraData)
|
||||
}
|
||||
case "/reply":
|
||||
|
@ -1822,7 +1834,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(105)
|
||||
counters.RouteViewCounter.Bump(106)
|
||||
err = routes.CreateReplySubmit(w,req,user)
|
||||
case "/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1837,7 +1849,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(106)
|
||||
counters.RouteViewCounter.Bump(107)
|
||||
err = routes.ReplyEditSubmit(w,req,user,extraData)
|
||||
case "/reply/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1852,7 +1864,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(107)
|
||||
counters.RouteViewCounter.Bump(108)
|
||||
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
|
||||
case "/reply/like/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1873,7 +1885,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(108)
|
||||
counters.RouteViewCounter.Bump(109)
|
||||
err = routes.ReplyLikeSubmit(w,req,user,extraData)
|
||||
}
|
||||
case "/profile":
|
||||
|
@ -1891,7 +1903,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(109)
|
||||
counters.RouteViewCounter.Bump(110)
|
||||
err = routes.ProfileReplyCreateSubmit(w,req,user)
|
||||
case "/profile/reply/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1906,7 +1918,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(110)
|
||||
counters.RouteViewCounter.Bump(111)
|
||||
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
|
||||
case "/profile/reply/delete/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1921,7 +1933,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(111)
|
||||
counters.RouteViewCounter.Bump(112)
|
||||
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
|
||||
}
|
||||
case "/poll":
|
||||
|
@ -1939,19 +1951,19 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(112)
|
||||
counters.RouteViewCounter.Bump(113)
|
||||
err = routes.PollVote(w,req,user,extraData)
|
||||
case "/poll/results/":
|
||||
counters.RouteViewCounter.Bump(113)
|
||||
counters.RouteViewCounter.Bump(114)
|
||||
err = routes.PollResults(w,req,user,extraData)
|
||||
}
|
||||
case "/accounts":
|
||||
switch(req.URL.Path) {
|
||||
case "/accounts/login/":
|
||||
counters.RouteViewCounter.Bump(114)
|
||||
counters.RouteViewCounter.Bump(115)
|
||||
err = routes.AccountLogin(w,req,user)
|
||||
case "/accounts/create/":
|
||||
counters.RouteViewCounter.Bump(115)
|
||||
counters.RouteViewCounter.Bump(116)
|
||||
err = routes.AccountRegister(w,req,user)
|
||||
case "/accounts/logout/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
|
@ -1966,7 +1978,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(116)
|
||||
counters.RouteViewCounter.Bump(117)
|
||||
err = routes.AccountLogout(w,req,user)
|
||||
case "/accounts/login/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1975,10 +1987,10 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(117)
|
||||
counters.RouteViewCounter.Bump(118)
|
||||
err = routes.AccountLoginSubmit(w,req,user)
|
||||
case "/accounts/mfa_verify/":
|
||||
counters.RouteViewCounter.Bump(118)
|
||||
counters.RouteViewCounter.Bump(119)
|
||||
err = routes.AccountLoginMFAVerify(w,req,user)
|
||||
case "/accounts/mfa_verify/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1987,7 +1999,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(119)
|
||||
counters.RouteViewCounter.Bump(120)
|
||||
err = routes.AccountLoginMFAVerifySubmit(w,req,user)
|
||||
case "/accounts/create/submit/":
|
||||
err = common.ParseForm(w,req,user)
|
||||
|
@ -1996,7 +2008,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
return
|
||||
}
|
||||
|
||||
counters.RouteViewCounter.Bump(120)
|
||||
counters.RouteViewCounter.Bump(121)
|
||||
err = routes.AccountRegisterSubmit(w,req,user)
|
||||
}
|
||||
/*case "/sitemaps": // TODO: Count these views
|
||||
|
@ -2013,7 +2025,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
w.Header().Del("Content-Type")
|
||||
w.Header().Del("Content-Encoding")
|
||||
}
|
||||
counters.RouteViewCounter.Bump(122)
|
||||
counters.RouteViewCounter.Bump(123)
|
||||
req.URL.Path += extraData
|
||||
// TODO: Find a way to propagate errors up from this?
|
||||
router.UploadHandler(w,req) // TODO: Count these views
|
||||
|
@ -2023,14 +2035,14 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
// TODO: Add support for favicons and robots.txt files
|
||||
switch(extraData) {
|
||||
case "robots.txt":
|
||||
counters.RouteViewCounter.Bump(124)
|
||||
counters.RouteViewCounter.Bump(125)
|
||||
err = routes.RobotsTxt(w,req)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
}
|
||||
return
|
||||
/*case "sitemap.xml":
|
||||
counters.RouteViewCounter.Bump(125)
|
||||
counters.RouteViewCounter.Bump(126)
|
||||
err = routes.SitemapXml(w,req)
|
||||
if err != nil {
|
||||
router.handleError(err,w,req,user)
|
||||
|
@ -2046,7 +2058,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
router.RUnlock()
|
||||
|
||||
if ok {
|
||||
counters.RouteViewCounter.Bump(121) // TODO: Be more specific about *which* dynamic route it is
|
||||
counters.RouteViewCounter.Bump(122) // TODO: Be more specific about *which* dynamic route it is
|
||||
req.URL.Path += extraData
|
||||
err = handle(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -2061,7 +2073,7 @@ func (router *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, u
|
|||
} else {
|
||||
router.DumpRequest(req,"Bad Route")
|
||||
}
|
||||
counters.RouteViewCounter.Bump(126)
|
||||
counters.RouteViewCounter.Bump(127)
|
||||
common.NotFound(w,req,nil)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"Name": "english",
|
||||
|
||||
"Levels": {
|
||||
"Level": "Level {0}",
|
||||
"Level": "<span class='level_hideable'>Level </span>{0}",
|
||||
"LevelMax": ""
|
||||
},
|
||||
|
||||
|
@ -361,7 +361,6 @@
|
|||
"topic.plus":"+",
|
||||
"topic.plus_one":"+1",
|
||||
"topic.gap_up":" up",
|
||||
"topic.level":"Level",
|
||||
"topic.edit_button_text":"Edit",
|
||||
"topic.delete_button_text":"Delete",
|
||||
"topic.ip_button_text":"IP",
|
||||
|
@ -448,7 +447,6 @@
|
|||
|
||||
"account_dash_2fa_setup":"Setup your two-factor authentication.",
|
||||
"account_dash_2fa_manage":"Remove or manage your two-factor authentication.",
|
||||
"account_dash_level":"Level %d",
|
||||
"account_dash_security_notice":"Security",
|
||||
"account_avatar_select":"Select",
|
||||
"account_avatar_update_button":"Upload",
|
||||
|
@ -596,7 +594,6 @@
|
|||
"topic.reply_add_poll_button":"Add Poll",
|
||||
"topic.reply_add_file_button":"Add File",
|
||||
|
||||
"topic.level_prefix":"Level ",
|
||||
"topic.your_information":"Your information",
|
||||
|
||||
"paginator_less_than":"<",
|
||||
|
|
|
@ -48,6 +48,7 @@ func buildUserRoutes() {
|
|||
userGroup := newRouteGroup("/user/")
|
||||
userGroup.Routes(
|
||||
View("routes.ViewProfile", "/user/").LitBefore("req.URL.Path += extraData"),
|
||||
|
||||
MemberView("routes.AccountEdit", "/user/edit/"),
|
||||
MemberView("routes.AccountEditPassword", "/user/edit/password/"),
|
||||
Action("routes.AccountEditPasswordSubmit", "/user/edit/password/submit/"), // TODO: Full test this
|
||||
|
@ -59,6 +60,8 @@ func buildUserRoutes() {
|
|||
Action("routes.AccountEditMFADisableSubmit", "/user/edit/mfa/disable/submit/"),
|
||||
MemberView("routes.AccountEditEmail", "/user/edit/email/"),
|
||||
Action("routes.AccountEditEmailTokenSubmit", "/user/edit/token/", "extraData"),
|
||||
|
||||
MemberView("routes.LevelList", "/user/levels/"),
|
||||
)
|
||||
addRouteGroup(userGroup)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"encoding/hex"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
|
@ -761,3 +762,37 @@ func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user co
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LevelList(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||
header, ferr := common.UserCheck(w, r, &user)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
}
|
||||
header.Title = "Level Progress"
|
||||
|
||||
var fScores = common.GetLevels(20)
|
||||
var levels = make([]common.LevelListItem, len(fScores))
|
||||
for i, fScore := range fScores {
|
||||
var status string
|
||||
if user.Level > i {
|
||||
status = "complete"
|
||||
} else if user.Level < i {
|
||||
status = "future"
|
||||
} else {
|
||||
status = "inprogress"
|
||||
}
|
||||
iScore := int(math.Ceil(fScore))
|
||||
perc := int(math.Ceil((fScore / float64(user.Score)) * 100))
|
||||
levels[i] = common.LevelListItem{i, iScore, status, perc * 2}
|
||||
}
|
||||
|
||||
pi := common.LevelListPage{header, levels[1:]}
|
||||
if common.RunPreRenderHook("pre_render_level_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
err := common.Templates.ExecuteTemplate(w, "level_list.html", pi)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
</div>
|
||||
<div id="dash_right" class="coldyn_item">
|
||||
<div class="rowitem">{{if not .MFASetup}}<a href="/user/edit/mfa/setup/">{{lang "account_dash_2fa_setup"}}</a>{{else}}<a href="/user/edit/mfa/">{{lang "account_dash_2fa_manage"}}</a>{{end}} <span class="dash_security">{{lang "account_dash_security_notice"}}</span></div>
|
||||
<div class="rowitem">Level {{.CurrentUser.Level}}: [{{.CurrentScore}} / {{.NextScore}}] <span class="account_soon">{{lang "account_coming_soon"}}</span></div>
|
||||
<div class="rowitem">
|
||||
<a href="/user/levels/">{{level .CurrentUser.Level}}: [{{.CurrentScore}} / {{.NextScore}}]</a> <span class="account_soon">{{lang "account_coming_soon"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{{template "header.html" . }}
|
||||
<main>
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>{{.Title}}</h1></div>
|
||||
</div>
|
||||
{{range .Levels}}
|
||||
<div class="rowblock">
|
||||
<div class="rowitem passive rowmsg level_{{.Status}}">
|
||||
<div>{{level .Level}}</div>
|
||||
<div class="progressWrap"{{if eq .Status "inprogress"}} style="width: {{.Percentage}}%;"{{end}}>
|
||||
<div>Score: {{.Score}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</main>
|
||||
{{template "footer.html" . }}
|
|
@ -12,10 +12,12 @@
|
|||
<span class="profileName" title="{{.ProfileOwner.Name}}">{{.ProfileOwner.Name}}</span>{{if .ProfileOwner.Tag}}<span class="username" title="{{.ProfileOwner.Tag}}">{{.ProfileOwner.Tag}}</span>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="passiveBlock">
|
||||
<div class="levelBlock">
|
||||
<div class="rowitem passive">
|
||||
<a class="profile_menu_item">Level {{.ProfileOwner.Level}}: [{{.CurrentScore}} / {{.NextScore}}]</a>
|
||||
<a class="profile_menu_item">{{level .ProfileOwner.Level}}: [{{.CurrentScore}} / {{.NextScore}}]</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="passiveBlock">
|
||||
{{if not .CurrentUser.Loggedin}}<div class="rowitem passive">
|
||||
<a class="profile_menu_item">{{lang "profile_login_for_options"}}</a>
|
||||
</div>{{else}}
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
|
||||
<a class="username hide_on_micro like_count" aria-label="{{lang "topic.like_count_aria"}}">{{.Topic.LikeCount}}</a><a class="username hide_on_micro like_count_label" title="{{lang "topic.like_count_tooltip"}}"></a>
|
||||
|
||||
{{if .Topic.Tag}}<a class="username hide_on_micro user_tag">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro level" aria-label="{{lang "topic.level_aria"}}">{{.Topic.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="{{lang "topic.level_tooltip"}}"></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="{{lang "topic.level_aria"}}">{{level .Topic.Level}}</a><a class="username hide_on_micro level_label" style="float:right;" title="{{lang "topic.level_tooltip"}}"></a>{{end}}
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
<div class="avatar_item" style="background-image: url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;"> </div>
|
||||
<div class="user_meta">
|
||||
<a href="{{.Topic.UserLink}}" class="the_name" rel="author">{{.Topic.CreatedByName}}</a>
|
||||
{{if .Topic.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Topic.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{lang "topic.level_prefix"}}{{.Topic.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
{{if .Topic.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Topic.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Topic.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="poll_voter_{{.Poll.ID}}" class="content_container poll_voter">
|
||||
|
@ -68,7 +68,7 @@
|
|||
<div class="avatar_item" style="background-image: url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;"> </div>
|
||||
<div class="user_meta">
|
||||
<a href="{{.Topic.UserLink}}" class="the_name" rel="author">{{.Topic.CreatedByName}}</a>
|
||||
{{if .Topic.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Topic.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{lang "topic.level_prefix"}}{{.Topic.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
{{if .Topic.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Topic.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Topic.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content_container">
|
||||
|
@ -109,7 +109,7 @@
|
|||
<div class="avatar_item" style="background-image: url({{.CurrentUser.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;"> </div>
|
||||
<div class="user_meta">
|
||||
<a href="{{.CurrentUser.Link}}" class="the_name" rel="author">{{.CurrentUser.Name}}</a>
|
||||
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{lang "topic.level_prefix"}}{{.CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
{{if .CurrentUser.Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.CurrentUser.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .CurrentUser.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rowblock topic_reply_form quick_create_form" aria-label="{{lang "topic.reply_aria"}}">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="avatar_item" style="background-image: url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px -10px;"> </div>
|
||||
<div class="user_meta">
|
||||
<a href="{{.UserLink}}" class="the_name" rel="author">{{.CreatedByName}}</a>
|
||||
{{if .Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{lang "topic.level_prefix"}}{{.Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
{{if .Tag}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">{{.Tag}}</div><div class="tag_post"></div></div>{{else}}<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">{{level .Level}}</div><div class="tag_post"></div></div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="content_container"{{if .ActionType}} style="margin-left: 0px;"{{end}}>
|
||||
|
|
|
@ -55,7 +55,9 @@ li a {
|
|||
padding-bottom: 21px;
|
||||
color: #dddddd;
|
||||
}
|
||||
.menu_alerts .alert_bell, .menu_alerts .alert_counter, .menu_alerts:not(.selectedAlert) .alertList {
|
||||
.menu_alerts .alert_bell,
|
||||
.menu_alerts .alert_counter,
|
||||
.menu_alerts:not(.selectedAlert) .alertList {
|
||||
display: none;
|
||||
}
|
||||
.alertList {
|
||||
|
@ -70,6 +72,11 @@ li a {
|
|||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
.alertItem {
|
||||
padding: 10px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
.alertItem.withAvatar {
|
||||
background: none !important;
|
||||
height: 66px;
|
||||
|
@ -853,6 +860,44 @@ input[type=checkbox]:checked + label .sel {
|
|||
background-color: #444444;
|
||||
}
|
||||
|
||||
.level_complete, .level_future, .level_inprogress {
|
||||
display: flex;
|
||||
}
|
||||
.level_complete {
|
||||
background-color: rgb(68, 93, 68) !important;
|
||||
width: 100%;
|
||||
}
|
||||
.level_future {
|
||||
background-color: rgb(88, 68, 68) !important;
|
||||
width: 100%;
|
||||
}
|
||||
.progressWrap {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
}
|
||||
/* CSS behaves in stupid ways, so we need to be very specific about this */
|
||||
.rowblock:not(.topic_list):not(.rowhead):not(.opthead) .rowitem.level_inprogress:not(.post_item) {
|
||||
padding: 0px;
|
||||
}
|
||||
.level_inprogress > div {
|
||||
display: flex;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
padding-left: 12px;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
background-color: rgb(68, 93, 68) !important;
|
||||
}
|
||||
.level_inprogress .progressWrap {
|
||||
width: 100%;
|
||||
padding-left: 0px;
|
||||
padding-right: 12px;
|
||||
background-color: rgb(68, 68, 68) !important;
|
||||
}
|
||||
.level_inprogress .progressWrap div {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media(max-width: 600px) {
|
||||
.rowhead h1, .opthead h1, .colstack_head h1 {
|
||||
font-size: 19px;
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
.profileName {
|
||||
font-size: 21px;
|
||||
}
|
||||
.topBlock, .passiveBlock {
|
||||
.topBlock, .levelBlock, .passiveBlock {
|
||||
background-color: #444444;
|
||||
border-radius: 3px;
|
||||
width: 180px;
|
||||
padding: 16px;
|
||||
}
|
||||
.passiveBlock {
|
||||
.levelBlock, .passiveBlock {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
|
|
@ -316,9 +316,6 @@ a {
|
|||
.flag_label:before {
|
||||
content: "{{index .Phrases "topic.flag_button_text"}}";
|
||||
}
|
||||
.level_label:before {
|
||||
content: "{{index .Phrases "topic.level"}}";
|
||||
}
|
||||
|
||||
.like_count_label, .like_count {
|
||||
display: none;
|
||||
|
|
|
@ -707,6 +707,9 @@ button.username {
|
|||
color: #505050;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.level_hideable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-top: 23px;
|
||||
|
|
Loading…
Reference in New Issue