Experimenting with Nox's Control Panel.
Experimenting with better cache busting for static resources. HTTPSRedirect requests are now counted in the route analytics. More scripts are loaded asynchronously now. Upped the default ReadTimeout to eight seconds. Reduce the number of unneccesary NewAcc calls. Added panel_before_head as an injection point for themes. Themes can now declare scripts to be loaded asynchronously. Tweaked the WS resumption algorithm to mae the backoffs a little less aggressive. Fixed an ordering issue in the WS resumption algorithm where backoffs weren't expiring as fast as they should have. Fixed a bug where template logs weren't being written due to a panic. You can now use byte slices in more places in the transpiled templates. Fixed a bug where Cosora's misc.js seemed to be erroring out. Fixed a bug where YT embeds were getting blocked by the CSP. Added the panel_back_to_site phrase. Added the panel_welcome phrase.
This commit is contained in:
parent
3320cb4697
commit
660f24acff
|
@ -96,12 +96,11 @@ var langCodes = []string{
|
||||||
type DefaultLangViewCounter struct {
|
type DefaultLangViewCounter struct {
|
||||||
buckets []*RWMutexCounterBucket //[OSID]count
|
buckets []*RWMutexCounterBucket //[OSID]count
|
||||||
codesToIndices map[string]int
|
codesToIndices map[string]int
|
||||||
|
|
||||||
insert *sql.Stmt
|
insert *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultLangViewCounter() (*DefaultLangViewCounter, error) {
|
func NewDefaultLangViewCounter(acc *qgen.Accumulator) (*DefaultLangViewCounter, error) {
|
||||||
acc := qgen.NewAcc()
|
|
||||||
|
|
||||||
var langBuckets = make([]*RWMutexCounterBucket, len(langCodes))
|
var langBuckets = make([]*RWMutexCounterBucket, len(langCodes))
|
||||||
for bucketID, _ := range langBuckets {
|
for bucketID, _ := range langBuckets {
|
||||||
langBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
langBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||||
|
|
|
@ -12,8 +12,7 @@ type DefaultRouteViewCounter struct {
|
||||||
insert *sql.Stmt
|
insert *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultRouteViewCounter() (*DefaultRouteViewCounter, error) {
|
func NewDefaultRouteViewCounter(acc *qgen.Accumulator) (*DefaultRouteViewCounter, error) {
|
||||||
acc := qgen.NewAcc()
|
|
||||||
var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum))
|
var routeBuckets = make([]*RWMutexCounterBucket, len(routeMapEnum))
|
||||||
for bucketID, _ := range routeBuckets {
|
for bucketID, _ := range routeBuckets {
|
||||||
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
routeBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||||
|
|
|
@ -14,8 +14,7 @@ type DefaultOSViewCounter struct {
|
||||||
insert *sql.Stmt
|
insert *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultOSViewCounter() (*DefaultOSViewCounter, error) {
|
func NewDefaultOSViewCounter(acc *qgen.Accumulator) (*DefaultOSViewCounter, error) {
|
||||||
acc := qgen.NewAcc()
|
|
||||||
var osBuckets = make([]*RWMutexCounterBucket, len(osMapEnum))
|
var osBuckets = make([]*RWMutexCounterBucket, len(osMapEnum))
|
||||||
for bucketID, _ := range osBuckets {
|
for bucketID, _ := range osBuckets {
|
||||||
osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
osBuckets[bucketID] = &RWMutexCounterBucket{counter: 0}
|
||||||
|
|
|
@ -27,7 +27,7 @@ var staticFileMutex sync.RWMutex
|
||||||
type SFile struct {
|
type SFile struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
GzipData []byte
|
GzipData []byte
|
||||||
Sha256 []byte
|
Sha256 string
|
||||||
Pos int64
|
Pos int64
|
||||||
Length int64
|
Length int64
|
||||||
GzipLength int64
|
GzipLength int64
|
||||||
|
@ -240,7 +240,7 @@ func (list SFileList) JSTmplInit() error {
|
||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
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)})
|
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
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := []byte(hex.EncodeToString(hasher.Sum(nil)))
|
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
|
||||||
// Avoid double-compressing images
|
// Avoid double-compressing images
|
||||||
var gzipData []byte
|
var gzipData []byte
|
||||||
|
@ -318,7 +318,7 @@ func (list SFileList) Add(path string, prefix string) error {
|
||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
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)})
|
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)})
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,20 @@ import (
|
||||||
"github.com/Azareal/Gosora/common/phrases"
|
"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: Allow resources in spots other than /static/ and possibly even external domains (e.g. CDNs)
|
||||||
// TODO: Preload Trumboyg on Cosora on the forum list
|
// TODO: Preload Trumboyg on Cosora on the forum list
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Title string
|
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
|
//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
|
NoticeList []string
|
||||||
Scripts []string
|
Scripts []HResource
|
||||||
PreScriptsAsync []string
|
PreScriptsAsync []HResource
|
||||||
|
ScriptsAsync []HResource
|
||||||
//Preload []string
|
//Preload []string
|
||||||
Stylesheets []string
|
Stylesheets []string
|
||||||
Widgets PageWidgets
|
Widgets PageWidgets
|
||||||
|
@ -44,11 +50,41 @@ type Header struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (header *Header) AddScript(name string) {
|
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) {
|
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) {
|
/*func (header *Header) Preload(name string) {
|
||||||
|
|
|
@ -131,11 +131,15 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
||||||
if ext == "css" {
|
if ext == "css" {
|
||||||
header.AddSheet(resource.Name)
|
header.AddSheet(resource.Name)
|
||||||
} else if ext == "js" {
|
} else if ext == "js" {
|
||||||
|
if resource.Async {
|
||||||
|
header.AddScriptAsync(resource.Name)
|
||||||
|
} else {
|
||||||
header.AddScript(resource.Name)
|
header.AddScript(resource.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//h := w.Header()
|
//h := w.Header()
|
||||||
//h.Set("Content-Security-Policy", "default-src 'self'")
|
//h.Set("Content-Security-Policy", "default-src 'self'")
|
||||||
|
@ -229,11 +233,15 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (header *Head
|
||||||
if ext == "css" {
|
if ext == "css" {
|
||||||
header.AddSheet(resource.Name)
|
header.AddSheet(resource.Name)
|
||||||
} else if ext == "js" {
|
} else if ext == "js" {
|
||||||
|
if resource.Async {
|
||||||
|
header.AddScriptAsync(resource.Name)
|
||||||
|
} else {
|
||||||
header.AddScript(resource.Name)
|
header.AddScript(resource.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var addPreScript = func(name string) {
|
var addPreScript = func(name string) {
|
||||||
var tname string
|
var tname string
|
||||||
|
|
|
@ -109,8 +109,9 @@ func tmplInitHeaders(user User, user2 User, user3 User) (*Header, *Header, *Head
|
||||||
CurrentUser: user,
|
CurrentUser: user,
|
||||||
NoticeList: []string{"test"},
|
NoticeList: []string{"test"},
|
||||||
Stylesheets: []string{"panel.css"},
|
Stylesheets: []string{"panel.css"},
|
||||||
Scripts: []string{"whatever.js"},
|
Scripts: []HResource{HResource{"whatever.js", "d"}},
|
||||||
PreScriptsAsync: []string{"whatever.js"},
|
PreScriptsAsync: []HResource{HResource{"whatever.js", "d"}},
|
||||||
|
ScriptsAsync: []HResource{HResource{"whatever.js", "d"}},
|
||||||
Widgets: PageWidgets{
|
Widgets: PageWidgets{
|
||||||
LeftSidebar: template.HTML("lalala"),
|
LeftSidebar: template.HTML("lalala"),
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,11 +2,13 @@ package tmpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
|
@ -71,6 +73,7 @@ type CTemplateSet struct {
|
||||||
perThemeTmpls map[string]bool
|
perThemeTmpls map[string]bool
|
||||||
|
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
loggerf *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCTemplateSet(in string) *CTemplateSet {
|
func NewCTemplateSet(in string) *CTemplateSet {
|
||||||
|
@ -111,6 +114,7 @@ func NewCTemplateSet(in string) *CTemplateSet {
|
||||||
"index": 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)
|
panic(err)
|
||||||
}
|
}
|
||||||
c.logger = log.New(f, "", log.LstdFlags)
|
c.logger = log.New(f, "", log.LstdFlags)
|
||||||
|
c.loggerf = f
|
||||||
}
|
}
|
||||||
|
|
||||||
type SkipBlock struct {
|
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) {
|
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.dumpCall("compile", name, content, expects, expectsInt, varList, imports)
|
||||||
//c.detailf("c: %+v\n", c)
|
//c.detailf("c: %+v\n", c)
|
||||||
c.importMap = map[string]string{}
|
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"))
|
c.addText(con, []byte("false"))
|
||||||
con.Push("endelse", "}\n")
|
con.Push("endelse", "}\n")
|
||||||
return
|
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:
|
case reflect.String:
|
||||||
if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") {
|
if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") {
|
||||||
varname = "string(" + varname + ")"
|
varname = "string(" + varname + ")"
|
||||||
|
|
|
@ -77,6 +77,7 @@ type ThemeResource struct {
|
||||||
Name string
|
Name string
|
||||||
Location string
|
Location string
|
||||||
Loggedin bool // Only serve this resource to logged in users
|
Loggedin bool // Only serve this resource to logged in users
|
||||||
|
Async bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ThemeMapTmplToDock struct {
|
type ThemeMapTmplToDock struct {
|
||||||
|
@ -162,7 +163,7 @@ func (theme *Theme) AddThemeStaticFiles() error {
|
||||||
// Get a checksum for CSPs and cache busting
|
// Get a checksum for CSPs and cache busting
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
hasher.Write(data)
|
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)})
|
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)})
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,12 @@ func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
||||||
currentPage = string(msgblocks[1])
|
currentPage = string(msgblocks[1])
|
||||||
wsPageResponses(wsUser, conn, currentPage)
|
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`)) {
|
/*if bytes.Equal(message,[]byte(`start-view`)) {
|
||||||
} else if bytes.Equal(message,[]byte(`end-view`)) {
|
} else if bytes.Equal(message,[]byte(`end-view`)) {
|
||||||
|
|
|
@ -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.
|
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.
|
WriteTimeout - The number of seconds that a route is allowed to run for before the request is automatically terminated. Defaults to 10.
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,7 @@ var RouteMap = map[string]interface{}{
|
||||||
"routes.RobotsTxt": routes.RobotsTxt,
|
"routes.RobotsTxt": routes.RobotsTxt,
|
||||||
"routes.SitemapXml": routes.SitemapXml,
|
"routes.SitemapXml": routes.SitemapXml,
|
||||||
"routes.BadRoute": routes.BadRoute,
|
"routes.BadRoute": routes.BadRoute,
|
||||||
|
"routes.HTTPSRedirect": routes.HTTPSRedirect,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS
|
// ! NEVER RELY ON THESE REMAINING THE SAME BETWEEN COMMITS
|
||||||
|
@ -309,6 +310,7 @@ var routeMapEnum = map[string]int{
|
||||||
"routes.RobotsTxt": 138,
|
"routes.RobotsTxt": 138,
|
||||||
"routes.SitemapXml": 139,
|
"routes.SitemapXml": 139,
|
||||||
"routes.BadRoute": 140,
|
"routes.BadRoute": 140,
|
||||||
|
"routes.HTTPSRedirect": 141,
|
||||||
}
|
}
|
||||||
var reverseRouteMapEnum = map[int]string{
|
var reverseRouteMapEnum = map[int]string{
|
||||||
0: "routes.Overview",
|
0: "routes.Overview",
|
||||||
|
@ -452,6 +454,7 @@ var reverseRouteMapEnum = map[int]string{
|
||||||
138: "routes.RobotsTxt",
|
138: "routes.RobotsTxt",
|
||||||
139: "routes.SitemapXml",
|
139: "routes.SitemapXml",
|
||||||
140: "routes.BadRoute",
|
140: "routes.BadRoute",
|
||||||
|
141: "routes.HTTPSRedirect",
|
||||||
}
|
}
|
||||||
var osMapEnum = map[string]int{
|
var osMapEnum = map[string]int{
|
||||||
"unknown": 0,
|
"unknown": 0,
|
||||||
|
@ -600,6 +603,17 @@ func (writ *WriterIntercept) WriteHeader(code int) {
|
||||||
writ.ResponseWriter.WriteHeader(code)
|
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 {
|
type GenRouter struct {
|
||||||
UploadHandler func(http.ResponseWriter, *http.Request)
|
UploadHandler func(http.ResponseWriter, *http.Request)
|
||||||
extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError
|
extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError
|
||||||
|
|
|
@ -708,6 +708,8 @@
|
||||||
"option_yes":"Yes",
|
"option_yes":"Yes",
|
||||||
"option_no":"No",
|
"option_no":"No",
|
||||||
|
|
||||||
|
"panel_back_to_site":"Back to Site",
|
||||||
|
"panel_welcome":"Welcome ",
|
||||||
"panel_menu_head":"Control Panel",
|
"panel_menu_head":"Control Panel",
|
||||||
"panel_menu_aria":"The control panel menu",
|
"panel_menu_aria":"The control panel menu",
|
||||||
"panel_menu_users":"Users",
|
"panel_menu_users":"Users",
|
||||||
|
|
11
main.go
11
main.go
|
@ -28,7 +28,6 @@ import (
|
||||||
"github.com/Azareal/Gosora/common/counters"
|
"github.com/Azareal/Gosora/common/counters"
|
||||||
"github.com/Azareal/Gosora/common/phrases"
|
"github.com/Azareal/Gosora/common/phrases"
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
"github.com/Azareal/Gosora/query_gen"
|
||||||
"github.com/Azareal/Gosora/routes"
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -181,15 +180,15 @@ func afterDBInit() (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
counters.OSViewCounter, err = counters.NewDefaultOSViewCounter()
|
counters.OSViewCounter, err = counters.NewDefaultOSViewCounter(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
counters.LangViewCounter, err = counters.NewDefaultLangViewCounter()
|
counters.LangViewCounter, err = counters.NewDefaultLangViewCounter(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
counters.RouteViewCounter, err = counters.NewDefaultRouteViewCounter()
|
counters.RouteViewCounter, err = counters.NewDefaultRouteViewCounter(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -472,7 +471,7 @@ func startServer() {
|
||||||
var newServer = func(addr string, handler http.Handler) *http.Server {
|
var newServer = func(addr string, handler http.Handler) *http.Server {
|
||||||
rtime := common.Config.ReadTimeout
|
rtime := common.Config.ReadTimeout
|
||||||
if rtime == 0 {
|
if rtime == 0 {
|
||||||
rtime = 5
|
rtime = 8
|
||||||
} else if rtime == -1 {
|
} else if rtime == -1 {
|
||||||
rtime = 0
|
rtime = 0
|
||||||
}
|
}
|
||||||
|
@ -527,7 +526,7 @@ func startServer() {
|
||||||
// TODO: Redirect to port 443
|
// TODO: Redirect to port 443
|
||||||
go func() {
|
go func() {
|
||||||
log.Print("Listening on port 80")
|
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)
|
log.Printf("Listening on port %s", common.Site.Port)
|
||||||
|
|
|
@ -60,3 +60,5 @@ function buildStatsChart(rawLabels, seriesData, timeRange, legendNames) {
|
||||||
series: seriesData,
|
series: seriesData,
|
||||||
}, config);
|
}, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runInitHook("analytics_loaded");
|
|
@ -193,7 +193,7 @@ function wsAlertEvent(data) {
|
||||||
updateAlertList(generalAlerts/*, alist*/);
|
updateAlertList(generalAlerts/*, alist*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runWebSockets() {
|
function runWebSockets(resume = false) {
|
||||||
if(window.location.protocol == "https:") {
|
if(window.location.protocol == "https:") {
|
||||||
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
||||||
} else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
} else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
||||||
|
@ -206,6 +206,7 @@ function runWebSockets() {
|
||||||
conn.onopen = () => {
|
conn.onopen = () => {
|
||||||
console.log("The WebSockets connection was opened");
|
console.log("The WebSockets connection was opened");
|
||||||
conn.send("page " + document.location.pathname + '\r');
|
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
|
// 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();
|
if(me.User.ID > 0) Notification.requestPermission();
|
||||||
}
|
}
|
||||||
|
@ -213,23 +214,22 @@ function runWebSockets() {
|
||||||
conn.onclose = () => {
|
conn.onclose = () => {
|
||||||
conn = false;
|
conn = false;
|
||||||
console.log("The WebSockets connection was closed");
|
console.log("The WebSockets connection was closed");
|
||||||
let backoff = 1000;
|
let backoff = 0.8;
|
||||||
if(wsBackoff < 0) wsBackoff = 0;
|
if(wsBackoff < 0) wsBackoff = 0;
|
||||||
else if(wsBackoff > 12) backoff = 13000;
|
else if(wsBackoff > 12) backoff = 11;
|
||||||
else if(wsBackoff > 5) backoff = 7000;
|
else if(wsBackoff > 5) backoff = 5;
|
||||||
wsBackoff++;
|
wsBackoff++;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
||||||
for(var i = 0; i < alertMenuList.length; i++) {
|
for(var i = 0; i < alertMenuList.length; i++) loadAlerts(alertMenuList[i]);
|
||||||
loadAlerts(alertMenuList[i]);
|
runWebSockets(true);
|
||||||
}
|
}, backoff * 60 * 1000);
|
||||||
runWebSockets();
|
|
||||||
}, 60 * backoff);
|
|
||||||
|
|
||||||
if(wsBackoff > 0) {
|
if(wsBackoff > 0) {
|
||||||
if(wsBackoff <= 5) setTimeout(() => wsBackoff--, 60 * 4000);
|
if(wsBackoff <= 5) setTimeout(() => wsBackoff--, 5.5 * 60 * 1000);
|
||||||
else if(wsBackoff <= 12) setTimeout(() => wsBackoff--, 60 * 20000);
|
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) => {
|
notifyOnScriptW("template_alert", (e) => {
|
||||||
if(e!=undefined) console.log("failed alert? why?", e)
|
if(e!=undefined) console.log("failed alert? why?", e)
|
||||||
}, () => {
|
}, () => {
|
||||||
console.log("ha")
|
//console.log("ha")
|
||||||
if(!Template_alert) throw("template function not found");
|
if(!Template_alert) throw("template function not found");
|
||||||
addInitHook("after_phrases", () => {
|
addInitHook("after_phrases", () => {
|
||||||
// TODO: The load part of loadAlerts could be done asynchronously while the update of the DOM could be deferred
|
// TODO: The load part of loadAlerts could be done asynchronously while the update of the DOM could be deferred
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
alertsInitted = true;
|
alertsInitted = true;
|
||||||
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
var alertMenuList = document.getElementsByClassName("menu_alerts");
|
||||||
for(var i = 0; i < alertMenuList.length; i++) {
|
for(var i = 0; i < alertMenuList.length; i++) loadAlerts(alertMenuList[i]);
|
||||||
loadAlerts(alertMenuList[i]);
|
|
||||||
}
|
|
||||||
if(window["WebSocket"]) runWebSockets();
|
if(window["WebSocket"]) runWebSockets();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,7 +4,7 @@ var me = {};
|
||||||
var phraseBox = {};
|
var phraseBox = {};
|
||||||
if(tmplInits===undefined) var tmplInits = {};
|
if(tmplInits===undefined) var tmplInits = {};
|
||||||
var tmplPhrases = []; // [key] array of phrases indexed by order of use
|
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_iffe": [],
|
||||||
"pre_init": [],
|
"pre_init": [],
|
||||||
"start_init": [],
|
"start_init": [],
|
||||||
|
@ -15,6 +15,7 @@ var hooks = {
|
||||||
"open_edit":[],
|
"open_edit":[],
|
||||||
"close_edit":[],
|
"close_edit":[],
|
||||||
"edit_item_pre_bind":[],
|
"edit_item_pre_bind":[],
|
||||||
|
"analytics_loaded":[],
|
||||||
};
|
};
|
||||||
var ranInitHooks = {}
|
var ranInitHooks = {}
|
||||||
|
|
||||||
|
|
|
@ -174,6 +174,7 @@ func main() {
|
||||||
mapIt("routes.RobotsTxt")
|
mapIt("routes.RobotsTxt")
|
||||||
mapIt("routes.SitemapXml")
|
mapIt("routes.SitemapXml")
|
||||||
mapIt("routes.BadRoute")
|
mapIt("routes.BadRoute")
|
||||||
|
mapIt("routes.HTTPSRedirect")
|
||||||
tmplVars.AllRouteNames = allRouteNames
|
tmplVars.AllRouteNames = allRouteNames
|
||||||
tmplVars.AllRouteMap = allRouteMap
|
tmplVars.AllRouteMap = allRouteMap
|
||||||
|
|
||||||
|
@ -381,6 +382,17 @@ func (writ *WriterIntercept) WriteHeader(code int) {
|
||||||
writ.ResponseWriter.WriteHeader(code)
|
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 {
|
type GenRouter struct {
|
||||||
UploadHandler func(http.ResponseWriter, *http.Request)
|
UploadHandler func(http.ResponseWriter, *http.Request)
|
||||||
extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError
|
extraRoutes map[string]func(http.ResponseWriter, *http.Request, common.User) common.RouteError
|
||||||
|
|
|
@ -29,8 +29,9 @@ func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, hea
|
||||||
}
|
}
|
||||||
// TODO: Expand this to non-HTTPS requests too
|
// TODO: Expand this to non-HTTPS requests too
|
||||||
if !header.LooseCSP && common.Site.EnableSsl {
|
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 {
|
if header.CurrentUser.IsAdmin {
|
||||||
header.Elapsed1 = time.Since(header.StartedAt).String()
|
header.Elapsed1 = time.Since(header.StartedAt).String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ func PreAnalyticsDetail(w http.ResponseWriter, r *http.Request, user *common.Use
|
||||||
}
|
}
|
||||||
basePage.AddSheet("chartist/chartist.min.css")
|
basePage.AddSheet("chartist/chartist.min.css")
|
||||||
basePage.AddScript("chartist/chartist.min.js")
|
basePage.AddScript("chartist/chartist.min.js")
|
||||||
basePage.AddScript("analytics.js")
|
basePage.AddScriptAsync("analytics.js")
|
||||||
return basePage, nil
|
return basePage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
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) {
|
if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &header.CurrentUser, pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,5 @@
|
||||||
package routes
|
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
|
// Temporary stubs for view tracking
|
||||||
func DynamicRoute() {
|
func DynamicRoute() {
|
||||||
}
|
}
|
||||||
|
@ -19,3 +7,7 @@ func UploadedFile() {
|
||||||
}
|
}
|
||||||
func BadRoute() {
|
func BadRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Real implementation is in router_gen/main.go, this is just a stub to map the analytics onto
|
||||||
|
func HTTPSRedirect() {
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@
|
||||||
{{range .Header.Stylesheets}}
|
{{range .Header.Stylesheets}}
|
||||||
<link href="/static/{{.}}" rel="stylesheet" type="text/css">{{end}}
|
<link href="/static/{{.}}" rel="stylesheet" type="text/css">{{end}}
|
||||||
{{range .Header.PreScriptsAsync}}
|
{{range .Header.PreScriptsAsync}}
|
||||||
<script async type="text/javascript" src="/static/{{.}}"></script>{{end}}
|
<script async type="text/javascript" src="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}"></script>{{end}}
|
||||||
<meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" />
|
<meta property="x-loggedin" content="{{.CurrentUser.Loggedin}}" />
|
||||||
<script type="text/javascript" src="/static/init.js"></script>
|
<script type="text/javascript" src="/static/init.js"></script>
|
||||||
|
{{range .Header.ScriptsAsync}}
|
||||||
|
<script async type="text/javascript" src="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}"></script>{{end}}
|
||||||
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
|
<script type="text/javascript" src="/static/jquery-3.1.1.min.js"></script>
|
||||||
{{range .Header.Scripts}}
|
{{range .Header.Scripts}}
|
||||||
<script type="text/javascript" src="/static/{{.}}"></script>{{end}}
|
<script type="text/javascript" src="/static/{{.Name}}{{if .Hash}}?h={{.Hash}}{{end}}"></script>{{end}}
|
||||||
<script type="text/javascript" src="/static/global.js"></script>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" />
|
<meta name="viewport" content="width=device-width,initial-scale = 1.0, maximum-scale=1.0,user-scalable=no" />
|
||||||
{{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}}
|
{{if .Header.MetaDesc}}<meta name="description" content="{{.Header.MetaDesc}}" />{{end}}
|
||||||
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}
|
{{/** TODO: Have page / forum / topic level tags and descriptions below as-well **/}}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_logs_administration_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_logs_administration_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agent/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agent/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agents/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/agents/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forum/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forum/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forums/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/forums/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/lang/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/lang/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/langs/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/langs/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/posts/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/posts/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrer/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrer/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrers/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/referrers/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/route/{{.Route}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/route/{{.Route}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/routes/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/routes/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -11,6 +11,10 @@ let legendNames = [{{range .Graph.Legends}}
|
||||||
{{.}},{{end}}
|
{{.}},{{end}}
|
||||||
];
|
];
|
||||||
addInitHook("after_phrases", () => {
|
addInitHook("after_phrases", () => {
|
||||||
|
addInitHook("end_init", () => {
|
||||||
|
addInitHook("analytics_loaded", () => {
|
||||||
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames);
|
buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/system/{{.Agent}}" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/system/{{.Agent}}" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/systems/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/systems/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/topics/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/topics/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_analytics_right" class="colstack_right">
|
<main id="panel_analytics_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/views/" method="get">
|
<form id="timeRangeForm" name="timeRangeForm" action="/panel/analytics/views/" method="get">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem">
|
<div class="rowitem">
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "areyousure_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "areyousure_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_backups_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_backups_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_dashboard_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_dashboard_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_debug_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_debug_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,6 +7,7 @@ var formVars = {'perm_preset': ['can_moderate','can_post','read_only','no_access
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_forum_head_suffix"}}</h1></div>
|
<div class="rowitem"><h1>{{.Name}}{{lang "panel_forum_head_suffix"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
|
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_forum_head_suffix"}}</h1></div>
|
<div class="rowitem"><h1>{{.Name}}{{lang "panel_forum_head_suffix"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_forums_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_forums_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_group_menu.html" . }}
|
{{template "panel_group_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}}</h1></div>
|
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_group_menu.html" . }}
|
{{template "panel_group_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}}</h1></div>
|
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_groups_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_groups_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_logs_moderation_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_logs_moderation_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div id="panel_page_list" class="colstack panel_stack">
|
<div id="panel_page_list" class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_pages_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_pages_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div id="panel_page_edit" class="colstack panel_stack">
|
<div id="panel_page_edit" class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_pages_edit_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_pages_edit_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_plugins_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_plugins_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_logs_registration_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_logs_registration_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{.Setting.FriendlyName}}</h1></div>
|
<div class="rowitem"><h1>{{.Setting.FriendlyName}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_settings_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_settings_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_primary_themes"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_primary_themes"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_menus_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
{{/** TODO: Write the backend code and JS code for saving this menu **/}}
|
{{/** TODO: Write the backend code and JS code for saving this menu **/}}
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_edit_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_menus_edit_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_menus_items_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_menus_items_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,7 @@ type Widget struct {
|
||||||
<div class="colstack panel_stack">
|
<div class="colstack panel_stack">
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_themes_widgets_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_themes_widgets_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_user_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_user_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_users_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_users_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
{{template "panel_menu.html" . }}
|
{{template "panel_menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
|
{{template "panel_before_head.html" . }}
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>{{lang "panel_word_filters_head"}}</h1></div>
|
<div class="rowitem"><h1>{{lang "panel_word_filters_head"}}</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
"use strict"
|
"use strict";
|
||||||
|
|
||||||
$(document).ready(function(){
|
(() => {
|
||||||
|
console.log("bf")
|
||||||
|
addInitHook("end_init", () => {
|
||||||
|
console.log("af")
|
||||||
let loggedIn = document.head.querySelector("[property='x-loggedin']").content;
|
let loggedIn = document.head.querySelector("[property='x-loggedin']").content;
|
||||||
if(loggedIn) {
|
if(loggedIn) {
|
||||||
// Is there we way we can append instead? Maybe, an editor plugin?
|
// Is there we way we can append instead? Maybe, an editor plugin?
|
||||||
|
@ -65,9 +68,7 @@ $(document).ready(function(){
|
||||||
postItem.find('.user_content').each(function(){
|
postItem.find('.user_content').each(function(){
|
||||||
// TODO: Truncate an excessive number of lines to 5 or so
|
// TODO: Truncate an excessive number of lines to 5 or so
|
||||||
let contents = this.innerHTML;
|
let contents = this.innerHTML;
|
||||||
if(contents.length > 45) {
|
if(contents.length > 45) this.innerHTML = contents.substring(0,45) + "...";
|
||||||
this.innerHTML = contents.substring(0,45) + "...";
|
|
||||||
}
|
|
||||||
buttonPane.appendChild(this);
|
buttonPane.appendChild(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,16 +102,12 @@ $(document).ready(function(){
|
||||||
let colSel = $(".colstack_right .colstack_head:first");
|
let colSel = $(".colstack_right .colstack_head:first");
|
||||||
let colSelAlt = $(".colstack_right .colstack_item:first");
|
let colSelAlt = $(".colstack_right .colstack_item:first");
|
||||||
let colSelAltAlt = $(".colstack_right .coldyn_block:first");
|
let colSelAltAlt = $(".colstack_right .coldyn_block:first");
|
||||||
if(colSel.length > 0) {
|
if(colSel.length > 0) $('.alert').insertAfter(colSel);
|
||||||
$('.alert').insertAfter(colSel);
|
else if (colSelAlt.length > 0) $('.alert').insertBefore(colSelAlt);
|
||||||
} else if (colSelAlt.length > 0) {
|
else if (colSelAltAlt.length > 0) $('.alert').insertBefore(colSelAltAlt);
|
||||||
$('.alert').insertBefore(colSelAlt);
|
else $('.alert').insertAfter(".rowhead:first");
|
||||||
} else if (colSelAltAlt.length > 0) {
|
});
|
||||||
$('.alert').insertBefore(colSelAltAlt);
|
})();
|
||||||
} else {
|
|
||||||
$('.alert').insertAfter(".rowhead:first");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function newElement(etype, eclass) {
|
function newElement(etype, eclass) {
|
||||||
let element = document.createElement(etype);
|
let element = document.createElement(etype);
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name":"cosora/misc.js",
|
"Name":"cosora/misc.js",
|
||||||
"Location":"global"
|
"Location":"global",
|
||||||
|
"Async":true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="above_right">
|
||||||
|
<div class="left_bit">{{lang "panel_back_to_site"}}</div>
|
||||||
|
<div class="right_bit">
|
||||||
|
<img src="{{.CurrentUser.MicroAvatar}}" height=32 width=32 />
|
||||||
|
<span>{{lang "panel_welcome"}}{{.CurrentUser.Name}}</span></div>
|
||||||
|
</div>
|
|
@ -1,7 +1,7 @@
|
||||||
<nav class="colstack_left" aria-label="{{lang "panel_menu_aria"}}">
|
<nav class="colstack_left" aria-label="{{lang "panel_menu_aria"}}">
|
||||||
<div class="colstack_item colstack_head">
|
<!--<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem back_to_site"><a href="/">Back to site</a></div>
|
<div class="rowitem back_to_site"><a href="/">Back to site</a></div>
|
||||||
</div>
|
</div>-->
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">{{lang "panel_group_menu_head"}}</a></div>
|
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">{{lang "panel_group_menu_head"}}</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<nav class="colstack_left" aria-label="{{lang "panel_menu_aria"}}">
|
<nav class="colstack_left" aria-label="{{lang "panel_menu_aria"}}">
|
||||||
<div class="colstack_item colstack_head">
|
<!--<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem back_to_site"><a href="/">Back to site</a></div>
|
<div class="rowitem back_to_site"><a href="/">Back to site</a></div>
|
||||||
</div>
|
</div>-->
|
||||||
{{template "panel_inner_menu.html" . }}</nav>
|
{{template "panel_inner_menu.html" . }}</nav>
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
});
|
});
|
||||||
addHook("open_edit", () => $('.topic_block').addClass("edithead"));
|
addHook("open_edit", () => $('.topic_block').addClass("edithead"));
|
||||||
addHook("close_edit", () => $('.topic_block').removeClass("edithead"));
|
addHook("close_edit", () => $('.topic_block').removeClass("edithead"));
|
||||||
})();
|
|
||||||
|
|
||||||
$(document).ready(() => {
|
addInitHook("end_init", () => {
|
||||||
$(".alerts").click((event) => {
|
$(".alerts").click((event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
var alerts = $(".menu_alerts")[0];
|
var alerts = $(".menu_alerts")[0];
|
||||||
|
@ -31,13 +30,9 @@ $(document).ready(() => {
|
||||||
let colSel = $(".colstack_right .colstack_head:first");
|
let colSel = $(".colstack_right .colstack_head:first");
|
||||||
let colSelAlt = $(".colstack_right .colstack_item:first");
|
let colSelAlt = $(".colstack_right .colstack_item:first");
|
||||||
let colSelAltAlt = $(".colstack_right .coldyn_block:first");
|
let colSelAltAlt = $(".colstack_right .coldyn_block:first");
|
||||||
if(colSel.length > 0) {
|
if(colSel.length > 0) $('.alert').insertBefore(colSel);
|
||||||
$('.alert').insertBefore(colSel);
|
else if (colSelAlt.length > 0) $('.alert').insertBefore(colSelAlt);
|
||||||
} else if (colSelAlt.length > 0) {
|
else if (colSelAltAlt.length > 0) $('.alert').insertBefore(colSelAltAlt);
|
||||||
$('.alert').insertBefore(colSelAlt);
|
else $('.alert').insertAfter(".rowhead:first");
|
||||||
} else if (colSelAltAlt.length > 0) {
|
});
|
||||||
$('.alert').insertBefore(colSelAltAlt);
|
})();
|
||||||
} else {
|
|
||||||
$('.alert').insertAfter(".rowhead:first");
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -24,8 +24,37 @@
|
||||||
.menu_stats {
|
.menu_stats {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
.back_to_site {
|
/*.back_to_site {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
}*/
|
||||||
|
.above_right {
|
||||||
|
background-color: rgb(62, 62, 62);
|
||||||
|
margin-top: -12px;
|
||||||
|
margin-left: -24px;
|
||||||
|
margin-right: -24px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.above_right .left_bit {
|
||||||
|
padding-left: 20px;
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.above_right .right_bit {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
background-color: rgb(72, 72, 72);
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.above_right img {
|
||||||
|
border-radius: 24px;
|
||||||
|
}
|
||||||
|
.above_right span {
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-top: 5px;
|
||||||
|
color: rgb(180, 180, 180);
|
||||||
}
|
}
|
||||||
|
|
||||||
.colstack_right {
|
.colstack_right {
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name":"nox/misc.js",
|
"Name":"nox/misc.js",
|
||||||
"Location":"global"
|
"Location":"global",
|
||||||
|
"Async":true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue