Client templates can now be overriden like every other template.
The client templates load earlier now for a smoother user experience. Added a setting for setting a Google Site Verification meta tag without editing header.html Added support for favicons. /static/favicon.ico will be mapped to favicon.ico, if it exists. The parent forum is now visible on the topics list for Nox. Language headers which contain the wildcard character are no longer considered unknowns. Meta descriptions and open graph descriptions are no longer emitted for logged in users. Slimmed down topics_topic slightly for Nox. Pre-parsed widgets are now minified. Stale WebSockets connections should be cleaned up far quicker now. Template generation is now logged separately. Commented out some obsolete template logic. Marked a few template generator fields as unexported. Fixed the styling for the ban page in the profile for Nox. Fixed the styling for colline for Cosora and Tempra Simple. Fixed the sidebar overflowing outside of the box on Nox. Fixed the meta description text overflowing the box in the Setting Manager on Nox. Fixed excessive padding in the Page Manager. Fixed a few missing border on the profiles for Tempra Simple. Fixed the sidebar appearing in places it shouldn't on Tempra Simple. Fixed the status code emitted by NotFoundJS Fixed a bug where Gosora kept falling back to interpreted templates. Fixed a bug where WebSockets connections weren't getting closed properly if the user cache overflowed. Fixed a bug where WebSocket connections weren't getting initialised for guests. Fixed a bug where template overrides weren't always getting applied. Fixed a bug where root template overrides weren't always getting applied. Added the google_site_verify setting. Added the google_site_verify phrase. You will need to run the patcher or updater for this commit.
This commit is contained in:
parent
fe33112827
commit
cc1d0f089a
|
@ -125,6 +125,7 @@ func seedTables(adapter qgen.Adapter) error {
|
||||||
qgen.Install.SimpleInsert("settings", "name, content, type", "'megapost_min_words','1000','int'")
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'megapost_min_words','1000','int'")
|
||||||
qgen.Install.SimpleInsert("settings", "name, content, type", "'meta_desc','','html-attribute'")
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'meta_desc','','html-attribute'")
|
||||||
qgen.Install.SimpleInsert("settings", "name, content, type", "'rapid_loading','1','bool'")
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'rapid_loading','1','bool'")
|
||||||
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'google_site_verify','','html-attribute'")
|
||||||
qgen.Install.SimpleInsert("themes", "uname, default", "'cosora',1")
|
qgen.Install.SimpleInsert("themes", "uname, default", "'cosora',1")
|
||||||
qgen.Install.SimpleInsert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation?
|
qgen.Install.SimpleInsert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation?
|
||||||
|
|
||||||
|
|
|
@ -311,6 +311,12 @@ func SecurityError(w http.ResponseWriter, r *http.Request, user User) RouteError
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MicroNotFound(w http.ResponseWriter, r *http.Request) RouteError {
|
||||||
|
w.WriteHeader(404)
|
||||||
|
_, _ = w.Write([]byte("file not found"))
|
||||||
|
return HandledRouteError()
|
||||||
|
}
|
||||||
|
|
||||||
// NotFound is used when the requested page doesn't exist
|
// NotFound is used when the requested page doesn't exist
|
||||||
// ? - Add a JSQ version of this?
|
// ? - Add a JSQ version of this?
|
||||||
// ? - Add a user parameter?
|
// ? - Add a user parameter?
|
||||||
|
@ -320,7 +326,7 @@ func NotFound(w http.ResponseWriter, r *http.Request, header *Header) RouteError
|
||||||
|
|
||||||
// ? - Add a user parameter?
|
// ? - Add a user parameter?
|
||||||
func NotFoundJS(w http.ResponseWriter, r *http.Request) RouteError {
|
func NotFoundJS(w http.ResponseWriter, r *http.Request) RouteError {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(404)
|
||||||
writeJsonError(phrases.GetErrorPhrase("not_found_body"), w)
|
writeJsonError(phrases.GetErrorPhrase("not_found_body"), w)
|
||||||
return HandledRouteError()
|
return HandledRouteError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,17 +39,6 @@ type CSSData struct {
|
||||||
|
|
||||||
func (list SFileList) JSTmplInit() error {
|
func (list SFileList) JSTmplInit() error {
|
||||||
DebugLog("Initialising the client side templates")
|
DebugLog("Initialising the client side templates")
|
||||||
var fragMap = make(map[string][][]byte)
|
|
||||||
var parseFrags = func(name string) {
|
|
||||||
fragMap[name] = tmpl.GetFrag(name)
|
|
||||||
}
|
|
||||||
parseFrags("alert")
|
|
||||||
parseFrags("forum")
|
|
||||||
parseFrags("topics_topic")
|
|
||||||
parseFrags("topic_posts")
|
|
||||||
parseFrags("topic_alt_posts")
|
|
||||||
parseFrags("paginator")
|
|
||||||
DebugLog("fragMap: ", fragMap)
|
|
||||||
return filepath.Walk("./tmpl_client", func(path string, f os.FileInfo, err error) error {
|
return filepath.Walk("./tmpl_client", func(path string, f os.FileInfo, err error) error {
|
||||||
if f.IsDir() || strings.HasSuffix(path, "template_list.go") || strings.HasSuffix(path, "stub.go") {
|
if f.IsDir() || strings.HasSuffix(path, "template_list.go") || strings.HasSuffix(path, "stub.go") {
|
||||||
return nil
|
return nil
|
||||||
|
@ -75,7 +64,7 @@ func (list SFileList) JSTmplInit() error {
|
||||||
}
|
}
|
||||||
data = data[startIndex-len([]byte("func init() {")):]
|
data = data[startIndex-len([]byte("func init() {")):]
|
||||||
data = replace(data, "func ", "function ")
|
data = replace(data, "func ", "function ")
|
||||||
data = replace(data, "function init() {", "tmplInits[\""+tmplName+"\"] = ")
|
data = replace(data, "function init() {", "if(tmplInits===undefined) var tmplInits = {};\ntmplInits[\""+tmplName+"\"] = ")
|
||||||
data = replace(data, " error {\n", " {\nlet out = \"\"\n")
|
data = replace(data, " error {\n", " {\nlet out = \"\"\n")
|
||||||
funcIndex, hasFunc := skipAllUntilCharsExist(data, 0, []byte("function Template_"))
|
funcIndex, hasFunc := skipAllUntilCharsExist(data, 0, []byte("function Template_"))
|
||||||
if !hasFunc {
|
if !hasFunc {
|
||||||
|
@ -220,19 +209,23 @@ func (list SFileList) JSTmplInit() error {
|
||||||
data = replace(data, `=
|
data = replace(data, `=
|
||||||
}`, "= []")
|
}`, "= []")
|
||||||
|
|
||||||
fragset, ok := fragMap[shortName]
|
fragset := tmpl.GetFrag(shortName)
|
||||||
if !ok {
|
if fragset != nil {
|
||||||
DebugLog("tmplName: ", tmplName)
|
var sfrags = []byte("let " + shortName + "_frags = [];\n")
|
||||||
return errors.New("couldn't find template in fragmap")
|
for _, frags := range fragset {
|
||||||
|
sfrags = append(sfrags, []byte(shortName+"_frags.push(`"+string(frags)+"`);\n")...)
|
||||||
|
}
|
||||||
|
data = append(sfrags, data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sfrags = []byte("let " + shortName + "_frags = [];\n")
|
|
||||||
for _, frags := range fragset {
|
|
||||||
sfrags = append(sfrags, []byte(shortName+"_frags.push(`"+string(frags)+"`);\n")...)
|
|
||||||
}
|
|
||||||
data = append(sfrags, data...)
|
|
||||||
data = replace(data, "\n;", "\n")
|
data = replace(data, "\n;", "\n")
|
||||||
|
|
||||||
|
for name, _ := range Themes {
|
||||||
|
if strings.HasSuffix(shortName, "_"+name) {
|
||||||
|
data = append(data, "\nlet Template_"+strings.TrimSuffix(shortName, "_"+name)+" = Template_"+shortName+";"...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
path = tmplName + ".js"
|
path = tmplName + ".js"
|
||||||
DebugLog("js path: ", path)
|
DebugLog("js path: ", path)
|
||||||
var ext = filepath.Ext("/tmpl_client/" + path)
|
var ext = filepath.Ext("/tmpl_client/" + path)
|
||||||
|
|
|
@ -15,8 +15,9 @@ import (
|
||||||
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 []string
|
||||||
|
PreScriptsAsync []string
|
||||||
//Preload []string
|
//Preload []string
|
||||||
Stylesheets []string
|
Stylesheets []string
|
||||||
Widgets PageWidgets
|
Widgets PageWidgets
|
||||||
|
@ -33,18 +34,23 @@ type Header struct {
|
||||||
Path string
|
Path string
|
||||||
MetaDesc string
|
MetaDesc string
|
||||||
//OGImage string
|
//OGImage string
|
||||||
OGDesc string
|
OGDesc string
|
||||||
LooseCSP bool
|
GoogSiteVerify string
|
||||||
StartedAt time.Time
|
LooseCSP bool
|
||||||
Elapsed1 string
|
StartedAt time.Time
|
||||||
Writer http.ResponseWriter
|
Elapsed1 string
|
||||||
ExtData ExtData
|
Writer http.ResponseWriter
|
||||||
|
ExtData ExtData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (header *Header) AddScript(name string) {
|
func (header *Header) AddScript(name string) {
|
||||||
header.Scripts = append(header.Scripts, name)
|
header.Scripts = append(header.Scripts, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (header *Header) AddPreScriptAsync(name string) {
|
||||||
|
header.PreScriptsAsync = append(header.PreScriptsAsync, name)
|
||||||
|
}
|
||||||
|
|
||||||
/*func (header *Header) Preload(name string) {
|
/*func (header *Header) Preload(name string) {
|
||||||
header.Preload = append(header.Preload, name)
|
header.Preload = append(header.Preload, name)
|
||||||
}*/
|
}*/
|
||||||
|
|
|
@ -150,6 +150,18 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
||||||
stats.Themes = len(Themes)
|
stats.Themes = len(Themes)
|
||||||
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
||||||
|
|
||||||
|
var addPreScript = func(name string) {
|
||||||
|
var tname string
|
||||||
|
if theme.OverridenMap != nil {
|
||||||
|
_, ok := theme.OverridenMap[name+".html"]
|
||||||
|
if ok {
|
||||||
|
tname = "_" + theme.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header.AddPreScriptAsync("template_" + name + tname + ".js")
|
||||||
|
}
|
||||||
|
addPreScript("alert")
|
||||||
|
|
||||||
return header, stats, nil
|
return header, stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,14 +194,15 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (header *Head
|
||||||
}
|
}
|
||||||
|
|
||||||
header = &Header{
|
header = &Header{
|
||||||
Site: Site,
|
Site: Site,
|
||||||
Settings: SettingBox.Load().(SettingMap),
|
Settings: SettingBox.Load().(SettingMap),
|
||||||
Themes: Themes,
|
Themes: Themes,
|
||||||
Theme: theme,
|
Theme: theme,
|
||||||
CurrentUser: *user, // ! Some things rely on this being a pointer downstream from this function
|
CurrentUser: *user, // ! Some things rely on this being a pointer downstream from this function
|
||||||
Hooks: GetHookTable(),
|
Hooks: GetHookTable(),
|
||||||
Zone: "frontend",
|
Zone: "frontend",
|
||||||
Writer: w,
|
Writer: w,
|
||||||
|
GoogSiteVerify: header.Settings["google_site_verify"].(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsBanned {
|
if user.IsBanned {
|
||||||
|
@ -222,6 +235,22 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (header *Head
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var addPreScript = func(name string) {
|
||||||
|
var tname string
|
||||||
|
if theme.OverridenMap != nil {
|
||||||
|
//fmt.Printf("name %+v\n", name)
|
||||||
|
//fmt.Printf("theme.OverridenMap %+v\n", theme.OverridenMap)
|
||||||
|
_, ok := theme.OverridenMap[name+".html"]
|
||||||
|
if ok {
|
||||||
|
tname = "_" + theme.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
header.AddPreScriptAsync("template_" + name + tname + ".js")
|
||||||
|
}
|
||||||
|
addPreScript("topics_topic")
|
||||||
|
addPreScript("paginator")
|
||||||
|
addPreScript("alert")
|
||||||
|
|
||||||
return header, nil
|
return header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,14 +102,15 @@ func tmplInitUsers() (User, User, User) {
|
||||||
|
|
||||||
func tmplInitHeaders(user User, user2 User, user3 User) (*Header, *Header, *Header) {
|
func tmplInitHeaders(user User, user2 User, user3 User) (*Header, *Header, *Header) {
|
||||||
header := &Header{
|
header := &Header{
|
||||||
Site: Site,
|
Site: Site,
|
||||||
Settings: SettingBox.Load().(SettingMap),
|
Settings: SettingBox.Load().(SettingMap),
|
||||||
Themes: Themes,
|
Themes: Themes,
|
||||||
Theme: Themes[DefaultThemeBox.Load().(string)],
|
Theme: Themes[DefaultThemeBox.Load().(string)],
|
||||||
CurrentUser: user,
|
CurrentUser: user,
|
||||||
NoticeList: []string{"test"},
|
NoticeList: []string{"test"},
|
||||||
Stylesheets: []string{"panel"},
|
Stylesheets: []string{"panel.css"},
|
||||||
Scripts: []string{"whatever"},
|
Scripts: []string{"whatever.js"},
|
||||||
|
PreScriptsAsync: []string{"whatever.js"},
|
||||||
Widgets: PageWidgets{
|
Widgets: PageWidgets{
|
||||||
LeftSidebar: template.HTML("lalala"),
|
LeftSidebar: template.HTML("lalala"),
|
||||||
},
|
},
|
||||||
|
@ -168,7 +169,7 @@ func CompileTemplates() error {
|
||||||
config.Debug = Dev.DebugMode
|
config.Debug = Dev.DebugMode
|
||||||
config.SuperDebug = Dev.TemplateDebug
|
config.SuperDebug = Dev.TemplateDebug
|
||||||
|
|
||||||
c := tmpl.NewCTemplateSet()
|
c := tmpl.NewCTemplateSet("normal")
|
||||||
c.SetConfig(config)
|
c.SetConfig(config)
|
||||||
c.SetBaseImportMap(map[string]string{
|
c.SetBaseImportMap(map[string]string{
|
||||||
"io": "io",
|
"io": "io",
|
||||||
|
@ -189,6 +190,7 @@ func CompileTemplates() error {
|
||||||
|
|
||||||
log.Print("Compiling the per-theme templates")
|
log.Print("Compiling the per-theme templates")
|
||||||
for theme, tmpls := range oroots {
|
for theme, tmpls := range oroots {
|
||||||
|
c.ResetLogs("normal-" + theme)
|
||||||
c.SetThemeName(theme)
|
c.SetThemeName(theme)
|
||||||
c.SetPerThemeTmpls(tmpls)
|
c.SetPerThemeTmpls(tmpls)
|
||||||
log.Print("theme: ", theme)
|
log.Print("theme: ", theme)
|
||||||
|
@ -366,7 +368,156 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ? - Add template hooks?
|
||||||
func CompileJSTemplates() error {
|
func CompileJSTemplates() error {
|
||||||
|
log.Print("Compiling the JS templates")
|
||||||
|
// TODO: Implement per-theme template overrides here too
|
||||||
|
var overriden = make(map[string]map[string]bool)
|
||||||
|
for _, theme := range Themes {
|
||||||
|
overriden[theme.Name] = make(map[string]bool)
|
||||||
|
log.Printf("theme.OverridenTemplates: %+v\n", theme.OverridenTemplates)
|
||||||
|
for _, override := range theme.OverridenTemplates {
|
||||||
|
overriden[theme.Name][override] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("overriden: %+v\n", overriden)
|
||||||
|
|
||||||
|
var config tmpl.CTemplateConfig
|
||||||
|
config.Minify = Config.MinifyTemplates
|
||||||
|
config.Debug = Dev.DebugMode
|
||||||
|
config.SuperDebug = Dev.TemplateDebug
|
||||||
|
config.SkipHandles = true
|
||||||
|
config.SkipTmplPtrMap = true
|
||||||
|
config.SkipInitBlock = false
|
||||||
|
config.PackageName = "tmpl"
|
||||||
|
|
||||||
|
c := tmpl.NewCTemplateSet("js")
|
||||||
|
c.SetConfig(config)
|
||||||
|
c.SetBuildTags("!no_templategen")
|
||||||
|
c.SetOverrideTrack(overriden)
|
||||||
|
c.SetPerThemeTmpls(make(map[string]bool))
|
||||||
|
|
||||||
|
log.Print("Compiling the default templates")
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
err := compileJSTemplates(&wg, c, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oroots := c.GetOverridenRoots()
|
||||||
|
log.Printf("oroots: %+v\n", oroots)
|
||||||
|
|
||||||
|
log.Print("Compiling the per-theme templates")
|
||||||
|
for theme, tmpls := range oroots {
|
||||||
|
c.SetThemeName(theme)
|
||||||
|
c.SetPerThemeTmpls(tmpls)
|
||||||
|
log.Print("theme: ", theme)
|
||||||
|
log.Printf("perThemeTmpls: %+v\n", tmpls)
|
||||||
|
err = compileJSTemplates(&wg, c, theme)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dirPrefix = "./tmpl_client/"
|
||||||
|
writeTemplateList(c, &wg, dirPrefix)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string) error {
|
||||||
|
user, user2, user3 := tmplInitUsers()
|
||||||
|
header, _, _ := tmplInitHeaders(user, user2, user3)
|
||||||
|
now := time.Now()
|
||||||
|
var varList = make(map[string]tmpl.VarItem)
|
||||||
|
|
||||||
|
c.SetBaseImportMap(map[string]string{
|
||||||
|
"io": "io",
|
||||||
|
"github.com/Azareal/Gosora/common/alerts": "github.com/Azareal/Gosora/common/alerts",
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: Check what sort of path is sent exactly and use it here
|
||||||
|
alertItem := alerts.AlertItem{Avatar: "", ASID: 1, Path: "/", Message: "uh oh, something happened"}
|
||||||
|
alertTmpl, err := c.Compile("alert.html", "templates/", "alerts.AlertItem", alertItem, varList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetBaseImportMap(map[string]string{
|
||||||
|
"io": "io",
|
||||||
|
"github.com/Azareal/Gosora/common": "github.com/Azareal/Gosora/common",
|
||||||
|
})
|
||||||
|
// TODO: Fix the import loop so we don't have to use this hack anymore
|
||||||
|
c.SetBuildTags("!no_templategen,tmplgentopic")
|
||||||
|
|
||||||
|
tmpls := TItemHold(make(map[string]TItem))
|
||||||
|
|
||||||
|
var topicsRow = &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"}
|
||||||
|
tmpls.AddStd("topics_topic", "common.TopicsRow", topicsRow)
|
||||||
|
|
||||||
|
poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{
|
||||||
|
PollOption{0, "Nothing"},
|
||||||
|
PollOption{1, "Something"},
|
||||||
|
}, VoteCount: 7}
|
||||||
|
avatar, microAvatar := BuildAvatar(62, "")
|
||||||
|
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
|
||||||
|
topic := TopicUser{1, "blah", "Blah", "Hey there!", 62, false, false, now, now, 1, 1, 0, "", "127.0.0.1", 1, 0, 1, 0, "classname", poll.ID, "weird-data", BuildProfileURL("fake-user", 62), "Fake User", Config.DefaultGroup, avatar, microAvatar, 0, "", "", "", "", "", 58, false, miniAttach}
|
||||||
|
var replyList []ReplyUser
|
||||||
|
// TODO: Do we really want the UID here to be zero?
|
||||||
|
avatar, microAvatar = BuildAvatar(0, "")
|
||||||
|
replyList = append(replyList, ReplyUser{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", Config.DefaultGroup, now, 0, 0, avatar, microAvatar, "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, 1, "", "", miniAttach})
|
||||||
|
|
||||||
|
varList = make(map[string]tmpl.VarItem)
|
||||||
|
header.Title = "Topic Name"
|
||||||
|
tpage := TopicPage{header, replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}}
|
||||||
|
tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID)
|
||||||
|
tmpls.AddStd("topic_posts", "common.TopicPage", tpage)
|
||||||
|
tmpls.AddStd("topic_alt_posts", "common.TopicPage", tpage)
|
||||||
|
|
||||||
|
itemsPerPage := 25
|
||||||
|
_, page, lastPage := PageOffset(20, 1, itemsPerPage)
|
||||||
|
pageList := Paginate(20, itemsPerPage, 5)
|
||||||
|
tmpls.AddStd("paginator", "common.Paginator", Paginator{pageList, page, lastPage})
|
||||||
|
|
||||||
|
var dirPrefix = "./tmpl_client/"
|
||||||
|
var writeTemplate = func(name string, content string) {
|
||||||
|
log.Print("Writing template '" + name + "'")
|
||||||
|
if content == "" {
|
||||||
|
return //log.Fatal("No content body")
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
tname := themeName
|
||||||
|
if tname != "" {
|
||||||
|
tname = "_" + tname
|
||||||
|
}
|
||||||
|
err := writeFile(dirPrefix+"template_"+name+tname+".go", content)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Writing the templates")
|
||||||
|
for name, titem := range tmpls {
|
||||||
|
log.Print("Writing " + name)
|
||||||
|
varList := make(map[string]tmpl.VarItem)
|
||||||
|
tmpl, err := c.Compile(name+".html", "templates/", titem.Expects, titem.ExpectsInt, varList)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
writeTemplate(name, tmpl)
|
||||||
|
}
|
||||||
|
writeTemplate("alert", alertTmpl)
|
||||||
|
/*//writeTemplate("forum", forumTmpl)
|
||||||
|
writeTemplate("topics_topic", topicListItemTmpl)
|
||||||
|
writeTemplate("topic_posts", topicPostsTmpl)
|
||||||
|
writeTemplate("topic_alt_posts", topicAltPostsTmpl)
|
||||||
|
writeTemplate("paginator", paginatorTmpl)
|
||||||
|
//writeTemplate("panel_themes_widgets_widget", panelWidgetsWidgetTmpl)
|
||||||
|
writeTemplateList(c, &wg, dirPrefix)*/
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func CompileJSTemplates() error {
|
||||||
log.Print("Compiling the JS templates")
|
log.Print("Compiling the JS templates")
|
||||||
var config tmpl.CTemplateConfig
|
var config tmpl.CTemplateConfig
|
||||||
config.Minify = Config.MinifyTemplates
|
config.Minify = Config.MinifyTemplates
|
||||||
|
@ -443,12 +594,6 @@ func CompileJSTemplates() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*widget := &Widget{ID: 0}
|
|
||||||
panelWidgetsWidgetTmpl, err := c.Compile("panel_themes_widgets_widget.html", "templates/", "*common.Widget", widget, varList)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}*/
|
|
||||||
|
|
||||||
var dirPrefix = "./tmpl_client/"
|
var dirPrefix = "./tmpl_client/"
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var writeTemplate = func(name string, content string) {
|
var writeTemplate = func(name string, content string) {
|
||||||
|
@ -474,9 +619,10 @@ func CompileJSTemplates() error {
|
||||||
//writeTemplate("panel_themes_widgets_widget", panelWidgetsWidgetTmpl)
|
//writeTemplate("panel_themes_widgets_widget", panelWidgetsWidgetTmpl)
|
||||||
writeTemplateList(c, &wg, dirPrefix)
|
writeTemplateList(c, &wg, dirPrefix)
|
||||||
return nil
|
return nil
|
||||||
}
|
}*/
|
||||||
|
|
||||||
func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) string {
|
func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) string {
|
||||||
|
DebugLog("in getTemplateList")
|
||||||
pout := "\n// nolint\nfunc init() {\n"
|
pout := "\n// nolint\nfunc init() {\n"
|
||||||
var tFragCount = make(map[string]int)
|
var tFragCount = make(map[string]int)
|
||||||
var bodyMap = make(map[string]string) //map[body]fragmentPrefix
|
var bodyMap = make(map[string]string) //map[body]fragmentPrefix
|
||||||
|
@ -484,6 +630,8 @@ func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) st
|
||||||
var tmpCount = 0
|
var tmpCount = 0
|
||||||
for _, frag := range c.FragOut {
|
for _, frag := range c.FragOut {
|
||||||
front := frag.TmplName + "_frags[" + strconv.Itoa(frag.Index) + "]"
|
front := frag.TmplName + "_frags[" + strconv.Itoa(frag.Index) + "]"
|
||||||
|
DebugLog("front: ", front)
|
||||||
|
DebugLog("frag.Body: ", frag.Body)
|
||||||
/*bodyMap, tok := tmplMap[frag.TmplName]
|
/*bodyMap, tok := tmplMap[frag.TmplName]
|
||||||
if !tok {
|
if !tok {
|
||||||
tmplMap[frag.TmplName] = make(map[string]string)
|
tmplMap[frag.TmplName] = make(map[string]string)
|
||||||
|
@ -493,9 +641,12 @@ func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) st
|
||||||
if !ok {
|
if !ok {
|
||||||
bodyMap[frag.Body] = front
|
bodyMap[frag.Body] = front
|
||||||
var bits string
|
var bits string
|
||||||
|
DebugLog("encoding frag.Body")
|
||||||
for _, char := range []byte(frag.Body) {
|
for _, char := range []byte(frag.Body) {
|
||||||
if char == '\'' {
|
if char == '\'' {
|
||||||
bits += "'\\" + string(char) + "',"
|
bits += "'\\" + string(char) + "',"
|
||||||
|
} else if char < 32 {
|
||||||
|
bits += strconv.Itoa(int(char)) + ","
|
||||||
} else {
|
} else {
|
||||||
bits += "'" + string(char) + "',"
|
bits += "'" + string(char) + "',"
|
||||||
}
|
}
|
||||||
|
@ -506,6 +657,7 @@ func getTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string) st
|
||||||
tmpCount++
|
tmpCount++
|
||||||
//pout += front + " = []byte(`" + frag.Body + "`)\n"
|
//pout += front + " = []byte(`" + frag.Body + "`)\n"
|
||||||
} else {
|
} else {
|
||||||
|
DebugLog("encoding cached index " + fp)
|
||||||
pout += front + " = " + fp + "\n"
|
pout += front + " = " + fp + "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func minify(data string) string {
|
func Minify(data string) string {
|
||||||
data = strings.Replace(data, "\t", "", -1)
|
data = strings.Replace(data, "\t", "", -1)
|
||||||
data = strings.Replace(data, "\v", "", -1)
|
data = strings.Replace(data, "\v", "", -1)
|
||||||
data = strings.Replace(data, "\n", "", -1)
|
data = strings.Replace(data, "\n", "", -1)
|
||||||
|
@ -19,7 +19,7 @@ func minify(data string) string {
|
||||||
// TODO: Handle CSS nested in <style> tags?
|
// TODO: Handle CSS nested in <style> tags?
|
||||||
// TODO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func minifyHTML(data string) string {
|
func minifyHTML(data string) string {
|
||||||
return minify(data)
|
return Minify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Have static files use this
|
// TODO: Have static files use this
|
||||||
|
@ -27,7 +27,7 @@ func minifyHTML(data string) string {
|
||||||
// TODO: Convert the rgb()s to hex codes?
|
// TODO: Convert the rgb()s to hex codes?
|
||||||
// TODO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func minifyCSS(data string) string {
|
func minifyCSS(data string) string {
|
||||||
return minify(data)
|
return Minify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Convert this to three character hex strings whenever possible?
|
// TODO: Convert this to three character hex strings whenever possible?
|
||||||
|
|
|
@ -2,14 +2,15 @@ package tmpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Turn this file into a library
|
// TODO: Turn this file into a library
|
||||||
|
@ -42,23 +43,23 @@ type CTemplateConfig struct {
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
type CTemplateSet struct {
|
type CTemplateSet struct {
|
||||||
templateList map[string]*parse.Tree
|
templateList map[string]*parse.Tree
|
||||||
fileDir string
|
fileDir string
|
||||||
funcMap map[string]interface{}
|
funcMap map[string]interface{}
|
||||||
importMap map[string]string
|
importMap map[string]string
|
||||||
TemplateFragmentCount map[string]int
|
//templateFragmentCount map[string]int
|
||||||
FragOnce map[string]bool
|
fragOnce map[string]bool
|
||||||
fragmentCursor map[string]int
|
fragmentCursor map[string]int
|
||||||
FragOut []OutFrag
|
FragOut []OutFrag
|
||||||
fragBuf []Fragment
|
fragBuf []Fragment
|
||||||
varList map[string]VarItem
|
varList map[string]VarItem
|
||||||
localVars map[string]map[string]VarItemReflect
|
localVars map[string]map[string]VarItemReflect
|
||||||
hasDispInt bool
|
hasDispInt bool
|
||||||
localDispStructIndex int
|
localDispStructIndex int
|
||||||
langIndexToName []string
|
langIndexToName []string
|
||||||
guestOnly bool
|
guestOnly bool
|
||||||
memberOnly bool
|
memberOnly bool
|
||||||
stats map[string]int
|
stats map[string]int
|
||||||
//tempVars map[string]string
|
//tempVars map[string]string
|
||||||
config CTemplateConfig
|
config CTemplateConfig
|
||||||
baseImportMap map[string]string
|
baseImportMap map[string]string
|
||||||
|
@ -68,9 +69,15 @@ type CTemplateSet struct {
|
||||||
overridenRoots map[string]map[string]bool
|
overridenRoots map[string]map[string]bool
|
||||||
themeName string
|
themeName string
|
||||||
perThemeTmpls map[string]bool
|
perThemeTmpls map[string]bool
|
||||||
|
|
||||||
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCTemplateSet() *CTemplateSet {
|
func NewCTemplateSet(in string) *CTemplateSet {
|
||||||
|
f, err := os.OpenFile("./logs/tmpls-"+in+"-"+strconv.FormatInt(time.Now().Unix(), 10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return &CTemplateSet{
|
return &CTemplateSet{
|
||||||
config: CTemplateConfig{
|
config: CTemplateConfig{
|
||||||
PackageName: "main",
|
PackageName: "main",
|
||||||
|
@ -102,6 +109,7 @@ func NewCTemplateSet() *CTemplateSet {
|
||||||
"dyntmpl": true,
|
"dyntmpl": true,
|
||||||
"index": true,
|
"index": true,
|
||||||
},
|
},
|
||||||
|
logger: log.New(f, "", log.LstdFlags),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +148,14 @@ func (c *CTemplateSet) SetPerThemeTmpls(perThemeTmpls map[string]bool) {
|
||||||
c.perThemeTmpls = perThemeTmpls
|
c.perThemeTmpls = perThemeTmpls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CTemplateSet) ResetLogs(in string) {
|
||||||
|
f, err := os.OpenFile("./logs/tmpls-"+in+"-"+strconv.FormatInt(time.Now().Unix(), 10)+".log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
c.logger = log.New(f, "", log.LstdFlags)
|
||||||
|
}
|
||||||
|
|
||||||
type SkipBlock struct {
|
type SkipBlock struct {
|
||||||
Frags map[int]int
|
Frags map[int]int
|
||||||
LastCount int
|
LastCount int
|
||||||
|
@ -187,13 +203,15 @@ import "errors"
|
||||||
if !c.config.SkipInitBlock {
|
if !c.config.SkipInitBlock {
|
||||||
stub += "// nolint\nfunc init() {\n"
|
stub += "// nolint\nfunc init() {\n"
|
||||||
|
|
||||||
if !c.config.SkipHandles {
|
if !c.config.SkipHandles && c.themeName == "" {
|
||||||
stub += "\tcommon.Template_" + fname + "_handle = Template_" + fname + "\n"
|
stub += "\tcommon.Template_" + fname + "_handle = Template_" + fname + "\n"
|
||||||
stub += "\tcommon.Ctemplates = append(common.Ctemplates,\"" + fname + "\")\n\tcommon.TmplPtrMap[\"" + fname + "\"] = &common.Template_" + fname + "_handle\n"
|
stub += "\tcommon.Ctemplates = append(common.Ctemplates,\"" + fname + "\")\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.config.SkipTmplPtrMap {
|
if !c.config.SkipTmplPtrMap {
|
||||||
stub += "\tcommon.TmplPtrMap[\"o_" + fname + "\"] = Template_" + fname + "\n"
|
stub += "tmpl := Template_" + fname + "\n"
|
||||||
|
stub += "\tcommon.TmplPtrMap[\"" + fname + "\"] = &tmpl\n"
|
||||||
|
stub += "\tcommon.TmplPtrMap[\"o_" + fname + "\"] = tmpl\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
stub += "}\n\n"
|
stub += "}\n\n"
|
||||||
|
@ -216,6 +234,7 @@ func Template_` + fname + `(tmpl_` + fname + `_i interface{}, w io.Writer) error
|
||||||
c.fileDir = fileDir
|
c.fileDir = fileDir
|
||||||
content, err := c.loadTemplate(c.fileDir, name)
|
content, err := c.loadTemplate(c.fileDir, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.detail("bailing out: ", err)
|
||||||
return "", "", "", err
|
return "", "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,11 +254,12 @@ func Template_` + fname + `(tmpl_` + fname + `_i interface{}, w io.Writer) error
|
||||||
|
|
||||||
func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||||
if c.config.Debug {
|
if c.config.Debug {
|
||||||
fmt.Println("Compiling template '" + name + "'")
|
c.logger.Println("Compiling template '" + name + "'")
|
||||||
}
|
}
|
||||||
c.fileDir = fileDir
|
c.fileDir = fileDir
|
||||||
content, err := c.loadTemplate(c.fileDir, name)
|
content, err := c.loadTemplate(c.fileDir, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
c.detail("bailing out: ", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,6 +267,8 @@ 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) {
|
||||||
|
//c.dumpCall("compile", name, content, expects, expectsInt, varList, imports)
|
||||||
|
//c.detailf("c: %+v\n", c)
|
||||||
c.importMap = map[string]string{}
|
c.importMap = map[string]string{}
|
||||||
for index, item := range c.baseImportMap {
|
for index, item := range c.baseImportMap {
|
||||||
c.importMap[index] = item
|
c.importMap[index] = item
|
||||||
|
@ -273,6 +295,8 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
||||||
if c.themeName != "" {
|
if c.themeName != "" {
|
||||||
_, ok := c.perThemeTmpls[fname]
|
_, ok := c.perThemeTmpls[fname]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
c.detail("fname not in c.perThemeTmpls")
|
||||||
|
c.detail("c.perThemeTmpls", c.perThemeTmpls)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
fname += "_" + c.themeName
|
fname += "_" + c.themeName
|
||||||
|
@ -283,6 +307,35 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
||||||
fname += "_member"
|
fname += "_member"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.detail("root overridenTrack loop")
|
||||||
|
c.detail("fname:", fname)
|
||||||
|
for themeName, track := range c.overridenTrack {
|
||||||
|
c.detail("themeName:", themeName)
|
||||||
|
c.detailf("track: %+v\n", track)
|
||||||
|
croot, ok := c.overridenRoots[themeName]
|
||||||
|
if !ok {
|
||||||
|
croot = make(map[string]bool)
|
||||||
|
c.overridenRoots[themeName] = croot
|
||||||
|
}
|
||||||
|
c.detailf("croot: %+v\n", croot)
|
||||||
|
for tmplName, _ := range track {
|
||||||
|
cname := tmplName
|
||||||
|
if c.guestOnly {
|
||||||
|
cname += "_guest"
|
||||||
|
} else if c.memberOnly {
|
||||||
|
cname += "_member"
|
||||||
|
}
|
||||||
|
c.detail("cname:", cname)
|
||||||
|
if fname == cname {
|
||||||
|
c.detail("match")
|
||||||
|
croot[strings.TrimSuffix(strings.TrimSuffix(fname, "_guest"), "_member")] = true
|
||||||
|
} else {
|
||||||
|
c.detail("no match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.detailf("c.overridenRoots: %+v\n", c.overridenRoots)
|
||||||
|
|
||||||
var outBuf []OutBufferFrame
|
var outBuf []OutBufferFrame
|
||||||
var rootHold = "tmpl_" + fname + "_vars"
|
var rootHold = "tmpl_" + fname + "_vars"
|
||||||
con := CContext{
|
con := CContext{
|
||||||
|
@ -298,27 +351,28 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
||||||
c.localVars = make(map[string]map[string]VarItemReflect)
|
c.localVars = make(map[string]map[string]VarItemReflect)
|
||||||
c.localVars[fname] = make(map[string]VarItemReflect)
|
c.localVars[fname] = make(map[string]VarItemReflect)
|
||||||
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
|
c.localVars[fname]["."] = VarItemReflect{".", con.VarHolder, con.HoldReflect}
|
||||||
if c.FragOnce == nil {
|
if c.fragOnce == nil {
|
||||||
c.FragOnce = make(map[string]bool)
|
c.fragOnce = make(map[string]bool)
|
||||||
}
|
}
|
||||||
c.fragmentCursor = map[string]int{fname: 0}
|
c.fragmentCursor = map[string]int{fname: 0}
|
||||||
c.fragBuf = nil
|
c.fragBuf = nil
|
||||||
c.langIndexToName = nil
|
c.langIndexToName = nil
|
||||||
|
|
||||||
// TODO: Is this the first template loaded in? We really should have some sort of constructor for CTemplateSet
|
// TODO: Is this the first template loaded in? We really should have some sort of constructor for CTemplateSet
|
||||||
if c.TemplateFragmentCount == nil {
|
//if c.templateFragmentCount == nil {
|
||||||
c.TemplateFragmentCount = make(map[string]int)
|
// c.templateFragmentCount = make(map[string]int)
|
||||||
}
|
//}
|
||||||
|
//c.detailf("c: %+v\n", c)
|
||||||
|
|
||||||
startIndex := con.StartTemplate("")
|
startIndex := con.StartTemplate("")
|
||||||
c.rootIterate(c.templateList[fname], con)
|
c.rootIterate(c.templateList[fname], con)
|
||||||
con.EndTemplate("")
|
con.EndTemplate("")
|
||||||
c.afterTemplate(con, startIndex)
|
c.afterTemplate(con, startIndex)
|
||||||
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
//c.templateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
||||||
|
|
||||||
_, ok := c.FragOnce[fname]
|
_, ok := c.fragOnce[fname]
|
||||||
if !ok {
|
if !ok {
|
||||||
c.FragOnce[fname] = true
|
c.fragOnce[fname] = true
|
||||||
}
|
}
|
||||||
if len(c.langIndexToName) > 0 {
|
if len(c.langIndexToName) > 0 {
|
||||||
c.importMap[langPkg] = langPkg
|
c.importMap[langPkg] = langPkg
|
||||||
|
@ -346,13 +400,15 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
||||||
}
|
}
|
||||||
fout += "// nolint\nfunc init() {\n"
|
fout += "// nolint\nfunc init() {\n"
|
||||||
|
|
||||||
if !c.config.SkipHandles {
|
if !c.config.SkipHandles && c.themeName == "" {
|
||||||
fout += "\tcommon.Template_" + fname + "_handle = Template_" + fname + "\n"
|
fout += "\tcommon.Template_" + fname + "_handle = Template_" + fname + "\n"
|
||||||
fout += "\tcommon.Ctemplates = append(common.Ctemplates,\"" + fname + "\")\n\tcommon.TmplPtrMap[\"" + fname + "\"] = &common.Template_" + fname + "_handle\n"
|
fout += "\tcommon.Ctemplates = append(common.Ctemplates,\"" + fname + "\")\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.config.SkipTmplPtrMap {
|
if !c.config.SkipTmplPtrMap {
|
||||||
fout += "\tcommon.TmplPtrMap[\"o_" + fname + "\"] = Template_" + fname + "\n"
|
fout += "tmpl := Template_" + fname + "\n"
|
||||||
|
fout += "\tcommon.TmplPtrMap[\"" + fname + "\"] = &tmpl\n"
|
||||||
|
fout += "\tcommon.TmplPtrMap[\"o_" + fname + "\"] = tmpl\n"
|
||||||
}
|
}
|
||||||
if len(c.langIndexToName) > 0 {
|
if len(c.langIndexToName) > 0 {
|
||||||
fout += "\t" + fname + "_tmpl_phrase_id = phrases.RegisterTmplPhraseNames([]string{\n"
|
fout += "\t" + fname + "_tmpl_phrase_id = phrases.RegisterTmplPhraseNames([]string{\n"
|
||||||
|
@ -458,9 +514,9 @@ w.Write([]byte(`, " + ", -1)
|
||||||
|
|
||||||
if c.config.Debug {
|
if c.config.Debug {
|
||||||
for index, count := range c.stats {
|
for index, count := range c.stats {
|
||||||
fmt.Println(index+": ", strconv.Itoa(count))
|
c.logger.Println(index+": ", strconv.Itoa(count))
|
||||||
}
|
}
|
||||||
fmt.Println(" ")
|
c.logger.Println(" ")
|
||||||
}
|
}
|
||||||
c.detail("Output!")
|
c.detail("Output!")
|
||||||
c.detail(fout)
|
c.detail(fout)
|
||||||
|
@ -579,13 +635,15 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) addText(con CContext, text []byte) {
|
func (c *CTemplateSet) addText(con CContext, text []byte) {
|
||||||
|
c.dumpCall("addText", con, text)
|
||||||
tmpText := bytes.TrimSpace(text)
|
tmpText := bytes.TrimSpace(text)
|
||||||
if len(tmpText) == 0 {
|
if len(tmpText) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
nodeText := string(text)
|
nodeText := string(text)
|
||||||
|
c.detail("con.TemplateName: ", con.TemplateName)
|
||||||
fragIndex := c.fragmentCursor[con.TemplateName]
|
fragIndex := c.fragmentCursor[con.TemplateName]
|
||||||
_, ok := c.FragOnce[con.TemplateName]
|
_, ok := c.fragOnce[con.TemplateName]
|
||||||
c.fragBuf = append(c.fragBuf, Fragment{nodeText, con.TemplateName, fragIndex, ok})
|
c.fragBuf = append(c.fragBuf, Fragment{nodeText, con.TemplateName, fragIndex, ok})
|
||||||
con.PushText(strconv.Itoa(fragIndex), fragIndex, len(c.fragBuf)-1)
|
con.PushText(strconv.Itoa(fragIndex), fragIndex, len(c.fragBuf)-1)
|
||||||
c.fragmentCursor[con.TemplateName] = fragIndex + 1
|
c.fragmentCursor[con.TemplateName] = fragIndex + 1
|
||||||
|
@ -816,9 +874,9 @@ func (c *CTemplateSet) compileExprSwitch(con CContext, node *parse.CommandNode)
|
||||||
switch n := firstWord.(type) {
|
switch n := firstWord.(type) {
|
||||||
case *parse.FieldNode:
|
case *parse.FieldNode:
|
||||||
if c.config.SuperDebug {
|
if c.config.SuperDebug {
|
||||||
fmt.Println("Field Node:", n.Ident)
|
c.logger.Println("Field Node:", n.Ident)
|
||||||
for _, id := range n.Ident {
|
for _, id := range n.Ident {
|
||||||
fmt.Println("Field Bit:", id)
|
c.logger.Println("Field Bit:", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
|
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
|
||||||
|
@ -851,8 +909,8 @@ func (c *CTemplateSet) compileExprSwitch(con CContext, node *parse.CommandNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) unknownNode(node parse.Node) {
|
func (c *CTemplateSet) unknownNode(node parse.Node) {
|
||||||
fmt.Println("Unknown Kind:", reflect.ValueOf(node).Elem().Kind())
|
c.logger.Println("Unknown Kind:", reflect.ValueOf(node).Elem().Kind())
|
||||||
fmt.Println("Unknown Type:", reflect.ValueOf(node).Elem().Type().Name())
|
c.logger.Println("Unknown Type:", reflect.ValueOf(node).Elem().Type().Name())
|
||||||
panic("I don't know what node this is! Grr...")
|
panic("I don't know what node this is! Grr...")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,12 +951,12 @@ func (c *CTemplateSet) simpleMath(con CContext, pos int, node *parse.CommandNode
|
||||||
func (c *CTemplateSet) compareJoin(con CContext, pos int, node *parse.CommandNode, symbol string) (pos2 int, out string) {
|
func (c *CTemplateSet) compareJoin(con CContext, pos int, node *parse.CommandNode, symbol string) (pos2 int, out string) {
|
||||||
c.detailf("Building %s function", symbol)
|
c.detailf("Building %s function", symbol)
|
||||||
if pos == 0 {
|
if pos == 0 {
|
||||||
fmt.Println("pos:", pos)
|
c.logger.Println("pos:", pos)
|
||||||
panic(symbol + " is missing a left operand")
|
panic(symbol + " is missing a left operand")
|
||||||
}
|
}
|
||||||
if len(node.Args) <= pos {
|
if len(node.Args) <= pos {
|
||||||
fmt.Println("post pos:", pos)
|
c.logger.Println("post pos:", pos)
|
||||||
fmt.Println("len(node.Args):", len(node.Args))
|
c.logger.Println("len(node.Args):", len(node.Args))
|
||||||
panic(symbol + " is missing a right operand")
|
panic(symbol + " is missing a right operand")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1090,9 +1148,9 @@ func (c *CTemplateSet) compileReflectSwitch(con CContext, node *parse.CommandNod
|
||||||
switch n := firstWord.(type) {
|
switch n := firstWord.(type) {
|
||||||
case *parse.FieldNode:
|
case *parse.FieldNode:
|
||||||
if c.config.SuperDebug {
|
if c.config.SuperDebug {
|
||||||
fmt.Println("Field Node:", n.Ident)
|
c.logger.Println("Field Node:", n.Ident)
|
||||||
for _, id := range n.Ident {
|
for _, id := range n.Ident {
|
||||||
fmt.Println("Field Bit:", id)
|
c.logger.Println("Field Bit:", id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
|
/* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */
|
||||||
|
@ -1176,12 +1234,12 @@ func (c *CTemplateSet) compileIfVarSub(con CContext, varname string) (out string
|
||||||
cur = cur.FieldByName(bit)
|
cur = cur.FieldByName(bit)
|
||||||
out += "." + bit
|
out += "." + bit
|
||||||
if !cur.IsValid() {
|
if !cur.IsValid() {
|
||||||
fmt.Println("cur: ", cur)
|
c.logger.Println("cur: ", cur)
|
||||||
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
}
|
}
|
||||||
stepInterface()
|
stepInterface()
|
||||||
if !cur.IsValid() {
|
if !cur.IsValid() {
|
||||||
fmt.Println("cur: ", cur)
|
c.logger.Println("cur: ", cur)
|
||||||
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(out + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
}
|
}
|
||||||
dumpKind("Data")
|
dumpKind("Data")
|
||||||
|
@ -1219,9 +1277,9 @@ func (c *CTemplateSet) compileBoolSub(con CContext, varname string) string {
|
||||||
case reflect.Slice, reflect.Map:
|
case reflect.Slice, reflect.Map:
|
||||||
out = "len(" + out + ") != 0"
|
out = "len(" + out + ") != 0"
|
||||||
default:
|
default:
|
||||||
fmt.Println("Variable Name:", varname)
|
c.logger.Println("Variable Name:", varname)
|
||||||
fmt.Println("Variable Holder:", con.VarHolder)
|
c.logger.Println("Variable Holder:", con.VarHolder)
|
||||||
fmt.Println("Variable Kind:", con.HoldReflect.Kind())
|
c.logger.Println("Variable Kind:", con.HoldReflect.Kind())
|
||||||
panic("I don't know what this variable's type is o.o\n")
|
panic("I don't know what this variable's type is o.o\n")
|
||||||
}
|
}
|
||||||
c.retCall("compileBoolSub", out)
|
c.retCall("compileBoolSub", out)
|
||||||
|
@ -1398,17 +1456,17 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V
|
||||||
if !val.IsValid() {
|
if !val.IsValid() {
|
||||||
panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
}
|
}
|
||||||
fmt.Println("Unknown Struct Name:", varname)
|
c.logger.Println("Unknown Struct Name:", varname)
|
||||||
fmt.Println("Unknown Struct:", val.Type().Name())
|
c.logger.Println("Unknown Struct:", val.Type().Name())
|
||||||
panic("-- I don't know what this variable's type is o.o\n")
|
panic("-- I don't know what this variable's type is o.o\n")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if !val.IsValid() {
|
if !val.IsValid() {
|
||||||
panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
}
|
}
|
||||||
fmt.Println("Unknown Variable Name:", varname)
|
c.logger.Println("Unknown Variable Name:", varname)
|
||||||
fmt.Println("Unknown Kind:", val.Kind())
|
c.logger.Println("Unknown Kind:", val.Kind())
|
||||||
fmt.Println("Unknown Type:", val.Type().Name())
|
c.logger.Println("Unknown Type:", val.Type().Name())
|
||||||
panic("-- I don't know what this variable's type is o.o\n")
|
panic("-- I don't know what this variable's type is o.o\n")
|
||||||
}
|
}
|
||||||
c.detail("base: ", base)
|
c.detail("base: ", base)
|
||||||
|
@ -1427,17 +1485,25 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
||||||
// TODO: Cascade errors back up the tree to the caller?
|
// TODO: Cascade errors back up the tree to the caller?
|
||||||
content, err := c.loadTemplate(c.fileDir, node.Name)
|
content, err := c.loadTemplate(c.fileDir, node.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
c.logger.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tree := parse.New(node.Name, c.funcMap)
|
tree := parse.New(node.Name, c.funcMap)
|
||||||
var treeSet = make(map[string]*parse.Tree)
|
var treeSet = make(map[string]*parse.Tree)
|
||||||
tree, err = tree.Parse(content, "{{", "}}", treeSet, c.funcMap)
|
tree, err = tree.Parse(content, "{{", "}}", treeSet, c.funcMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
c.logger.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name))
|
fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name))
|
||||||
|
if c.themeName != "" {
|
||||||
|
_, ok := c.perThemeTmpls[fname]
|
||||||
|
if !ok {
|
||||||
|
c.detail("fname not in c.perThemeTmpls")
|
||||||
|
c.detail("c.perThemeTmpls", c.perThemeTmpls)
|
||||||
|
}
|
||||||
|
fname += "_" + c.themeName
|
||||||
|
}
|
||||||
if c.guestOnly {
|
if c.guestOnly {
|
||||||
fname += "_guest"
|
fname += "_guest"
|
||||||
} else if c.memberOnly {
|
} else if c.memberOnly {
|
||||||
|
@ -1523,11 +1589,11 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
||||||
con.StartTemplate(startBit)
|
con.StartTemplate(startBit)
|
||||||
c.rootIterate(subtree, con)
|
c.rootIterate(subtree, con)
|
||||||
con.EndTemplate(endBit)
|
con.EndTemplate(endBit)
|
||||||
c.TemplateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
//c.templateFragmentCount[fname] = c.fragmentCursor[fname] + 1
|
||||||
|
|
||||||
_, ok := c.FragOnce[fname]
|
_, ok := c.fragOnce[fname]
|
||||||
if !ok {
|
if !ok {
|
||||||
c.FragOnce[fname] = true
|
c.fragOnce[fname] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// map[string]map[string]bool
|
// map[string]map[string]bool
|
||||||
|
@ -1562,13 +1628,16 @@ func (c *CTemplateSet) compileSubTemplate(pcon CContext, node *parse.TemplateNod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) loadTemplate(fileDir string, name string) (content string, err error) {
|
func (c *CTemplateSet) loadTemplate(fileDir string, name string) (content string, err error) {
|
||||||
|
c.dumpCall("loadTemplate", fileDir, name)
|
||||||
|
|
||||||
|
c.detail("c.themeName: ", c.themeName)
|
||||||
if c.themeName != "" {
|
if c.themeName != "" {
|
||||||
c.detail("per-theme override: ", "./themes/"+c.themeName+"/overrides/"+name)
|
c.detail("per-theme override: ", "./themes/"+c.themeName+"/overrides/"+name)
|
||||||
res, err := ioutil.ReadFile("./themes/" + c.themeName + "/overrides/" + name)
|
res, err := ioutil.ReadFile("./themes/" + c.themeName + "/overrides/" + name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
content = string(res)
|
content = string(res)
|
||||||
if c.config.Minify {
|
if c.config.Minify {
|
||||||
content = minify(content)
|
content = Minify(content)
|
||||||
}
|
}
|
||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
@ -1586,7 +1655,7 @@ func (c *CTemplateSet) loadTemplate(fileDir string, name string) (content string
|
||||||
}
|
}
|
||||||
content = string(res)
|
content = string(res)
|
||||||
if c.config.Minify {
|
if c.config.Minify {
|
||||||
content = minify(content)
|
content = Minify(content)
|
||||||
}
|
}
|
||||||
return content, nil
|
return content, nil
|
||||||
}
|
}
|
||||||
|
@ -1664,22 +1733,22 @@ func (c *CTemplateSet) afterTemplate(con CContext, startIndex int) {
|
||||||
|
|
||||||
func (c *CTemplateSet) detail(args ...interface{}) {
|
func (c *CTemplateSet) detail(args ...interface{}) {
|
||||||
if c.config.SuperDebug {
|
if c.config.SuperDebug {
|
||||||
log.Println(args...)
|
c.logger.Println(args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) detailf(left string, args ...interface{}) {
|
func (c *CTemplateSet) detailf(left string, args ...interface{}) {
|
||||||
if c.config.SuperDebug {
|
if c.config.SuperDebug {
|
||||||
log.Printf(left, args...)
|
c.logger.Printf(left, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) error(args ...interface{}) {
|
func (c *CTemplateSet) error(args ...interface{}) {
|
||||||
if c.config.Debug {
|
if c.config.Debug {
|
||||||
log.Println(args...)
|
c.logger.Println(args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) critical(args ...interface{}) {
|
func (c *CTemplateSet) critical(args ...interface{}) {
|
||||||
log.Println(args...)
|
c.logger.Println(args...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,23 +25,25 @@ var ErrNoDefaultTheme = errors.New("The default theme isn't registered in the sy
|
||||||
type Theme struct {
|
type Theme struct {
|
||||||
Path string // Redirect this file to another folder
|
Path string // Redirect this file to another folder
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
FriendlyName string
|
FriendlyName string
|
||||||
Version string
|
Version string
|
||||||
Creator string
|
Creator string
|
||||||
FullImage string
|
FullImage string
|
||||||
MobileFriendly bool
|
MobileFriendly bool
|
||||||
Disabled bool
|
Disabled bool
|
||||||
HideFromThemes bool
|
HideFromThemes bool
|
||||||
BgAvatars bool // For profiles, at the moment
|
BgAvatars bool // For profiles, at the moment
|
||||||
GridLists bool // User Manager
|
GridLists bool // User Manager
|
||||||
ForkOf string
|
ForkOf string
|
||||||
Tag string
|
Tag string
|
||||||
URL string
|
URL string
|
||||||
Docks []string // Allowed Values: leftSidebar, rightSidebar, footer
|
Docks []string // Allowed Values: leftSidebar, rightSidebar, footer
|
||||||
Settings map[string]ThemeSetting
|
Settings map[string]ThemeSetting
|
||||||
IntTmplHandle *htmpl.Template
|
IntTmplHandle *htmpl.Template
|
||||||
|
// TODO: Do we really need both OverridenTemplates AND OverridenMap?
|
||||||
OverridenTemplates []string
|
OverridenTemplates []string
|
||||||
|
OverridenMap map[string]bool
|
||||||
Templates []TemplateMapping
|
Templates []TemplateMapping
|
||||||
TemplatesMap map[string]string
|
TemplatesMap map[string]string
|
||||||
TmplPtr map[string]interface{}
|
TmplPtr map[string]interface{}
|
||||||
|
@ -297,6 +299,7 @@ func (theme *Theme) RunTmpl(template string, pi interface{}, w io.Writer) error
|
||||||
case func(interface{}, io.Writer) error:
|
case func(interface{}, io.Writer) error:
|
||||||
return tmplO(pi, w)
|
return tmplO(pi, w)
|
||||||
case nil, string:
|
case nil, string:
|
||||||
|
//fmt.Println("falling back to interpreted for " + template)
|
||||||
mapping, ok := theme.TemplatesMap[template]
|
mapping, ok := theme.TemplatesMap[template]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = template
|
mapping = template
|
||||||
|
@ -330,6 +333,10 @@ func (theme *Theme) GetTmpl(template string) interface{} {
|
||||||
if ok {
|
if ok {
|
||||||
return tmpl
|
return tmpl
|
||||||
}
|
}
|
||||||
|
tmpl, ok = TmplPtrMap[template+"_"+theme.Name]
|
||||||
|
if ok {
|
||||||
|
return tmpl
|
||||||
|
}
|
||||||
tmpl, ok = TmplPtrMap[template]
|
tmpl, ok = TmplPtrMap[template]
|
||||||
if ok {
|
if ok {
|
||||||
return tmpl
|
return tmpl
|
||||||
|
|
|
@ -146,6 +146,7 @@ func NewThemeList() (themes ThemeList, err error) {
|
||||||
}
|
}
|
||||||
if len(overrides) > 0 {
|
if len(overrides) > 0 {
|
||||||
var overCount = 0
|
var overCount = 0
|
||||||
|
theme.OverridenMap = make(map[string]bool)
|
||||||
for _, override := range overrides {
|
for _, override := range overrides {
|
||||||
if override.IsDir() {
|
if override.IsDir() {
|
||||||
continue
|
continue
|
||||||
|
@ -157,7 +158,10 @@ func NewThemeList() (themes ThemeList, err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
overCount++
|
overCount++
|
||||||
theme.OverridenTemplates = append(theme.OverridenTemplates, strings.TrimSuffix(override.Name(), ext))
|
nosuf := strings.TrimSuffix(override.Name(), ext)
|
||||||
|
theme.OverridenTemplates = append(theme.OverridenTemplates, nosuf)
|
||||||
|
theme.OverridenMap[nosuf] = true
|
||||||
|
//theme.TmplPtr[nosuf] = TmplPtrMap["o_"+nosuf]
|
||||||
log.Print("succeeded")
|
log.Print("succeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +251,6 @@ func ResetTemplateOverrides() {
|
||||||
log.Print("The origin template doesn't exist!")
|
log.Print("The origin template doesn't exist!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
destTmplPtr, ok := TmplPtrMap[name]
|
destTmplPtr, ok := TmplPtrMap[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Print("The destination template doesn't exist!")
|
log.Print("The destination template doesn't exist!")
|
||||||
|
|
|
@ -49,8 +49,10 @@ func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
||||||
// TODO: Spit out a 500 instead of nil?
|
// TODO: Spit out a 500 instead of nil?
|
||||||
conn, err := wsUpgrader.Upgrade(w, r, nil)
|
conn, err := wsUpgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return LocalError("unable to upgrade", w, r, user)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
wsUser, err := WsHub.AddConn(user, conn)
|
wsUser, err := WsHub.AddConn(user, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -96,7 +98,6 @@ func RouteWebsockets(w http.ResponseWriter, r *http.Request, user User) RouteErr
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn.Close()
|
|
||||||
DebugLog("Closing connection for user " + strconv.Itoa(user.ID))
|
DebugLog("Closing connection for user " + strconv.Itoa(user.ID))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
min "github.com/Azareal/Gosora/common/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Clean this file up
|
// TODO: Clean this file up
|
||||||
|
@ -49,7 +51,11 @@ func preparseWidget(widget *Widget, wdata string) (err error) {
|
||||||
prebuildWidget := func(name string, data interface{}) (string, error) {
|
prebuildWidget := func(name string, data interface{}) (string, error) {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
err := DefaultTemplates.ExecuteTemplate(&b, name+".html", data)
|
err := DefaultTemplates.ExecuteTemplate(&b, name+".html", data)
|
||||||
return string(b.Bytes()), err
|
content := string(b.Bytes())
|
||||||
|
if Config.MinifyTemplates {
|
||||||
|
content = min.Minify(content)
|
||||||
|
}
|
||||||
|
return content, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sbytes := []byte(wdata)
|
sbytes := []byte(wdata)
|
||||||
|
|
|
@ -38,6 +38,28 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WsHubImpl) Start() {
|
func (hub *WsHubImpl) Start() {
|
||||||
|
ticker := time.NewTicker(time.Minute * 5)
|
||||||
|
defer func() {
|
||||||
|
ticker.Stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
var item = func(lock *sync.RWMutex, userMap map[int]*WSUser) {
|
||||||
|
lock.RLock()
|
||||||
|
defer lock.RUnlock()
|
||||||
|
// TODO: Copy to temporary slice for less contention?
|
||||||
|
for _, user := range userMap {
|
||||||
|
user.Ping()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
item(&hub.evenUserLock, hub.evenOnlineUsers)
|
||||||
|
item(&hub.oddUserLock, hub.oddOnlineUsers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
if Config.DisableLiveTopicList {
|
if Config.DisableLiveTopicList {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -298,15 +320,10 @@ func (hub *WsHubImpl) removeUser(uid int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WsHubImpl) AddConn(user User, conn *websocket.Conn) (*WSUser, error) {
|
func (hub *WsHubImpl) AddConn(user User, conn *websocket.Conn) (*WSUser, error) {
|
||||||
// TODO: How should we handle user state changes if we're holding a pointer which never changes?
|
|
||||||
userptr, err := Users.Get(user.ID)
|
|
||||||
if err != nil && err != ErrStoreCapacityOverflow {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.ID == 0 {
|
if user.ID == 0 {
|
||||||
wsUser := new(WSUser)
|
wsUser := new(WSUser)
|
||||||
wsUser.User = userptr
|
wsUser.User = new(User)
|
||||||
|
*wsUser.User = user
|
||||||
wsUser.AddSocket(conn, "")
|
wsUser.AddSocket(conn, "")
|
||||||
WsHub.GuestLock.Lock()
|
WsHub.GuestLock.Lock()
|
||||||
WsHub.OnlineGuests[wsUser] = true
|
WsHub.OnlineGuests[wsUser] = true
|
||||||
|
@ -314,6 +331,12 @@ func (hub *WsHubImpl) AddConn(user User, conn *websocket.Conn) (*WSUser, error)
|
||||||
return wsUser, nil
|
return wsUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: How should we handle user state changes if we're holding a pointer which never changes?
|
||||||
|
userptr, err := Users.Get(user.ID)
|
||||||
|
if err != nil && err != ErrStoreCapacityOverflow {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var mutex *sync.RWMutex
|
var mutex *sync.RWMutex
|
||||||
var theMap map[int]*WSUser
|
var theMap map[int]*WSUser
|
||||||
if user.ID%2 == 0 {
|
if user.ID%2 == 0 {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package common
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
)
|
)
|
||||||
|
@ -21,6 +22,20 @@ type WSUserSocket struct {
|
||||||
Page string
|
Page string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wsUser *WSUser) Ping() error {
|
||||||
|
for _, socket := range wsUser.Sockets {
|
||||||
|
if socket == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
socket.conn.SetWriteDeadline(time.Now().Add(time.Minute))
|
||||||
|
err := socket.conn.WriteMessage(websocket.PingMessage, nil)
|
||||||
|
if err != nil {
|
||||||
|
socket.conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (wsUser *WSUser) WriteAll(msg string) error {
|
func (wsUser *WSUser) WriteAll(msg string) error {
|
||||||
msgbytes := []byte(msg)
|
msgbytes := []byte(msg)
|
||||||
for _, socket := range wsUser.Sockets {
|
for _, socket := range wsUser.Sockets {
|
||||||
|
|
|
@ -862,11 +862,22 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if lang != "" {
|
if lang != "" {
|
||||||
lang = strings.TrimSpace(lang)
|
lang = strings.TrimSpace(lang)
|
||||||
lLang := strings.Split(lang,"-")
|
lLang := strings.Split(lang,"-")
|
||||||
llLang := strings.Split(strings.Split(lLang[0],";")[0],",")
|
tLang := strings.Split(strings.Split(lLang[0],";")[0],",")
|
||||||
common.DebugDetail("llLang:", llLang)
|
common.DebugDetail("tLang:", tLang)
|
||||||
validCode := counters.LangViewCounter.Bump(llLang[0])
|
var llLang string
|
||||||
if !validCode {
|
for _, seg := range tLang {
|
||||||
r.DumpRequest(req,"Invalid ISO Code")
|
if seg == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
llLang = seg
|
||||||
|
}
|
||||||
|
if llLang == "" {
|
||||||
|
counters.LangViewCounter.Bump("none")
|
||||||
|
} else {
|
||||||
|
validCode := counters.LangViewCounter.Bump(llLang)
|
||||||
|
if !validCode {
|
||||||
|
r.DumpRequest(req,"Invalid ISO Code")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
counters.LangViewCounter.Bump("none")
|
counters.LangViewCounter.Bump("none")
|
||||||
|
@ -2104,6 +2115,11 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
case "robots.txt":
|
case "robots.txt":
|
||||||
counters.RouteViewCounter.Bump(134)
|
counters.RouteViewCounter.Bump(134)
|
||||||
return routes.RobotsTxt(w,req)
|
return routes.RobotsTxt(w,req)
|
||||||
|
case "favicon.ico":
|
||||||
|
req.URL.Path = "/static"+req.URL.Path+extraData
|
||||||
|
//log.Print("req.URL.Path: ",req.URL.Path)
|
||||||
|
routes.StaticFile(w,req)
|
||||||
|
return nil
|
||||||
/*case "sitemap.xml":
|
/*case "sitemap.xml":
|
||||||
counters.RouteViewCounter.Bump(135)
|
counters.RouteViewCounter.Bump(135)
|
||||||
return routes.SitemapXml(w,req)*/
|
return routes.SitemapXml(w,req)*/
|
||||||
|
|
|
@ -51,7 +51,8 @@
|
||||||
"bigpost_min_words":"Big Post Minimum Words",
|
"bigpost_min_words":"Big Post Minimum Words",
|
||||||
"megapost_min_words":"Mega Post Minimum Words",
|
"megapost_min_words":"Mega Post Minimum Words",
|
||||||
"meta_desc":"Meta Description",
|
"meta_desc":"Meta Description",
|
||||||
"rapid_loading":"Rapid Loaded?"
|
"rapid_loading":"Rapid Loaded?",
|
||||||
|
"google_site_verify":"Google Site Verify"
|
||||||
},
|
},
|
||||||
|
|
||||||
"PermPresets": {
|
"PermPresets": {
|
||||||
|
|
|
@ -26,6 +26,7 @@ func init() {
|
||||||
addPatch(12, patch12)
|
addPatch(12, patch12)
|
||||||
addPatch(13, patch13)
|
addPatch(13, patch13)
|
||||||
addPatch(14, patch14)
|
addPatch(14, patch14)
|
||||||
|
addPatch(15, patch15)
|
||||||
}
|
}
|
||||||
|
|
||||||
func patch0(scanner *bufio.Scanner) (err error) {
|
func patch0(scanner *bufio.Scanner) (err error) {
|
||||||
|
@ -532,3 +533,7 @@ func patch14(scanner *bufio.Scanner) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func patch15(scanner *bufio.Scanner) error {
|
||||||
|
return execStmt(qgen.Builder.SimpleInsert("settings", "name, content, type", "'google_site_verify','','html-attribute'"))
|
||||||
|
}
|
||||||
|
|
|
@ -283,8 +283,7 @@ function runWebSockets() {
|
||||||
(() => {
|
(() => {
|
||||||
addInitHook("pre_init", () => {
|
addInitHook("pre_init", () => {
|
||||||
// We can only get away with this because template_alert has no phrases, otherwise it too would have to be part of the "dance", I miss Go concurrency :(
|
// We can only get away with this because template_alert has no phrases, otherwise it too would have to be part of the "dance", I miss Go concurrency :(
|
||||||
loadScript("template_alert.js", () => {
|
notifyOnScriptW("/static/template_alert", () => {}, () => {
|
||||||
console.log("Loaded template_alert.js");
|
|
||||||
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(() => {
|
||||||
|
@ -471,6 +470,7 @@ function mainInit(){
|
||||||
let out = "";
|
let out = "";
|
||||||
for(let i = 0; i < topics.length;i++) out += Template_topics_topic(topics[i]);
|
for(let i = 0; i < topics.length;i++) out += Template_topics_topic(topics[i]);
|
||||||
$(".topic_list").html(out);
|
$(".topic_list").html(out);
|
||||||
|
//$(".topic_list").addClass("single_forum");
|
||||||
|
|
||||||
let obj = {Title: document.title, Url: url};
|
let obj = {Title: document.title, Url: url};
|
||||||
history.pushState(obj, obj.Title, obj.Url);
|
history.pushState(obj, obj.Title, obj.Url);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
var me = {};
|
var me = {};
|
||||||
var phraseBox = {};
|
var phraseBox = {};
|
||||||
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 = {
|
||||||
"pre_iffe": [],
|
"pre_iffe": [],
|
||||||
|
@ -52,41 +52,101 @@ function len(item) {
|
||||||
return item.length;
|
return item.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const asyncGetScript = (source) => {
|
function asyncGetScript(source) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let script = document.createElement('script');
|
let script = document.createElement('script');
|
||||||
script.async = true;
|
script.async = true;
|
||||||
|
|
||||||
const onloadHander = (haha, isAbort) => {
|
const onloadHandler = (e, isAbort) => {
|
||||||
if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
|
if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
|
||||||
script.onload = null;
|
script.onload = null;
|
||||||
script.onreadystatechange = null;
|
script.onreadystatechange = null;
|
||||||
script = undefined;
|
script = undefined;
|
||||||
|
|
||||||
isAbort ? reject(haha) : resolve();
|
isAbort ? reject(e) : resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
script.onerror = (haha) => {
|
script.onerror = (e) => {
|
||||||
reject(haha);
|
reject(e);
|
||||||
};
|
};
|
||||||
script.onload = onloadHander;
|
script.onload = onloadHandler;
|
||||||
script.onreadystatechange = onloadHander;
|
script.onreadystatechange = onloadHandler;
|
||||||
script.src = source;
|
script.src = source;
|
||||||
|
|
||||||
const prior = document.getElementsByTagName('script')[0];
|
const prior = document.getElementsByTagName('script')[0];
|
||||||
prior.parentNode.insertBefore(script, prior);
|
prior.parentNode.insertBefore(script, prior);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function loadScript(name, callback) {
|
function notifyOnScript(source) {
|
||||||
let url = "/static/"+name
|
return new Promise((resolve, reject) => {
|
||||||
|
let script = document.querySelectorAll('[src^="'+source+'"]')[0];
|
||||||
|
if(script===undefined) {
|
||||||
|
reject("no script found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!script.readyState) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onloadHandler = (e, isAbort) => {
|
||||||
|
if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
|
||||||
|
script.onload = null;
|
||||||
|
script.onreadystatechange = null;
|
||||||
|
isAbort ? reject(e) : resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
script.onerror = (e) => {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
script.onload = onloadHandler;
|
||||||
|
script.onreadystatechange = onloadHandler;
|
||||||
|
script.src = source;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyOnScriptW(name, complete, success) {
|
||||||
|
notifyOnScript(name)
|
||||||
|
.then(() => {
|
||||||
|
console.log("Loaded " +name+".js");
|
||||||
|
complete();
|
||||||
|
if(success!==undefined) success();
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log("Unable to get script name '"+name+"'");
|
||||||
|
console.log("e: ", e);
|
||||||
|
console.trace();
|
||||||
|
complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Send data at load time so we don't have to rely on a fallback template here
|
||||||
|
function loadScript(name, callback,fail) {
|
||||||
|
let fname = name;
|
||||||
|
let value = "; " + document.cookie;
|
||||||
|
let parts = value.split("; current_theme=");
|
||||||
|
if (parts.length == 2) fname += "_"+ parts.pop().split(";").shift();
|
||||||
|
|
||||||
|
let url = "/static/"+fname+".js"
|
||||||
|
let iurl = "/static/"+name+".js"
|
||||||
asyncGetScript(url)
|
asyncGetScript(url)
|
||||||
.then(callback)
|
.then(callback)
|
||||||
.catch((haha) => {
|
.catch((e) => {
|
||||||
console.log("Unable to get script '"+url+"'");
|
console.log("Unable to get script '"+url+"'");
|
||||||
console.log("haha: ", haha);
|
if(fname!=name) {
|
||||||
|
asyncGetScript(iurl)
|
||||||
|
.then(callback)
|
||||||
|
.catch((e) => {
|
||||||
|
console.log("Unable to get script '"+iurl+"'");
|
||||||
|
console.log("e: ", e);
|
||||||
|
console.trace();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("e: ", e);
|
||||||
console.trace();
|
console.trace();
|
||||||
|
fail(e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +166,7 @@ function RelativeTime(date) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function initPhrases() {
|
function initPhrases() {
|
||||||
|
console.log("in initPhrases")
|
||||||
fetchPhrases("status,topic_list,alerts,paginator")
|
fetchPhrases("status,topic_list,alerts,paginator")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,13 +205,11 @@ function fetchPhrases(plist) {
|
||||||
runInitHook("pre_iife");
|
runInitHook("pre_iife");
|
||||||
let toLoad = 2;
|
let toLoad = 2;
|
||||||
// TODO: Shunt this into loggedIn if there aren't any search and filter widgets?
|
// TODO: Shunt this into loggedIn if there aren't any search and filter widgets?
|
||||||
loadScript("template_topics_topic.js", () => {
|
notifyOnScriptW("/static/template_topics_topic", () => {
|
||||||
console.log("Loaded template_topics_topic.js");
|
|
||||||
toLoad--;
|
toLoad--;
|
||||||
if(toLoad===0) initPhrases();
|
if(toLoad===0) initPhrases();
|
||||||
});
|
});
|
||||||
loadScript("template_paginator.js", () => {
|
notifyOnScriptW("/static/template_paginator", () => {
|
||||||
console.log("Loaded template_paginator.js");
|
|
||||||
toLoad--;
|
toLoad--;
|
||||||
if(toLoad===0) initPhrases();
|
if(toLoad===0) initPhrases();
|
||||||
});
|
});
|
||||||
|
|
|
@ -654,11 +654,22 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if lang != "" {
|
if lang != "" {
|
||||||
lang = strings.TrimSpace(lang)
|
lang = strings.TrimSpace(lang)
|
||||||
lLang := strings.Split(lang,"-")
|
lLang := strings.Split(lang,"-")
|
||||||
llLang := strings.Split(strings.Split(lLang[0],";")[0],",")
|
tLang := strings.Split(strings.Split(lLang[0],";")[0],",")
|
||||||
common.DebugDetail("llLang:", llLang)
|
common.DebugDetail("tLang:", tLang)
|
||||||
validCode := counters.LangViewCounter.Bump(llLang[0])
|
var llLang string
|
||||||
if !validCode {
|
for _, seg := range tLang {
|
||||||
r.DumpRequest(req,"Invalid ISO Code")
|
if seg == "*" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
llLang = seg
|
||||||
|
}
|
||||||
|
if llLang == "" {
|
||||||
|
counters.LangViewCounter.Bump("none")
|
||||||
|
} else {
|
||||||
|
validCode := counters.LangViewCounter.Bump(llLang)
|
||||||
|
if !validCode {
|
||||||
|
r.DumpRequest(req,"Invalid ISO Code")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
counters.LangViewCounter.Bump("none")
|
counters.LangViewCounter.Bump("none")
|
||||||
|
@ -734,6 +745,11 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
case "robots.txt":
|
case "robots.txt":
|
||||||
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.RobotsTxt"}})
|
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.RobotsTxt"}})
|
||||||
return routes.RobotsTxt(w,req)
|
return routes.RobotsTxt(w,req)
|
||||||
|
case "favicon.ico":
|
||||||
|
req.URL.Path = "/static"+req.URL.Path+extraData
|
||||||
|
//log.Print("req.URL.Path: ",req.URL.Path)
|
||||||
|
routes.StaticFile(w,req)
|
||||||
|
return nil
|
||||||
/*case "sitemap.xml":
|
/*case "sitemap.xml":
|
||||||
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.SitemapXml"}})
|
counters.RouteViewCounter.Bump({{index .AllRouteMap "routes.SitemapXml"}})
|
||||||
return routes.SitemapXml(w,req)*/
|
return routes.SitemapXml(w,req)*/
|
||||||
|
|
|
@ -21,7 +21,10 @@ func ParseSEOURL(urlBit string) (slug string, id int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
if header.MetaDesc != "" && header.OGDesc == "" {
|
if header.CurrentUser.Loggedin {
|
||||||
|
header.MetaDesc = ""
|
||||||
|
header.OGDesc = ""
|
||||||
|
} else if header.MetaDesc != "" && header.OGDesc == "" {
|
||||||
header.OGDesc = header.MetaDesc
|
header.OGDesc = header.MetaDesc
|
||||||
}
|
}
|
||||||
// TODO: Expand this to non-HTTPS requests too
|
// TODO: Expand this to non-HTTPS requests too
|
||||||
|
|
|
@ -19,7 +19,7 @@ var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(common.Day)) // TODO: Mak
|
||||||
func StaticFile(w http.ResponseWriter, r *http.Request) {
|
func StaticFile(w http.ResponseWriter, r *http.Request) {
|
||||||
file, ok := common.StaticFiles.Get(r.URL.Path)
|
file, ok := common.StaticFiles.Get(r.URL.Path)
|
||||||
if !ok {
|
if !ok {
|
||||||
common.DebugLogf("Failed to find '%s'", r.URL.Path)
|
common.DebugLogf("Failed to find '%s'", r.URL.Path) // TODO: Use MicroNotFound? Might be better than the unneccessary overhead of sprintf
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ INSERT INTO [settings] ([name],[content],[type]) VALUES ('bigpost_min_words','25
|
||||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('megapost_min_words','1000','int');
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('megapost_min_words','1000','int');
|
||||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('meta_desc','','html-attribute');
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('meta_desc','','html-attribute');
|
||||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('rapid_loading','1','bool');
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('rapid_loading','1','bool');
|
||||||
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('google_site_verify','','html-attribute');
|
||||||
INSERT INTO [themes] ([uname],[default]) VALUES ('cosora',1);
|
INSERT INTO [themes] ([uname],[default]) VALUES ('cosora',1);
|
||||||
INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,1);
|
INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,1);
|
||||||
INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,'Admin');
|
INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,'Admin');
|
||||||
|
|
|
@ -12,6 +12,7 @@ INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('bigpost_min_words','250
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('rapid_loading','1','bool');
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('rapid_loading','1','bool');
|
||||||
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('google_site_verify','','html-attribute');
|
||||||
INSERT INTO `themes`(`uname`,`default`) VALUES ('cosora',1);
|
INSERT INTO `themes`(`uname`,`default`) VALUES ('cosora',1);
|
||||||
INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
||||||
INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,'Admin');
|
INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,'Admin');
|
||||||
|
|
|
@ -4,6 +4,7 @@ INSERT INTO "settings"("name","content","type") VALUES ('bigpost_min_words','250
|
||||||
INSERT INTO "settings"("name","content","type") VALUES ('megapost_min_words','1000','int');
|
INSERT INTO "settings"("name","content","type") VALUES ('megapost_min_words','1000','int');
|
||||||
INSERT INTO "settings"("name","content","type") VALUES ('meta_desc','','html-attribute');
|
INSERT INTO "settings"("name","content","type") VALUES ('meta_desc','','html-attribute');
|
||||||
INSERT INTO "settings"("name","content","type") VALUES ('rapid_loading','1','bool');
|
INSERT INTO "settings"("name","content","type") VALUES ('rapid_loading','1','bool');
|
||||||
|
INSERT INTO "settings"("name","content","type") VALUES ('google_site_verify','','html-attribute');
|
||||||
INSERT INTO "themes"("uname","default") VALUES ('cosora',1);
|
INSERT INTO "themes"("uname","default") VALUES ('cosora',1);
|
||||||
INSERT INTO "emails"("email","uid","validated") VALUES ('admin@localhost',1,1);
|
INSERT INTO "emails"("email","uid","validated") VALUES ('admin@localhost',1,1);
|
||||||
INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","tag") VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,'Admin');
|
INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","tag") VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,'Admin');
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<div id="forum_topic_list" class="rowblock topic_list" aria-label="{{lang "forum_list_aria"}}">
|
<div id="forum_topic_list" class="rowblock topic_list single_forum" aria-label="{{lang "forum_list_aria"}}">
|
||||||
{{range .ItemList}}<div class="topic_row{{if .Sticky}} topic_sticky{{else if .IsClosed}} topic_closed{{end}}" data-tid="{{.ID}}">
|
{{range .ItemList}}<div class="topic_row{{if .Sticky}} topic_sticky{{else if .IsClosed}} topic_closed{{end}}" data-tid="{{.ID}}">
|
||||||
<div class="rowitem topic_left passive datarow">
|
<div class="rowitem topic_left passive datarow">
|
||||||
<span class="selector"></span>
|
<span class="selector"></span>
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<link href="/static/{{.Header.Theme.Name}}/main.css" rel="stylesheet" type="text/css">
|
<link href="/static/{{.Header.Theme.Name}}/main.css" rel="stylesheet" type="text/css">
|
||||||
{{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}}
|
||||||
|
<script async type="text/javascript" src="/static/{{.}}"></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>
|
||||||
<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>
|
||||||
|
@ -20,6 +22,7 @@
|
||||||
<meta name="twitter:title" content="{{.Title}} | {{.Header.Site.Name}}" />
|
<meta name="twitter:title" content="{{.Title}} | {{.Header.Site.Name}}" />
|
||||||
{{if .OGDesc}}<meta property="og:description" content="{{.OGDesc}}" />
|
{{if .OGDesc}}<meta property="og:description" content="{{.OGDesc}}" />
|
||||||
<meta property="twitter:description" content="{{.OGDesc}}" />{{end}}
|
<meta property="twitter:description" content="{{.OGDesc}}" />{{end}}
|
||||||
|
{{if .GoogSiteVerify}}<meta name="google-site-verification" content="{{.GoogSiteVerify}}" />{{end}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{if not .CurrentUser.IsSuperMod}}<style>.supermod_only { display: none !important; }</style>{{end}}
|
{{if not .CurrentUser.IsSuperMod}}<style>.supermod_only { display: none !important; }</style>{{end}}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<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>
|
||||||
<div id="panel_settings" class="colstack_item rowlist">
|
<div id="panel_pages" class="colstack_item rowlist">
|
||||||
{{range .ItemList}}
|
{{range .ItemList}}
|
||||||
<div class="rowitem panel_compactrow">
|
<div class="rowitem panel_compactrow">
|
||||||
<a href="/panel/pages/edit/{{.ID}}" class="panel_upshift">{{.Title}}</a> <a href="/pages/{{.Name}}">[{{.Name}}]</a>
|
<a href="/panel/pages/edit/{{.ID}}" class="panel_upshift">{{.Title}}</a> <a href="/pages/{{.Name}}">[{{.Name}}]</a>
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
<div class="rowitem"><h1><a>{{lang "profile_ban_user_head"}}</a></h1></div>
|
<div class="rowitem"><h1><a>{{lang "profile_ban_user_head"}}</a></h1></div>
|
||||||
</div>
|
</div>
|
||||||
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}" method="post" style="display: none;">
|
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}" method="post" style="display: none;">
|
||||||
|
<div class="the_form">
|
||||||
{{/** TODO: Put a JS duration calculator here instead of this text? **/}}
|
{{/** TODO: Put a JS duration calculator here instead of this text? **/}}
|
||||||
<div class="colline">{{lang "profile_ban_user_notice"}}</div>
|
<div class="colline">{{lang "profile_ban_user_notice"}}</div>
|
||||||
<div class="colstack_item">
|
<div class="colstack_item">
|
||||||
|
@ -80,6 +81,7 @@
|
||||||
<div class="formitem"><button name="ban-button" class="formbutton form_middle_button">{{lang "profile_ban_user_button"}}</button></div>
|
<div class="formitem"><button name="ban-button" class="formbutton form_middle_button">{{lang "profile_ban_user_button"}}</button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
.colline {
|
||||||
|
border: 1px solid var(--element-border-color);
|
||||||
|
border-bottom: 2px solid var(--element-border-color);
|
||||||
|
background-color: var(--element-background-color);
|
||||||
|
padding: 8px;
|
||||||
|
margin-left: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<div class="topic_row{{if .Sticky}} topic_sticky{{else if .IsClosed}} topic_closed{{end}}" data-tid="{{.ID}}">
|
||||||
|
<div class="rowitem topic_left passive datarow">
|
||||||
|
<a href="{{.Creator.Link}}"><img src="{{.Creator.MicroAvatar}}" height=64 alt="{{.Creator.Name}}'s Avatar" title="{{.Creator.Name}}'s Avatar" /></a>
|
||||||
|
<span class="topic_inner_left">
|
||||||
|
<span class="rowtopic" itemprop="itemListElement" title="{{.Title}}"><a href="{{.Link}}">{{.Title}}</a>{{if .ForumName}}<a class="parent_forum_sep">-</a><a href="{{.ForumLink}}" title="{{.ForumName}}" class="rowsmall parent_forum">{{.ForumName}}</a>{{end}}</span>
|
||||||
|
<br /><a class="rowsmall starter" href="{{.Creator.Link}}" title="{{.Creator.Name}}">{{.Creator.Name}}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="topic_middle">
|
||||||
|
<div class="topic_middle_inside rowsmall">
|
||||||
|
<span class="replyCount">{{.PostCount}} {{lang "topic_list.replies_suffix"}}</span>
|
||||||
|
<span class="likeCount">{{.LikeCount}} {{lang "topic_list.likes_suffix"}}</span>
|
||||||
|
<span class="viewCount">{{.ViewCount}} {{lang "topic_list.views_suffix"}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="rowitem topic_right passive datarow">
|
||||||
|
<div class="topic_right_inside">
|
||||||
|
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.MicroAvatar}}" height=64 alt="{{.LastUser.Name}}'s Avatar" title="{{.LastUser.Name}}'s Avatar" /></a>
|
||||||
|
<span>
|
||||||
|
<a href="{{.LastUser.Link}}" class="lastName" title="{{.LastUser.Name}}">{{.LastUser.Name}}</a><br>
|
||||||
|
<a href="{{.Link}}?page={{.LastPage}}{{if .LastReplyID}}#post-{{.LastReplyID}}{{end}}" class="rowsmall lastReplyAt" title="{{abstime .LastReplyAt}}">{{reltime .LastReplyAt}}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,3 +1,6 @@
|
||||||
|
#main {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
.colstack_left {
|
.colstack_left {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
|
|
|
@ -209,6 +209,8 @@ li a {
|
||||||
}
|
}
|
||||||
.filter_item a {
|
.filter_item a {
|
||||||
color: #BBBBBB;
|
color: #BBBBBB;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.colstack_right .colstack_item:not(.colstack_head):not(.rowhead) .rowitem:not(:last-child) {
|
.colstack_right .colstack_item:not(.colstack_head):not(.rowhead) .rowitem:not(:last-child) {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
@ -512,8 +514,9 @@ h2 {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.topic_inner_left .parent_forum {
|
.parent_forum_sep {
|
||||||
display: none; /* Comment this until we figure out how to make it work */
|
margin-left: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
.topic_right_inside {
|
.topic_right_inside {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1176,6 +1179,9 @@ input[type=checkbox]:checked + label .sel {
|
||||||
.topic_list_title_block .mod_opt a:before {
|
.topic_list_title_block .mod_opt a:before {
|
||||||
content: "{{lang "topic_list.moderate_short" . }}";
|
content: "{{lang "topic_list.moderate_short" . }}";
|
||||||
}
|
}
|
||||||
|
.topic_inner_left .parent_forum, .parent_forum_sep {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: 601px) {
|
@media(max-width: 601px) {
|
||||||
|
@ -1274,6 +1280,9 @@ input[type=checkbox]:checked + label .sel {
|
||||||
.menu_profile {
|
.menu_profile {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#main {
|
||||||
|
max-width: calc(100% - 180px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: 850px) {
|
@media(max-width: 850px) {
|
||||||
|
|
|
@ -165,6 +165,8 @@ button, .formbutton, .panel_right_button:not(.has_inner_button), #panel_users .p
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
#panel_settings.rowlist.bgavatars.micro_grid, .micro_grid {
|
#panel_settings.rowlist.bgavatars.micro_grid, .micro_grid {
|
||||||
grid-gap: 24px;
|
grid-gap: 24px;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#main {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
#profile_left_lane {
|
#profile_left_lane {
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,9 @@ li a {
|
||||||
.rowmenu {
|
.rowmenu {
|
||||||
border: 1px solid hsl(0, 0%, 80%);
|
border: 1px solid hsl(0, 0%, 80%);
|
||||||
}
|
}
|
||||||
|
.rowmenu > div:not(:last-child) {
|
||||||
|
border-bottom: 1px solid hsl(0, 0%, 80%);
|
||||||
|
}
|
||||||
.rowsmall {
|
.rowsmall {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
.sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.colline {
|
||||||
|
border-left: 1px solid hsl(0, 0%, 80%);
|
||||||
|
padding: 10px;
|
||||||
|
border-right: 1px solid hsl(0, 0%, 80%);
|
||||||
|
background-color: white;
|
||||||
|
}
|
Loading…
Reference in New Issue