diff --git a/common/counters/langs.go b/common/counters/langs.go index 9f78ccbc..d2f6a384 100644 --- a/common/counters/langs.go +++ b/common/counters/langs.go @@ -96,12 +96,11 @@ var langCodes = []string{ type DefaultLangViewCounter struct { buckets []*RWMutexCounterBucket //[OSID]count codesToIndices map[string]int - insert *sql.Stmt + + insert *sql.Stmt } -func NewDefaultLangViewCounter() (*DefaultLangViewCounter, error) { - acc := qgen.NewAcc() - +func NewDefaultLangViewCounter(acc *qgen.Accumulator) (*DefaultLangViewCounter, error) { var langBuckets = make([]*RWMutexCounterBucket, len(langCodes)) for bucketID, _ := range langBuckets { langBuckets[bucketID] = &RWMutexCounterBucket{counter: 0} diff --git a/common/counters/routes.go b/common/counters/routes.go index d60d2764..ac55aff8 100644 --- a/common/counters/routes.go +++ b/common/counters/routes.go @@ -12,8 +12,7 @@ type DefaultRouteViewCounter struct { insert *sql.Stmt } -func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) { - acc := qgen.NewAcc() +func NewDefaultRouteViewCounter(acc *qgen.Accumulator) (*DefaultRouteViewCounter, error) { var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum)) for bucketID, _ := range routeBuckets { routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0} diff --git a/common/counters/systems.go b/common/counters/systems.go index 3f24367a..163dc0e9 100644 --- a/common/counters/systems.go +++ b/common/counters/systems.go @@ -14,8 +14,7 @@ type DefaultOSViewCounter struct { insert *sql.Stmt } -func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) { - acc := qgen.NewAcc() +func NewDefaultOSViewCounter(acc *qgen.Accumulator) (*DefaultOSViewCounter, error) { var osBuckets = make([]*RWMutexCounterBucket, len(osMapEnum)) for bucketID, _ := range osBuckets { osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0} diff --git a/common/files.go b/common/files.go index 468945ed..a8ca909a 100644 --- a/common/files.go +++ b/common/files.go @@ -27,7 +27,7 @@ var staticFileMutex sync.RWMutex type SFile struct { Data []byte GzipData []byte - Sha256 []byte + Sha256 string Pos int64 Length int64 GzipLength int64 @@ -240,7 +240,7 @@ func (list SFileList) JSTmplInit() error { // Get a checksum for CSPs and cache busting hasher := sha256.New() hasher.Write(data) - checksum := []byte(hex.EncodeToString(hasher.Sum(nil))) + checksum := hex.EncodeToString(hasher.Sum(nil)) list.Set("/static/"+path, SFile{data, gzipData, checksum, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) @@ -267,7 +267,7 @@ func (list SFileList) Init() error { // Get a checksum for CSPs and cache busting hasher := sha256.New() hasher.Write(data) - checksum := []byte(hex.EncodeToString(hasher.Sum(nil))) + checksum := hex.EncodeToString(hasher.Sum(nil)) // Avoid double-compressing images var gzipData []byte @@ -318,7 +318,7 @@ func (list SFileList) Add(path string, prefix string) error { // Get a checksum for CSPs and cache busting hasher := sha256.New() hasher.Write(data) - checksum := []byte(hex.EncodeToString(hasher.Sum(nil))) + checksum := hex.EncodeToString(hasher.Sum(nil)) list.Set("/static"+path, SFile{data, gzipData, checksum, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) diff --git a/common/pages.go b/common/pages.go index 8303bd32..fe70a062 100644 --- a/common/pages.go +++ b/common/pages.go @@ -10,14 +10,20 @@ import ( "github.com/Azareal/Gosora/common/phrases" ) +type HResource struct { + Name string + Hash string +} + // TODO: Allow resources in spots other than /static/ and possibly even external domains (e.g. CDNs) // TODO: Preload Trumboyg on Cosora on the forum list type Header struct { Title string //Title []byte // Experimenting with []byte for increased efficiency, let's avoid converting too many things to []byte, as it involves a lot of extra boilerplate NoticeList []string - Scripts []string - PreScriptsAsync []string + Scripts []HResource + PreScriptsAsync []HResource + ScriptsAsync []HResource //Preload []string Stylesheets []string Widgets PageWidgets @@ -44,11 +50,41 @@ type Header struct { } func (header *Header) AddScript(name string) { - header.Scripts = append(header.Scripts, name) + fname := "/static/" + name + var hash string + if fname[0] == '/' && fname[1] != '/' { + file, ok := StaticFiles.Get(fname) + if ok { + hash = file.Sha256 + } + } + //log.Print("name:", name) + //log.Print("hash:", hash) + header.Scripts = append(header.Scripts, HResource{name, hash}) } func (header *Header) AddPreScriptAsync(name string) { - header.PreScriptsAsync = append(header.PreScriptsAsync, name) + fname := "/static/" + name + var hash string + if fname[0] == '/' && fname[1] != '/' { + file, ok := StaticFiles.Get(fname) + if ok { + hash = file.Sha256 + } + } + header.PreScriptsAsync = append(header.PreScriptsAsync, HResource{name, hash}) +} + +func (header *Header) AddScriptAsync(name string) { + fname := "/static/" + name + var hash string + if fname[0] == '/' && fname[1] != '/' { + file, ok := StaticFiles.Get(fname) + if ok { + hash = file.Sha256 + } + } + header.ScriptsAsync = append(header.ScriptsAsync, HResource{name, hash}) } /*func (header *Header) Preload(name string) { diff --git a/common/routes_common.go b/common/routes_common.go index f2946076..ca15db58 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -131,7 +131,11 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header if ext == "css" { header.AddSheet(resource.Name) } else if ext == "js" { - header.AddScript(resource.Name) + if resource.Async { + header.AddScriptAsync(resource.Name) + } else { + header.AddScript(resource.Name) + } } } } @@ -229,7 +233,11 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (header *Head if ext == "css" { header.AddSheet(resource.Name) } else if ext == "js" { - header.AddScript(resource.Name) + if resource.Async { + header.AddScriptAsync(resource.Name) + } else { + header.AddScript(resource.Name) + } } } } diff --git a/common/template_init.go b/common/template_init.go index 86a1e3d1..a99b0a12 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -109,8 +109,9 @@ func tmplInitHeaders(user User, user2 User, user3 User) (*Header, *Header, *Head CurrentUser: user, NoticeList: []string{"test"}, Stylesheets: []string{"panel.css"}, - Scripts: []string{"whatever.js"}, - PreScriptsAsync: []string{"whatever.js"}, + Scripts: []HResource{HResource{"whatever.js", "d"}}, + PreScriptsAsync: []HResource{HResource{"whatever.js", "d"}}, + ScriptsAsync: []HResource{HResource{"whatever.js", "d"}}, Widgets: PageWidgets{ LeftSidebar: template.HTML("lalala"), }, diff --git a/common/templates/templates.go b/common/templates/templates.go index c183e354..87fdc6c8 100644 --- a/common/templates/templates.go +++ b/common/templates/templates.go @@ -2,11 +2,13 @@ package tmpl import ( "bytes" + "fmt" "io/ioutil" "log" "os" "path/filepath" "reflect" + "runtime/debug" "strconv" "strings" "text/template/parse" @@ -70,7 +72,8 @@ type CTemplateSet struct { themeName string perThemeTmpls map[string]bool - logger *log.Logger + logger *log.Logger + loggerf *os.File } func NewCTemplateSet(in string) *CTemplateSet { @@ -110,7 +113,8 @@ func NewCTemplateSet(in string) *CTemplateSet { "dyntmpl": true, "index": true, }, - logger: log.New(f, "", log.LstdFlags), + logger: log.New(f, "", log.LstdFlags), + loggerf: f, } } @@ -155,6 +159,7 @@ func (c *CTemplateSet) ResetLogs(in string) { panic(err) } c.logger = log.New(f, "", log.LstdFlags) + c.loggerf = f } type SkipBlock struct { @@ -268,6 +273,19 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe } func (c *CTemplateSet) compile(name string, content string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) { + defer func() { + r := recover() + if r != nil { + fmt.Println(r) + debug.PrintStack() + err := c.loggerf.Sync() + if err != nil { + fmt.Println(err) + } + log.Fatal("") + return + } + }() //c.dumpCall("compile", name, content, expects, expectsInt, varList, imports) //c.detailf("c: %+v\n", c) c.importMap = map[string]string{} @@ -1460,6 +1478,16 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V c.addText(con, []byte("false")) con.Push("endelse", "}\n") return + case reflect.Slice: + if val.Len() == 0 { + c.critical("varname:", varname) + panic("The sample data needs at-least one or more elements for the slices. We're looking into removing this requirement at some point!") + } + item := val.Index(0) + if item.Type().Name() != "uint8" { // uint8 == byte, complicated because it's a type alias + panic("unable to format " + item.Type().Name() + " as text") + } + base = varname case reflect.String: if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") { varname = "string(" + varname + ")" diff --git a/common/theme.go b/common/theme.go index 2e2cecba..626feeab 100644 --- a/common/theme.go +++ b/common/theme.go @@ -77,6 +77,7 @@ type ThemeResource struct { Name string Location string Loggedin bool // Only serve this resource to logged in users + Async bool } type ThemeMapTmplToDock struct { @@ -162,7 +163,7 @@ func (theme *Theme) AddThemeStaticFiles() error { // Get a checksum for CSPs and cache busting hasher := sha256.New() hasher.Write(data) - checksum := []byte(hex.EncodeToString(hasher.Sum(nil))) + checksum := hex.EncodeToString(hasher.Sum(nil)) StaticFiles.Set("/static/"+theme.Name+path, SFile{data, gzipData, checksum, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)}) diff --git a/common/websockets.go b/common/websockets.go index 38adec05..1797941f 100644 --- a/common/websockets.go +++ b/common/websockets.go @@ -92,6 +92,12 @@ func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteErr currentPage = string(msgblocks[1]) wsPageResponses(wsUser, conn, currentPage) } + } else if bytes.HasPrefix(msg, []byte("resume ")) { + msgblocks := bytes.SplitN(msg, []byte(" "), 2) + if len(msgblocks) < 2 { + continue + } + //log.Print("resuming on " + string(msgblocks[1])) } /*if bytes.Equal(message,[]byte(`start-view`)) { } else if bytes.Equal(message,[]byte(`end-view`)) { diff --git a/docs/configuration.md b/docs/configuration.md index cfbad8b4..bce91e23 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -84,7 +84,7 @@ MaxTopicTitleLength - The maximum length that a topic can be. Please note that t MaxUsernameLength - The maximum length that a user's name can be. Please note that this measures the number of bytes and may differ from language to language with it being equal to a letter in English and being two bytes in others. -ReadTimeout - The number of seconds that we are allowed to take to fully read a request. Defaults to 5. +ReadTimeout - The number of seconds that we are allowed to take to fully read a request. Defaults to 8. WriteTimeout - The number of seconds that a route is allowed to run for before the request is automatically terminated. Defaults to 10. diff --git a/gen_router.go b/gen_router.go index 231448fd..efd5b96e 100644 --- a/gen_router.go +++ b/gen_router.go @@ -164,6 +164,7 @@ var RouteMap = map[string]interface{}{ "routes.RobotsTxt": routes.RobotsTxt, "routes.SitemapXml": routes.SitemapXml, "routes.BadRoute": routes.BadRoute, + "routes.HTTPSRedirect": routes.HTTPSRedirect, } // ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS @@ -309,6 +310,7 @@ var routeMapEnum = map[string]int{ "routes.RobotsTxt": 138, "routes.SitemapXml": 139, "routes.BadRoute": 140, + "routes.HTTPSRedirect": 141, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -452,6 +454,7 @@ var reverseRouteMapEnum = map[int]string{ 138: "routes.RobotsTxt", 139: "routes.SitemapXml", 140: "routes.BadRoute", + 141: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -600,6 +603,17 @@ func (writ *WriterIntercept) WriteHeader(code int) { writ.ResponseWriter.WriteHeader(code) } +// HTTPSRedirect is a connection handler which redirects all HTTP requests to HTTPS +type HTTPSRedirect struct { +} + +func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Connection", "close") + counters.RouteViewCounter.Bump(141) + dest := "https://" + req.Host + req.URL.String() + http.Redirect(w, req, dest, http.StatusTemporaryRedirect) +} + type GenRouter struct { UploadHandler func(http.ResponseWriter, *http.Request) extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError diff --git a/langs/english.json b/langs/english.json index 09f11142..941ce2d8 100644 --- a/langs/english.json +++ b/langs/english.json @@ -708,6 +708,8 @@ "option_yes":"Yes", "option_no":"No", + "panel_back_to_site":"Back to Site", + "panel_welcome":"Welcome ", "panel_menu_head":"Control Panel", "panel_menu_aria":"The control panel menu", "panel_menu_users":"Users", diff --git a/main.go b/main.go index e9047aad..de7e75ae 100644 --- a/main.go +++ b/main.go @@ -28,7 +28,6 @@ import ( "github.com/Azareal/Gosora/common/counters" "github.com/Azareal/Gosora/common/phrases" "github.com/Azareal/Gosora/query_gen" - "github.com/Azareal/Gosora/routes" "github.com/fsnotify/fsnotify" "github.com/pkg/errors" ) @@ -181,15 +180,15 @@ func afterDBInit() (err error) { if err != nil { return errors.WithStack(err) } - counters.OSViewCounter, err = counters.NewDefaultOSViewCounter() + counters.OSViewCounter, err = counters.NewDefaultOSViewCounter(acc) if err != nil { return errors.WithStack(err) } - counters.LangViewCounter, err = counters.NewDefaultLangViewCounter() + counters.LangViewCounter, err = counters.NewDefaultLangViewCounter(acc) if err != nil { return errors.WithStack(err) } - counters.RouteViewCounter, err = counters.NewDefaultRouteViewCounter() + counters.RouteViewCounter, err = counters.NewDefaultRouteViewCounter(acc) if err != nil { return errors.WithStack(err) } @@ -472,7 +471,7 @@ func startServer() { var newServer = func(addr string, handler http.Handler) *http.Server { rtime := common.Config.ReadTimeout if rtime == 0 { - rtime = 5 + rtime = 8 } else if rtime == -1 { rtime = 0 } @@ -527,7 +526,7 @@ func startServer() { // TODO: Redirect to port 443 go func() { log.Print("Listening on port 80") - common.StoppedServer(newServer(":80", &routes.HTTPSRedirect{}).ListenAndServe()) + common.StoppedServer(newServer(":80", &HTTPSRedirect{}).ListenAndServe()) }() } log.Printf("Listening on port %s", common.Site.Port) diff --git a/public/analytics.js b/public/analytics.js index cf738f72..a47a8a1a 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -59,4 +59,6 @@ function buildStatsChart(rawLabels, seriesData, timeRange, legendNames) { labels: labels, series: seriesData, }, config); -} \ No newline at end of file +} + +runInitHook("analytics_loaded"); \ No newline at end of file diff --git a/public/global.js b/public/global.js index c3f54a22..55ddb348 100644 --- a/public/global.js +++ b/public/global.js @@ -193,7 +193,7 @@ function wsAlertEvent(data) { updateAlertList(generalAlerts/*, alist*/); } -function runWebSockets() { +function runWebSockets(resume = false) { if(window.location.protocol == "https:") { conn = new WebSocket("wss://" + document.location.host + "/ws/"); } else conn = new WebSocket("ws://" + document.location.host + "/ws/"); @@ -206,6 +206,7 @@ function runWebSockets() { conn.onopen = () => { console.log("The WebSockets connection was opened"); conn.send("page " + document.location.pathname + '\r'); + if(resume) conn.send("resume " + Math.round((new Date()).getTime() / 1000) + '\r'); // TODO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on if(me.User.ID > 0) Notification.requestPermission(); } @@ -213,23 +214,22 @@ function runWebSockets() { conn.onclose = () => { conn = false; console.log("The WebSockets connection was closed"); - let backoff = 1000; + let backoff = 0.8; if(wsBackoff < 0) wsBackoff = 0; - else if(wsBackoff > 12) backoff = 13000; - else if(wsBackoff > 5) backoff = 7000; + else if(wsBackoff > 12) backoff = 11; + else if(wsBackoff > 5) backoff = 5; wsBackoff++; setTimeout(() => { var alertMenuList = document.getElementsByClassName("menu_alerts"); - for(var i = 0; i < alertMenuList.length; i++) { - loadAlerts(alertMenuList[i]); - } - runWebSockets(); - }, 60 * backoff); + for(var i = 0; i < alertMenuList.length; i++) loadAlerts(alertMenuList[i]); + runWebSockets(true); + }, backoff * 60 * 1000); if(wsBackoff > 0) { - if(wsBackoff <= 5) setTimeout(() => wsBackoff--, 60 * 4000); - else if(wsBackoff <= 12) setTimeout(() => wsBackoff--, 60 * 20000); + if(wsBackoff <= 5) setTimeout(() => wsBackoff--, 5.5 * 60 * 1000); + else if(wsBackoff <= 12) setTimeout(() => wsBackoff--, 11.5 * 60 * 1000); + else setTimeout(() => wsBackoff--, 20 * 60 * 1000); } } @@ -333,16 +333,14 @@ function runWebSockets() { notifyOnScriptW("template_alert", (e) => { if(e!=undefined) console.log("failed alert? why?", e) }, () => { - console.log("ha") + //console.log("ha") if(!Template_alert) throw("template function not found"); addInitHook("after_phrases", () => { // TODO: The load part of loadAlerts could be done asynchronously while the update of the DOM could be deferred $(document).ready(() => { alertsInitted = true; var alertMenuList = document.getElementsByClassName("menu_alerts"); - for(var i = 0; i < alertMenuList.length; i++) { - loadAlerts(alertMenuList[i]); - } + for(var i = 0; i < alertMenuList.length; i++) loadAlerts(alertMenuList[i]); if(window["WebSocket"]) runWebSockets(); }); }); diff --git a/public/init.js b/public/init.js index 2e983d2c..4639886a 100644 --- a/public/init.js +++ b/public/init.js @@ -4,7 +4,7 @@ var me = {}; var phraseBox = {}; if(tmplInits===undefined) var tmplInits = {}; var tmplPhrases = []; // [key] array of phrases indexed by order of use -var hooks = { +var hooks = { // Shorten this list by binding the hooks just in time? "pre_iffe": [], "pre_init": [], "start_init": [], @@ -15,6 +15,7 @@ var hooks = { "open_edit":[], "close_edit":[], "edit_item_pre_bind":[], + "analytics_loaded":[], }; var ranInitHooks = {} diff --git a/router_gen/main.go b/router_gen/main.go index af528789..7cbabc11 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -174,6 +174,7 @@ func main() { mapIt("routes.RobotsTxt") mapIt("routes.SitemapXml") mapIt("routes.BadRoute") + mapIt("routes.HTTPSRedirect") tmplVars.AllRouteNames = allRouteNames tmplVars.AllRouteMap = allRouteMap @@ -381,6 +382,17 @@ func (writ *WriterIntercept) WriteHeader(code int) { writ.ResponseWriter.WriteHeader(code) } +// HTTPSRedirect is a connection handler which redirects all HTTP requests to HTTPS +type HTTPSRedirect struct { +} + +func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Connection", "close") + counters.RouteViewCounter.Bump({{ index .AllRouteMap "routes.HTTPSRedirect" }}) + dest := "https://" + req.Host + req.URL.String() + http.Redirect(w, req, dest, http.StatusTemporaryRedirect) +} + type GenRouter struct { UploadHandler func(http.ResponseWriter, *http.Request) extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError diff --git a/routes/common.go b/routes/common.go index 42b04239..7e0e0fab 100644 --- a/routes/common.go +++ b/routes/common.go @@ -29,8 +29,9 @@ func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, hea } // TODO: Expand this to non-HTTPS requests too if !header.LooseCSP && common.Site.EnableSsl { - w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; upgrade-insecure-requests") + w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self' www.youtube-nocookie.com;upgrade-insecure-requests") } + header.AddScript("global.js") if header.CurrentUser.IsAdmin { header.Elapsed1 = time.Since(header.StartedAt).String() } diff --git a/routes/panel/analytics.go b/routes/panel/analytics.go index 49282721..83d9299c 100644 --- a/routes/panel/analytics.go +++ b/routes/panel/analytics.go @@ -125,7 +125,7 @@ func PreAnalyticsDetail(w http.ResponseWriter, r *http.Request, user *common.Use } basePage.AddSheet("chartist/chartist.min.css") basePage.AddScript("chartist/chartist.min.js") - basePage.AddScript("analytics.js") + basePage.AddScriptAsync("analytics.js") return basePage, nil } diff --git a/routes/panel/common.go b/routes/panel/common.go index 4d6874d7..e57b77ef 100644 --- a/routes/panel/common.go +++ b/routes/panel/common.go @@ -22,6 +22,7 @@ func successRedirect(dest string, w http.ResponseWriter, r *http.Request, isJs b } func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, header *common.Header, pi interface{}) common.RouteError { + header.AddScript("global.js") if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &header.CurrentUser, pi) { return nil } diff --git a/routes/stubs.go b/routes/stubs.go index 66801d85..f673f0d6 100644 --- a/routes/stubs.go +++ b/routes/stubs.go @@ -1,17 +1,5 @@ package routes -import "net/http" - -// HTTPSRedirect is a connection handler which redirects all HTTP requests to HTTPS -type HTTPSRedirect struct { -} - -func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Connection", "close") - dest := "https://" + req.Host + req.URL.String() - http.Redirect(w, req, dest, http.StatusTemporaryRedirect) -} - // Temporary stubs for view tracking func DynamicRoute() { } @@ -19,3 +7,7 @@ func UploadedFile() { } func BadRoute() { } + +// Real implementation is in router_gen/main.go, this is just a stub to map the analytics onto +func HTTPSRedirect() { +} diff --git a/templates/header.html b/templates/header.html index cb39c6e6..5d132488 100644 --- a/templates/header.html +++ b/templates/header.html @@ -6,13 +6,14 @@ {{range .Header.Stylesheets}} {{end}} {{range .Header.PreScriptsAsync}} - {{end}} + {{end}} + {{range .Header.ScriptsAsync}} + {{end}} {{range .Header.Scripts}} - {{end}} - + {{end}} {{if .Header.MetaDesc}}{{end}} {{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}} diff --git a/templates/panel_adminlogs.html b/templates/panel_adminlogs.html index ddd6e7a3..c9fbc462 100644 --- a/templates/panel_adminlogs.html +++ b/templates/panel_adminlogs.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_logs_administration_head"}}

diff --git a/templates/panel_analytics_agent_views.html b/templates/panel_analytics_agent_views.html index 1751d531..ad644839 100644 --- a/templates/panel_analytics_agent_views.html +++ b/templates/panel_analytics_agent_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_agents.html b/templates/panel_analytics_agents.html index 22fad81a..9f206422 100644 --- a/templates/panel_analytics_agents.html +++ b/templates/panel_analytics_agents.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_forum_views.html b/templates/panel_analytics_forum_views.html index ec11212a..1f2ad5ce 100644 --- a/templates/panel_analytics_forum_views.html +++ b/templates/panel_analytics_forum_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_forums.html b/templates/panel_analytics_forums.html index dc0ddf25..f2eb606e 100644 --- a/templates/panel_analytics_forums.html +++ b/templates/panel_analytics_forums.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_lang_views.html b/templates/panel_analytics_lang_views.html index b2e26542..9073ce17 100644 --- a/templates/panel_analytics_lang_views.html +++ b/templates/panel_analytics_lang_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_langs.html b/templates/panel_analytics_langs.html index 6fe220ab..df3ec502 100644 --- a/templates/panel_analytics_langs.html +++ b/templates/panel_analytics_langs.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_posts.html b/templates/panel_analytics_posts.html index 03f1e3fd..27b84d35 100644 --- a/templates/panel_analytics_posts.html +++ b/templates/panel_analytics_posts.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_referrer_views.html b/templates/panel_analytics_referrer_views.html index 941b3820..edb41d08 100644 --- a/templates/panel_analytics_referrer_views.html +++ b/templates/panel_analytics_referrer_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_referrers.html b/templates/panel_analytics_referrers.html index bc10af6e..63b826fa 100644 --- a/templates/panel_analytics_referrers.html +++ b/templates/panel_analytics_referrers.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_route_views.html b/templates/panel_analytics_route_views.html index 549df9ee..9e284d09 100644 --- a/templates/panel_analytics_route_views.html +++ b/templates/panel_analytics_route_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_routes.html b/templates/panel_analytics_routes.html index 5dae4e5a..35d9f6cb 100644 --- a/templates/panel_analytics_routes.html +++ b/templates/panel_analytics_routes.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_script.html b/templates/panel_analytics_script.html index b63cbc2b..f7afc5fc 100644 --- a/templates/panel_analytics_script.html +++ b/templates/panel_analytics_script.html @@ -11,6 +11,10 @@ let legendNames = [{{range .Graph.Legends}} {{.}},{{end}} ]; addInitHook("after_phrases", () => { - buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames); + addInitHook("end_init", () => { + addInitHook("analytics_loaded", () => { + buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames); + }); + }); }); \ No newline at end of file diff --git a/templates/panel_analytics_system_views.html b/templates/panel_analytics_system_views.html index ecdcb33c..7847946c 100644 --- a/templates/panel_analytics_system_views.html +++ b/templates/panel_analytics_system_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_systems.html b/templates/panel_analytics_systems.html index 12c72856..03c2db43 100644 --- a/templates/panel_analytics_systems.html +++ b/templates/panel_analytics_systems.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_topics.html b/templates/panel_analytics_topics.html index 2d167598..48c057e9 100644 --- a/templates/panel_analytics_topics.html +++ b/templates/panel_analytics_topics.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_analytics_views.html b/templates/panel_analytics_views.html index 2937d357..e01f8f27 100644 --- a/templates/panel_analytics_views.html +++ b/templates/panel_analytics_views.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}
diff --git a/templates/panel_are_you_sure.html b/templates/panel_are_you_sure.html index 19da61e4..d7773ae8 100644 --- a/templates/panel_are_you_sure.html +++ b/templates/panel_are_you_sure.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "areyousure_head"}}

diff --git a/templates/panel_backups.html b/templates/panel_backups.html index 4e20132b..beeffe1b 100644 --- a/templates/panel_backups.html +++ b/templates/panel_backups.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_backups_head"}}

diff --git a/templates/panel_before_head.html b/templates/panel_before_head.html new file mode 100644 index 00000000..e69de29b diff --git a/templates/panel_dashboard.html b/templates/panel_dashboard.html index aa3cca01..c0568df5 100644 --- a/templates/panel_dashboard.html +++ b/templates/panel_dashboard.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_dashboard_head"}}

diff --git a/templates/panel_debug.html b/templates/panel_debug.html index 2d1a825d..137a8fa6 100644 --- a/templates/panel_debug.html +++ b/templates/panel_debug.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_debug_head"}}

diff --git a/templates/panel_forum_edit.html b/templates/panel_forum_edit.html index 16428fa8..b8671c5c 100644 --- a/templates/panel_forum_edit.html +++ b/templates/panel_forum_edit.html @@ -7,6 +7,7 @@ var formVars = {'perm_preset': ['can_moderate','can_post','read_only','no_access
+{{template "panel_before_head.html" . }}

{{.Name}}{{lang "panel_forum_head_suffix"}}

diff --git a/templates/panel_forum_edit_perms.html b/templates/panel_forum_edit_perms.html index 1f85e898..f4e479df 100644 --- a/templates/panel_forum_edit_perms.html +++ b/templates/panel_forum_edit_perms.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{.Name}}{{lang "panel_forum_head_suffix"}}

diff --git a/templates/panel_forums.html b/templates/panel_forums.html index 2e8e89c2..8580a61f 100644 --- a/templates/panel_forums.html +++ b/templates/panel_forums.html @@ -8,6 +8,7 @@
+{{template "panel_before_head.html" . }}

{{lang "panel_forums_head"}}

diff --git a/templates/panel_group_edit.html b/templates/panel_group_edit.html index 205f50b9..a234e191 100644 --- a/templates/panel_group_edit.html +++ b/templates/panel_group_edit.html @@ -2,6 +2,7 @@
{{template "panel_group_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{.Name}}{{lang "panel_group_head_suffix"}}

diff --git a/templates/panel_group_edit_perms.html b/templates/panel_group_edit_perms.html index c479d0e3..3f552f9e 100644 --- a/templates/panel_group_edit_perms.html +++ b/templates/panel_group_edit_perms.html @@ -2,6 +2,7 @@
{{template "panel_group_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{.Name}}{{lang "panel_group_head_suffix"}}

diff --git a/templates/panel_groups.html b/templates/panel_groups.html index 4983e4e5..012dd347 100644 --- a/templates/panel_groups.html +++ b/templates/panel_groups.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_groups_head"}}

diff --git a/templates/panel_modlogs.html b/templates/panel_modlogs.html index 39de8a90..41152c3c 100644 --- a/templates/panel_modlogs.html +++ b/templates/panel_modlogs.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_logs_moderation_head"}}

diff --git a/templates/panel_pages.html b/templates/panel_pages.html index 1f55b10e..ff3d2b8b 100644 --- a/templates/panel_pages.html +++ b/templates/panel_pages.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_pages_head"}}

diff --git a/templates/panel_pages_edit.html b/templates/panel_pages_edit.html index 27a08231..43e3b9f2 100644 --- a/templates/panel_pages_edit.html +++ b/templates/panel_pages_edit.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_pages_edit_head"}}

diff --git a/templates/panel_plugins.html b/templates/panel_plugins.html index ba682d25..e35717f1 100644 --- a/templates/panel_plugins.html +++ b/templates/panel_plugins.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_plugins_head"}}

diff --git a/templates/panel_reglogs.html b/templates/panel_reglogs.html index b53032ea..e2313739 100644 --- a/templates/panel_reglogs.html +++ b/templates/panel_reglogs.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_logs_registration_head"}}

diff --git a/templates/panel_setting.html b/templates/panel_setting.html index 29d1d53d..ba790936 100644 --- a/templates/panel_setting.html +++ b/templates/panel_setting.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{.Setting.FriendlyName}}

diff --git a/templates/panel_settings.html b/templates/panel_settings.html index 4f6acddc..1a52f67c 100644 --- a/templates/panel_settings.html +++ b/templates/panel_settings.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_settings_head"}}

diff --git a/templates/panel_themes.html b/templates/panel_themes.html index 42910d07..d8e6c51b 100644 --- a/templates/panel_themes.html +++ b/templates/panel_themes.html @@ -11,6 +11,7 @@
+{{template "panel_before_head.html" . }}

{{lang "panel_themes_primary_themes"}}

diff --git a/templates/panel_themes_menus.html b/templates/panel_themes_menus.html index 65ac2c0f..2ce4bf5a 100644 --- a/templates/panel_themes_menus.html +++ b/templates/panel_themes_menus.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_themes_menus_head"}}

diff --git a/templates/panel_themes_menus_item_edit.html b/templates/panel_themes_menus_item_edit.html index 877be147..aec7329a 100644 --- a/templates/panel_themes_menus_item_edit.html +++ b/templates/panel_themes_menus_item_edit.html @@ -4,6 +4,7 @@ {{/** TODO: Write the backend code and JS code for saving this menu **/}} {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_themes_menus_edit_head"}}

diff --git a/templates/panel_themes_menus_items.html b/templates/panel_themes_menus_items.html index a9844b4e..fc368118 100644 --- a/templates/panel_themes_menus_items.html +++ b/templates/panel_themes_menus_items.html @@ -2,6 +2,7 @@
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_themes_menus_items_head"}}

diff --git a/templates/panel_themes_widgets.html b/templates/panel_themes_widgets.html index d626f253..b437d396 100644 --- a/templates/panel_themes_widgets.html +++ b/templates/panel_themes_widgets.html @@ -13,6 +13,7 @@ type Widget struct {
{{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_themes_widgets_head"}}

diff --git a/templates/panel_user_edit.html b/templates/panel_user_edit.html index 77cc3bdf..206c6c6d 100644 --- a/templates/panel_user_edit.html +++ b/templates/panel_user_edit.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_user_head"}}

diff --git a/templates/panel_users.html b/templates/panel_users.html index 7445086b..d7529374 100644 --- a/templates/panel_users.html +++ b/templates/panel_users.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_users_head"}}

diff --git a/templates/panel_word_filters.html b/templates/panel_word_filters.html index 5fdf3b42..66d3d0a5 100644 --- a/templates/panel_word_filters.html +++ b/templates/panel_word_filters.html @@ -3,6 +3,7 @@ {{template "panel_menu.html" . }}
+{{template "panel_before_head.html" . }}

{{lang "panel_word_filters_head"}}

diff --git a/themes/cosora/public/misc.js b/themes/cosora/public/misc.js index 750ac848..2b98f134 100644 --- a/themes/cosora/public/misc.js +++ b/themes/cosora/public/misc.js @@ -1,116 +1,113 @@ -"use strict" +"use strict"; -$(document).ready(function(){ - let loggedIn = document.head.querySelector("[property='x-loggedin']").content; - if(loggedIn) { - // Is there we way we can append instead? Maybe, an editor plugin? - attachItemCallback = function(attachItem) { - let currentContent = $('#input_content').trumbowyg('html'); - $('#input_content').trumbowyg('html', currentContent); - } - - $(".topic_name_row").click(function(){ - $(".topic_create_form").addClass("selectedInput"); - }); - //$.trumbowyg.svgPath = false; +(() => { + console.log("bf") + addInitHook("end_init", () => { + console.log("af") + let loggedIn = document.head.querySelector("[property='x-loggedin']").content; + if(loggedIn) { + // Is there we way we can append instead? Maybe, an editor plugin? + attachItemCallback = function(attachItem) { + let currentContent = $('#input_content').trumbowyg('html'); + $('#input_content').trumbowyg('html', currentContent); + } + + $(".topic_name_row").click(function(){ + $(".topic_create_form").addClass("selectedInput"); + }); + //$.trumbowyg.svgPath = false; - // TODO: Bind this to the viewport resize event - var btnlist = []; - if(document.documentElement.clientWidth > 550) { - btnlist = [['viewHTML'],['undo','redo'],['formatting'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']]; - } else { - btnlist = [['viewHTML'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']]; - } - - $('.topic_create_form #input_content').trumbowyg({ - btns: btnlist, - }); - $('.topic_reply_form #input_content').trumbowyg({ - btns: btnlist, - autogrow: true, - }); - $('#profile_comments_form .topic_reply_form .input_content').trumbowyg({ - btns: [['viewHTML'],['strong','em','del'],['link'],['insertImage'],['removeformat']], - autogrow: true, - }); - addHook("edit_item_pre_bind", () => { - $('.user_content textarea').trumbowyg({ + // TODO: Bind this to the viewport resize event + var btnlist = []; + if(document.documentElement.clientWidth > 550) { + btnlist = [['viewHTML'],['undo','redo'],['formatting'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']]; + } else { + btnlist = [['viewHTML'],['strong','em','del'],['link'],['insertImage'],['unorderedList','orderedList'],['removeformat']]; + } + + $('.topic_create_form #input_content').trumbowyg({ + btns: btnlist, + }); + $('.topic_reply_form #input_content').trumbowyg({ btns: btnlist, autogrow: true, }); - }); - } - - // TODO: Refactor this to use `each` less - $('.button_menu').click(function(){ - console.log(".button_menu"); - // The outer container - let buttonPane = newElement("div","button_menu_pane"); - let postItem = $(this).parents('.post_item'); - - // Create the userinfo row in the pane - let userInfo = newElement("div","userinfo"); - postItem.find('.avatar_item').each(function(){ - userInfo.appendChild(this); - }); - - let userText = newElement("div","userText"); - postItem.find('.userinfo:not(.avatar_item)').children().each(function(){ - userText.appendChild(this); - }); - userInfo.appendChild(userText); - buttonPane.appendChild(userInfo); - - // Copy a short preview of the post contents into the pane - postItem.find('.user_content').each(function(){ - // TODO: Truncate an excessive number of lines to 5 or so - let contents = this.innerHTML; - if(contents.length > 45) { - this.innerHTML = contents.substring(0,45) + "..."; - } - buttonPane.appendChild(this); - }); - - // Copy the buttons from the post to the pane - let buttonGrid = newElement("div","buttonGrid"); - let gridElementCount = 0; - $(this).parent().children('a:not(.button_menu)').each(function(){ - buttonGrid.appendChild(this); - gridElementCount++; - }); - - - // Fill in the placeholder grid nodes - let rowCount = 4; - console.log("rowCount: ",rowCount); - console.log("gridElementCount: ",gridElementCount); - if(gridElementCount%rowCount != 0) { - let fillerNodes = (rowCount - (gridElementCount%rowCount)); - console.log("fillerNodes: ",fillerNodes); - for(let i = 0; i < fillerNodes;i++ ) { - console.log("added a gridFiller"); - buttonGrid.appendChild(newElement("div","gridFiller")); - } + $('#profile_comments_form .topic_reply_form .input_content').trumbowyg({ + btns: [['viewHTML'],['strong','em','del'],['link'],['insertImage'],['removeformat']], + autogrow: true, + }); + addHook("edit_item_pre_bind", () => { + $('.user_content textarea').trumbowyg({ + btns: btnlist, + autogrow: true, + }); + }); } - buttonPane.appendChild(buttonGrid); - document.getElementById("back").appendChild(buttonPane); + // TODO: Refactor this to use `each` less + $('.button_menu').click(function(){ + console.log(".button_menu"); + // The outer container + let buttonPane = newElement("div","button_menu_pane"); + let postItem = $(this).parents('.post_item'); + + // Create the userinfo row in the pane + let userInfo = newElement("div","userinfo"); + postItem.find('.avatar_item').each(function(){ + userInfo.appendChild(this); + }); + + let userText = newElement("div","userText"); + postItem.find('.userinfo:not(.avatar_item)').children().each(function(){ + userText.appendChild(this); + }); + userInfo.appendChild(userText); + buttonPane.appendChild(userInfo); + + // Copy a short preview of the post contents into the pane + postItem.find('.user_content').each(function(){ + // TODO: Truncate an excessive number of lines to 5 or so + let contents = this.innerHTML; + if(contents.length > 45) this.innerHTML = contents.substring(0,45) + "..."; + buttonPane.appendChild(this); + }); + + // Copy the buttons from the post to the pane + let buttonGrid = newElement("div","buttonGrid"); + let gridElementCount = 0; + $(this).parent().children('a:not(.button_menu)').each(function(){ + buttonGrid.appendChild(this); + gridElementCount++; + }); + + + // Fill in the placeholder grid nodes + let rowCount = 4; + console.log("rowCount: ",rowCount); + console.log("gridElementCount: ",gridElementCount); + if(gridElementCount%rowCount != 0) { + let fillerNodes = (rowCount - (gridElementCount%rowCount)); + console.log("fillerNodes: ",fillerNodes); + for(let i = 0; i < fillerNodes;i++ ) { + console.log("added a gridFiller"); + buttonGrid.appendChild(newElement("div","gridFiller")); + } + } + buttonPane.appendChild(buttonGrid); + + document.getElementById("back").appendChild(buttonPane); + }); + + // Move the alerts under the first header + let colSel = $(".colstack_right .colstack_head:first"); + let colSelAlt = $(".colstack_right .colstack_item:first"); + let colSelAltAlt = $(".colstack_right .coldyn_block:first"); + if(colSel.length > 0) $('.alert').insertAfter(colSel); + else if (colSelAlt.length > 0) $('.alert').insertBefore(colSelAlt); + else if (colSelAltAlt.length > 0) $('.alert').insertBefore(colSelAltAlt); + else $('.alert').insertAfter(".rowhead:first"); }); - - // Move the alerts under the first header - let colSel = $(".colstack_right .colstack_head:first"); - let colSelAlt = $(".colstack_right .colstack_item:first"); - let colSelAltAlt = $(".colstack_right .coldyn_block:first"); - if(colSel.length > 0) { - $('.alert').insertAfter(colSel); - } else if (colSelAlt.length > 0) { - $('.alert').insertBefore(colSelAlt); - } else if (colSelAltAlt.length > 0) { - $('.alert').insertBefore(colSelAltAlt); - } else { - $('.alert').insertAfter(".rowhead:first"); - } -}); +})(); function newElement(etype, eclass) { let element = document.createElement(etype); diff --git a/themes/cosora/theme.json b/themes/cosora/theme.json index 5a26f874..adb7af87 100644 --- a/themes/cosora/theme.json +++ b/themes/cosora/theme.json @@ -29,7 +29,8 @@ }, { "Name":"cosora/misc.js", - "Location":"global" + "Location":"global", + "Async":true } ] } diff --git a/themes/nox/overrides/panel_before_head.html b/themes/nox/overrides/panel_before_head.html new file mode 100644 index 00000000..cb7af928 --- /dev/null +++ b/themes/nox/overrides/panel_before_head.html @@ -0,0 +1,6 @@ +
+
{{lang "panel_back_to_site"}}
+
+ + {{lang "panel_welcome"}}{{.CurrentUser.Name}}
+
\ No newline at end of file diff --git a/themes/nox/overrides/panel_group_menu.html b/themes/nox/overrides/panel_group_menu.html index 3fae9bb4..491bad4e 100644 --- a/themes/nox/overrides/panel_group_menu.html +++ b/themes/nox/overrides/panel_group_menu.html @@ -1,7 +1,7 @@