Reduce bandwidth usage for client templates.
Add js and ptmpl (stub) template functions. Simple constant folding for true / false values in templates. Use empty string instead of 0 for poll vote ips when DisablePollIP is enabled. Shorten some things.
This commit is contained in:
parent
7e3cd48284
commit
363826624f
@ -126,10 +126,10 @@ func LogWarning(err error, extra ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func errorHeader(w http.ResponseWriter, user User, title string) *Header {
|
func errorHeader(w http.ResponseWriter, user User, title string) *Header {
|
||||||
header := DefaultHeader(w, user)
|
h := DefaultHeader(w, user)
|
||||||
header.Title = title
|
h.Title = title
|
||||||
header.Zone = "error"
|
h.Zone = "error"
|
||||||
return header
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Dump the request?
|
// TODO: Dump the request?
|
||||||
@ -400,4 +400,4 @@ func handleErrorTemplate(w http.ResponseWriter, r *http.Request, pi ErrorPage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Alias of routes.renderTemplate
|
// Alias of routes.renderTemplate
|
||||||
var RenderTemplateAlias func(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, header *Header, pi interface{}) error
|
var RenderTemplateAlias func(tmplName, hookName string, w http.ResponseWriter, r *http.Request, header *Header, pi interface{}) error
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/tmpl_client"
|
tmpl "github.com/Azareal/Gosora/tmpl_client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SFileList map[string]SFile
|
type SFileList map[string]SFile
|
||||||
@ -28,11 +28,11 @@ type SFile struct {
|
|||||||
Data []byte
|
Data []byte
|
||||||
GzipData []byte
|
GzipData []byte
|
||||||
Sha256 string
|
Sha256 string
|
||||||
OName string
|
OName string
|
||||||
Pos int64
|
Pos int64
|
||||||
Length int64
|
Length int64
|
||||||
GzipLength int64
|
GzipLength int64
|
||||||
StrGzipLength string
|
StrGzipLength string
|
||||||
Mimetype string
|
Mimetype string
|
||||||
Info os.FileInfo
|
Info os.FileInfo
|
||||||
FormattedModTime string
|
FormattedModTime string
|
||||||
@ -70,7 +70,7 @@ func (list SFileList) JSTmplInit() error {
|
|||||||
data = data[startIndex-len([]byte("if(tmplInits===undefined)")):]
|
data = data[startIndex-len([]byte("if(tmplInits===undefined)")):]
|
||||||
data = replace(data, "// nolint", "")
|
data = replace(data, "// nolint", "")
|
||||||
data = replace(data, "func ", "function ")
|
data = replace(data, "func ", "function ")
|
||||||
data = replace(data, " error {\n", " {\nlet out = \"\"\n")
|
data = replace(data, " error {\n", " {\nlet o = \"\"\n")
|
||||||
funcIndex, hasFunc := skipAllUntilCharsExist(data, 0, []byte("function Template_"))
|
funcIndex, hasFunc := skipAllUntilCharsExist(data, 0, []byte("function Template_"))
|
||||||
if !hasFunc {
|
if !hasFunc {
|
||||||
return errors.New("no template function found")
|
return errors.New("no template function found")
|
||||||
@ -175,9 +175,9 @@ func (list SFileList) JSTmplInit() error {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
data = replace(data, "for _, item := range ", "for(item of ")
|
data = replace(data, "for _, item := range ", "for(item of ")
|
||||||
data = replace(data, "w.Write([]byte(", "out += ")
|
data = replace(data, "w.Write([]byte(", "o += ")
|
||||||
data = replace(data, "w.Write(StringToBytes(", "out += ")
|
data = replace(data, "w.Write(StringToBytes(", "o += ")
|
||||||
data = replace(data, "w.Write(", "out += ")
|
data = replace(data, "w.Write(", "o += ")
|
||||||
data = replace(data, "+= c.", "+= ")
|
data = replace(data, "+= c.", "+= ")
|
||||||
data = replace(data, "strconv.Itoa(", "")
|
data = replace(data, "strconv.Itoa(", "")
|
||||||
data = replace(data, "strconv.FormatInt(", "")
|
data = replace(data, "strconv.FormatInt(", "")
|
||||||
@ -186,7 +186,7 @@ func (list SFileList) JSTmplInit() error {
|
|||||||
data = replace(data, ", 10;", "")
|
data = replace(data, ", 10;", "")
|
||||||
data = replace(data, "var plist = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "const plist = tmplPhrases[\""+tmplName+"\"];")
|
data = replace(data, "var plist = GetTmplPhrasesBytes("+shortName+"_tmpl_phrase_id)", "const plist = tmplPhrases[\""+tmplName+"\"];")
|
||||||
data = replace(data, "var cached_var_", "let cached_var_")
|
data = replace(data, "var cached_var_", "let cached_var_")
|
||||||
data = replace(data, `tmpl_`+shortName+`_vars, ok := tmpl_`+shortName+`_i.`, `/*`)
|
data = replace(data, `tmpl_vars, ok := tmpl_i.`, `/*`)
|
||||||
data = replace(data, "[]byte(", "")
|
data = replace(data, "[]byte(", "")
|
||||||
data = replace(data, "StringToBytes(", "")
|
data = replace(data, "StringToBytes(", "")
|
||||||
data = replace(data, "RelativeTime(tmpl_"+shortName+"_vars.", "tmpl_"+shortName+"_vars.Relative")
|
data = replace(data, "RelativeTime(tmpl_"+shortName+"_vars.", "tmpl_"+shortName+"_vars.Relative")
|
||||||
@ -194,7 +194,7 @@ func (list SFileList) JSTmplInit() error {
|
|||||||
data = replace(data, ".Format(\"2006-01-02 15:04:05\"", "")
|
data = replace(data, ".Format(\"2006-01-02 15:04:05\"", "")
|
||||||
data = replace(data, ", 10", "")
|
data = replace(data, ", 10", "")
|
||||||
data = replace(data, "if ", "if(")
|
data = replace(data, "if ", "if(")
|
||||||
data = replace(data, "return nil", "return out")
|
data = replace(data, "return nil", "return o")
|
||||||
data = replace(data, " )", ")")
|
data = replace(data, " )", ")")
|
||||||
data = replace(data, " \n", "\n")
|
data = replace(data, " \n", "\n")
|
||||||
data = replace(data, "\n", ";\n")
|
data = replace(data, "\n", ";\n")
|
||||||
@ -212,10 +212,12 @@ func (list SFileList) JSTmplInit() error {
|
|||||||
|
|
||||||
fragset := tmpl.GetFrag(shortName)
|
fragset := tmpl.GetFrag(shortName)
|
||||||
if fragset != nil {
|
if fragset != nil {
|
||||||
sfrags := []byte("let " + shortName + "_frags = [];\n")
|
sfrags := []byte("let " + shortName + "_frags = [\n")
|
||||||
for _, frags := range fragset {
|
for _, frags := range fragset {
|
||||||
sfrags = append(sfrags, []byte(shortName+"_frags.push(`"+string(frags)+"`);\n")...)
|
//sfrags = append(sfrags, []byte(shortName+"_frags.push(`"+string(frags)+"`);\n")...)
|
||||||
|
sfrags = append(sfrags, []byte("\t`"+string(frags)+"`,\n")...)
|
||||||
}
|
}
|
||||||
|
sfrags = append(sfrags, []byte("];\n")...)
|
||||||
data = append(sfrags, data...)
|
data = append(sfrags, data...)
|
||||||
}
|
}
|
||||||
data = replace(data, "\n;", "\n")
|
data = replace(data, "\n;", "\n")
|
||||||
@ -240,7 +242,7 @@ func (list SFileList) JSTmplInit() error {
|
|||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
|
||||||
list.Set("/s/"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
list.Set("/s/"+path, SFile{data, gzipData, checksum, path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLogf("Added the '%s' static file.", path)
|
DebugLogf("Added the '%s' static file.", path)
|
||||||
return nil
|
return nil
|
||||||
@ -285,7 +287,7 @@ func (list SFileList) Init() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list.Set("/s/"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)})
|
list.Set("/s/"+path, SFile{data, gzipData, checksum, path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), mimetype, f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLogf("Added the '%s' static file.", path)
|
DebugLogf("Added the '%s' static file.", path)
|
||||||
return nil
|
return nil
|
||||||
@ -318,7 +320,7 @@ func (list SFileList) Add(path, prefix string) error {
|
|||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
|
||||||
list.Set("/s"+path, SFile{data, gzipData, checksum,path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
list.Set("/s"+path, SFile{data, gzipData, checksum, path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLogf("Added the '%s' static file", path)
|
DebugLogf("Added the '%s' static file", path)
|
||||||
return nil
|
return nil
|
||||||
|
@ -13,8 +13,8 @@ var FPStore ForumPermsStore
|
|||||||
type ForumPermsStore interface {
|
type ForumPermsStore interface {
|
||||||
Init() error
|
Init() error
|
||||||
GetAllMap() (bigMap map[int]map[int]*ForumPerms)
|
GetAllMap() (bigMap map[int]map[int]*ForumPerms)
|
||||||
Get(fid, gid int) (fperms *ForumPerms, err error)
|
Get(fid, gid int) (fp *ForumPerms, err error)
|
||||||
GetCopy(fid, gid int) (fperms ForumPerms, err error)
|
GetCopy(fid, gid int) (fp ForumPerms, err error)
|
||||||
ReloadAll() error
|
ReloadAll() error
|
||||||
Reload(id int) error
|
Reload(id int) error
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ func (s *MemoryForumPermsStore) GetAllMap() (bigMap map[int]map[int]*ForumPerms)
|
|||||||
// TODO: Add a hook here and have plugin_guilds use it
|
// TODO: Add a hook here and have plugin_guilds use it
|
||||||
// TODO: Check if the forum exists?
|
// TODO: Check if the forum exists?
|
||||||
// TODO: Fix the races
|
// TODO: Fix the races
|
||||||
func (s *MemoryForumPermsStore) Get(fid, gid int) (fperms *ForumPerms, err error) {
|
func (s *MemoryForumPermsStore) Get(fid, gid int) (fp *ForumPerms, err error) {
|
||||||
var fmap map[int]*ForumPerms
|
var fmap map[int]*ForumPerms
|
||||||
var ok bool
|
var ok bool
|
||||||
if fid%2 == 0 {
|
if fid%2 == 0 {
|
||||||
@ -206,22 +206,22 @@ func (s *MemoryForumPermsStore) Get(fid, gid int) (fperms *ForumPerms, err error
|
|||||||
s.oddLock.RUnlock()
|
s.oddLock.RUnlock()
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
return fperms, ErrNoRows
|
return fp, ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
fperms, ok = fmap[gid]
|
fp, ok = fmap[gid]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fperms, ErrNoRows
|
return fp, ErrNoRows
|
||||||
}
|
}
|
||||||
return fperms, nil
|
return fp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check if the forum exists?
|
// TODO: Check if the forum exists?
|
||||||
// TODO: Fix the races
|
// TODO: Fix the races
|
||||||
func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fperms ForumPerms, err error) {
|
func (s *MemoryForumPermsStore) GetCopy(fid, gid int) (fp ForumPerms, err error) {
|
||||||
fPermsPtr, err := s.Get(fid, gid)
|
fPermsPtr, err := s.Get(fid, gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fperms, err
|
return fp, err
|
||||||
}
|
}
|
||||||
return *fPermsPtr, nil
|
return *fPermsPtr, nil
|
||||||
}
|
}
|
||||||
|
@ -74,17 +74,17 @@ func InitPhrases(lang string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(path)
|
ext := filepath.Ext("/langs/" + path)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ext = filepath.Ext("/langs/" + path)
|
|
||||||
if ext != ".json" {
|
if ext != ".json" {
|
||||||
log.Printf("Found a '%s' in /langs/", ext)
|
log.Printf("Found a '%s' in /langs/", ext)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var langPack LanguagePack
|
var langPack LanguagePack
|
||||||
err = json.Unmarshal(data, &langPack)
|
err = json.Unmarshal(data, &langPack)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,7 +98,7 @@ func InitPhrases(lang string) error {
|
|||||||
|
|
||||||
// [prefix][name]phrase
|
// [prefix][name]phrase
|
||||||
langPack.TmplPhrasesPrefixes = make(map[string]map[string]string)
|
langPack.TmplPhrasesPrefixes = make(map[string]map[string]string)
|
||||||
var conMap = make(map[string]string) // Cache phrase strings so we can de-dupe items to reduce memory use. There appear to be some minor improvements with this, although we would need a more thorough check to be sure.
|
conMap := make(map[string]string) // Cache phrase strings so we can de-dupe items to reduce memory use. There appear to be some minor improvements with this, although we would need a more thorough check to be sure.
|
||||||
for name, phrase := range langPack.TmplPhrases {
|
for name, phrase := range langPack.TmplPhrases {
|
||||||
_, ok := conMap[phrase]
|
_, ok := conMap[phrase]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -304,10 +304,10 @@ func GetTmplPhrasesByPrefix(prefix string) (phrases map[string]string, ok bool)
|
|||||||
return res, ok
|
return res, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPlaceholder(prefix string, suffix string) string {
|
func getPlaceholder(prefix, suffix string) string {
|
||||||
return "{lang." + prefix + "[" + suffix + "]}"
|
return "{lang." + prefix + "[" + suffix + "]}"
|
||||||
}
|
}
|
||||||
func getPlaceholderBytes(prefix string, suffix string) []byte {
|
func getPlaceholderBytes(prefix, suffix string) []byte {
|
||||||
return []byte("{lang." + prefix + "[" + suffix + "]}")
|
return []byte("{lang." + prefix + "[" + suffix + "]}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ type PollStore interface {
|
|||||||
Get(id int) (*Poll, error)
|
Get(id int) (*Poll, error)
|
||||||
Exists(id int) bool
|
Exists(id int) bool
|
||||||
Create(parent Pollable, pollType int, pollOptions map[int]string) (int, error)
|
Create(parent Pollable, pollType int, pollOptions map[int]string) (int, error)
|
||||||
CastVote(optionIndex int, pollID int, uid int, ip string) error
|
CastVote(optionIndex, pollID, uid int, ip string) error
|
||||||
Reload(id int) error
|
Reload(id int) error
|
||||||
//Count() int
|
//Count() int
|
||||||
|
|
||||||
@ -77,24 +77,24 @@ func (s *DefaultPollStore) Exists(id int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultPollStore) Get(id int) (*Poll, error) {
|
func (s *DefaultPollStore) Get(id int) (*Poll, error) {
|
||||||
poll, err := s.cache.Get(id)
|
p, err := s.cache.Get(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return poll, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
poll = &Poll{ID: id}
|
p = &Poll{ID: id}
|
||||||
var optionTxt []byte
|
var optionTxt []byte
|
||||||
err = s.get.QueryRow(id).Scan(&poll.ParentID, &poll.ParentTable, &poll.Type, &optionTxt, &poll.VoteCount)
|
err = s.get.QueryRow(id).Scan(&p.ParentID, &p.ParentTable, &p.Type, &optionTxt, &p.VoteCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(optionTxt, &poll.Options)
|
err = json.Unmarshal(optionTxt, &p.Options)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
poll.QuickOptions = s.unpackOptionsMap(poll.Options)
|
p.QuickOptions = s.unpackOptionsMap(p.Options)
|
||||||
s.cache.Set(poll)
|
s.cache.Set(p)
|
||||||
}
|
}
|
||||||
return poll, err
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
@ -212,9 +212,9 @@ func (s *DefaultPollStore) unpackOptionsMap(rawOptions map[int]string) []PollOpt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use a transaction for this?
|
// TODO: Use a transaction for this?
|
||||||
func (s *DefaultPollStore) CastVote(optionIndex int, pollID int, uid int, ip string) error {
|
func (s *DefaultPollStore) CastVote(optionIndex, pollID, uid int, ip string) error {
|
||||||
if Config.DisablePollIP {
|
if Config.DisablePollIP {
|
||||||
ip = "0"
|
ip = ""
|
||||||
}
|
}
|
||||||
_, err := s.addVote.Exec(pollID, uid, optionIndex, ip)
|
_, err := s.addVote.Exec(pollID, uid, optionIndex, ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -526,7 +526,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
|
|||||||
|
|
||||||
t := TItemHold(make(map[string]TItem))
|
t := TItemHold(make(map[string]TItem))
|
||||||
|
|
||||||
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", 0, "", &user2, "", 0, &user3, "General", "/forum/general.2", nil}
|
topicsRow := &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "::1", 1, 0, 1, 0, 1, "classname", 0, "", &user2, "", 0, &user3, "General", "/forum/general.2", nil}
|
||||||
t.AddStd("topics_topic", "c.TopicsRow", topicsRow)
|
t.AddStd("topics_topic", "c.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{
|
poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{
|
||||||
@ -535,7 +535,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri
|
|||||||
}, VoteCount: 7}
|
}, VoteCount: 7}
|
||||||
avatar, microAvatar := BuildAvatar(62, "")
|
avatar, microAvatar := BuildAvatar(62, "")
|
||||||
miniAttach := []*MiniAttachment{&MiniAttachment{Path: "/"}}
|
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, nil, false}
|
topic := TopicUser{1, "blah", "Blah", "Hey there!", 62, false, false, now, now, 1, 1, 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, nil, false}
|
||||||
var replyList []*ReplyUser
|
var replyList []*ReplyUser
|
||||||
// TODO: Do we really want the UID here to be zero?
|
// TODO: Do we really want the UID here to be zero?
|
||||||
avatar, microAvatar = BuildAvatar(0, "")
|
avatar, microAvatar = BuildAvatar(0, "")
|
||||||
@ -816,6 +816,19 @@ func initDefaultTmplFuncMap() {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmap["ptmpl"] = func(nameInt, pageInt, headerInt interface{}) interface{} {
|
||||||
|
header := headerInt.(*Header)
|
||||||
|
err := header.Theme.RunTmpl(nameInt.(string), pageInt, header.Writer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fmap["js"] = func() interface{} {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
fmap["flush"] = func() interface{} {
|
fmap["flush"] = func() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,8 @@ func NewCTemplateSet(in string) *CTemplateSet {
|
|||||||
"reltime": true,
|
"reltime": true,
|
||||||
"scope": true,
|
"scope": true,
|
||||||
"dyntmpl": true,
|
"dyntmpl": true,
|
||||||
|
"ptmpl": true,
|
||||||
|
"js": true,
|
||||||
"index": true,
|
"index": true,
|
||||||
"flush": true,
|
"flush": true,
|
||||||
},
|
},
|
||||||
@ -183,7 +185,7 @@ type OutFrag struct {
|
|||||||
Body string
|
Body string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) CompileByLoggedin(name string, fileDir string, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (stub string, gout string, mout string, err error) {
|
func (c *CTemplateSet) CompileByLoggedin(name, fileDir, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (stub, gout, mout string, err error) {
|
||||||
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
|
||||||
@ -233,15 +235,15 @@ import "errors"
|
|||||||
// TODO: Try to remove this redundant interface cast
|
// TODO: Try to remove this redundant interface cast
|
||||||
stub += `
|
stub += `
|
||||||
// nolint
|
// nolint
|
||||||
func Template_` + fname + `(tmpl_` + fname + `_i interface{}, w io.Writer) error {
|
func Template_` + fname + `(tmpl_i interface{}, w io.Writer) error {
|
||||||
tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
|
tmpl_vars, ok := tmpl_i.(` + expects + `)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("invalid page struct value")
|
return errors.New("invalid page struct value")
|
||||||
}
|
}
|
||||||
if tmpl_` + fname + `_vars.CurrentUser.Loggedin {
|
if tmpl_vars.CurrentUser.Loggedin {
|
||||||
return Template_` + fname + `_member(tmpl_` + fname + `_i, w)
|
return Template_` + fname + `_member(tmpl_i, w)
|
||||||
}
|
}
|
||||||
return Template_` + fname + `_guest(tmpl_` + fname + `_i, w)
|
return Template_` + fname + `_guest(tmpl_i, w)
|
||||||
}`
|
}`
|
||||||
|
|
||||||
c.fileDir = fileDir
|
c.fileDir = fileDir
|
||||||
@ -265,7 +267,7 @@ func Template_` + fname + `(tmpl_` + fname + `_i interface{}, w io.Writer) error
|
|||||||
return stub, gout, mout, err
|
return stub, gout, mout, err
|
||||||
}
|
}
|
||||||
|
|
||||||
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, fileDir, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||||
if c.config.Debug {
|
if c.config.Debug {
|
||||||
c.logger.Println("Compiling template '" + name + "'")
|
c.logger.Println("Compiling template '" + name + "'")
|
||||||
}
|
}
|
||||||
@ -279,7 +281,7 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
|
|||||||
return c.compile(name, content, expects, expectsInt, varList, imports...)
|
return c.compile(name, content, expects, expectsInt, varList, imports...)
|
||||||
}
|
}
|
||||||
|
|
||||||
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, content, expects string, expectsInt interface{}, varList map[string]VarItem, imports ...string) (out string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
fmt.Println(r)
|
fmt.Println(r)
|
||||||
@ -363,6 +365,7 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
|||||||
|
|
||||||
var outBuf []OutBufferFrame
|
var outBuf []OutBufferFrame
|
||||||
rootHold := "tmpl_" + fname + "_vars"
|
rootHold := "tmpl_" + fname + "_vars"
|
||||||
|
//rootHold := "tmpl_vars"
|
||||||
con := CContext{
|
con := CContext{
|
||||||
RootHolder: rootHold,
|
RootHolder: rootHold,
|
||||||
VarHolder: rootHold,
|
VarHolder: rootHold,
|
||||||
@ -486,12 +489,17 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.lang == "normal" {
|
if c.lang == "normal" {
|
||||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_i interface{}, w io.Writer) error {\n"
|
fout += "// nolint\nfunc Template_" + fname + "(tmpl_i interface{}, w io.Writer) error {\n"
|
||||||
fout += `tmpl_` + fname + `_vars, ok := tmpl_` + fname + `_i.(` + expects + `)
|
fout += `tmpl_` + fname + `_vars, ok := tmpl_i.(` + expects + `)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("invalid page struct value")
|
return errors.New("invalid page struct value")
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
/*fout += `tmpl_vars, ok := tmpl_i.(` + expects + `)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid page struct value")
|
||||||
|
}
|
||||||
|
`*/
|
||||||
fout += `var iw http.ResponseWriter
|
fout += `var iw http.ResponseWriter
|
||||||
gzw, ok := w.(c.GzipResponseWriter)
|
gzw, ok := w.(c.GzipResponseWriter)
|
||||||
if ok {
|
if ok {
|
||||||
@ -501,6 +509,7 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe
|
|||||||
`
|
`
|
||||||
} else {
|
} else {
|
||||||
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars interface{}, w io.Writer) error {\n"
|
fout += "// nolint\nfunc Template_" + fname + "(tmpl_" + fname + "_vars interface{}, w io.Writer) error {\n"
|
||||||
|
//fout += "// nolint\nfunc Template_" + fname + "(tmpl_vars interface{}, w io.Writer) error {\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.langIndexToName) > 0 {
|
if len(c.langIndexToName) > 0 {
|
||||||
@ -678,6 +687,15 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// simple constant folding
|
||||||
|
if expr == "true" {
|
||||||
|
c.compileSwitch(con, node.List)
|
||||||
|
return
|
||||||
|
} else if expr == "false" {
|
||||||
|
c.compileSwitch(con, node.ElseList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
con.Push("startif", "if "+expr+" {\n")
|
con.Push("startif", "if "+expr+" {\n")
|
||||||
c.compileSwitch(con, node.List)
|
c.compileSwitch(con, node.List)
|
||||||
if node.ElseList == nil {
|
if node.ElseList == nil {
|
||||||
@ -980,16 +998,16 @@ func (c *CTemplateSet) compileExprSwitch(con CContext, node *parse.CommandNode)
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) unknownNode(node parse.Node) {
|
func (c *CTemplateSet) unknownNode(n parse.Node) {
|
||||||
elem := reflect.ValueOf(node).Elem()
|
elem := reflect.ValueOf(n).Elem()
|
||||||
c.logger.Println("Unknown Kind:", elem.Kind())
|
c.logger.Println("Unknown Kind:", elem.Kind())
|
||||||
c.logger.Println("Unknown Type:", elem.Type().Name())
|
c.logger.Println("Unknown Type:", elem.Type().Name())
|
||||||
panic("I don't know what node this is! Grr...")
|
panic("I don't know what node this is! Grr...")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) compileIdentSwitchN(con CContext, node *parse.CommandNode) (out string) {
|
func (c *CTemplateSet) compileIdentSwitchN(con CContext, n *parse.CommandNode) (out string) {
|
||||||
c.detail("in compileIdentSwitchN")
|
c.detail("in compileIdentSwitchN")
|
||||||
out, _, _, _ = c.compileIdentSwitch(con, node)
|
out, _, _, _ = c.compileIdentSwitch(con, n)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1052,7 +1070,7 @@ func (c *CTemplateSet) compareJoin(con CContext, pos int, node *parse.CommandNod
|
|||||||
return pos, out
|
return pos, out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) compileIdentSwitch(con CContext, node *parse.CommandNode) (out string, val reflect.Value, literal bool, notident bool) {
|
func (c *CTemplateSet) compileIdentSwitch(con CContext, node *parse.CommandNode) (out string, val reflect.Value, literal, notident bool) {
|
||||||
c.dumpCall("compileIdentSwitch", con, node)
|
c.dumpCall("compileIdentSwitch", con, node)
|
||||||
litString := func(inner string, bytes bool) {
|
litString := func(inner string, bytes bool) {
|
||||||
if !bytes {
|
if !bytes {
|
||||||
@ -1136,6 +1154,14 @@ ArgLoop:
|
|||||||
out = "c.HasWidgets(" + leftParam + "," + rightParam + ")"
|
out = "c.HasWidgets(" + leftParam + "," + rightParam + ")"
|
||||||
literal = true
|
literal = true
|
||||||
break ArgLoop
|
break ArgLoop
|
||||||
|
case "js":
|
||||||
|
if c.lang == "js" {
|
||||||
|
out = "true"
|
||||||
|
} else {
|
||||||
|
out = "false"
|
||||||
|
}
|
||||||
|
literal = true
|
||||||
|
break ArgLoop
|
||||||
case "lang":
|
case "lang":
|
||||||
// TODO: Implement string literals properly
|
// TODO: Implement string literals properly
|
||||||
leftOp := node.Args[pos+1].String()
|
leftOp := node.Args[pos+1].String()
|
||||||
@ -1172,7 +1198,7 @@ ArgLoop:
|
|||||||
for i := pos + 2; i < len(node.Args); i++ {
|
for i := pos + 2; i < len(node.Args); i++ {
|
||||||
op := node.Args[i].String()
|
op := node.Args[i].String()
|
||||||
if op != "" {
|
if op != "" {
|
||||||
if /*op[0] == '.' || */op[0] == '$' {
|
if /*op[0] == '.' || */ op[0] == '$' {
|
||||||
panic("langf args cannot be dynamic")
|
panic("langf args cannot be dynamic")
|
||||||
}
|
}
|
||||||
if op[0] != '.' && op[0] != '"' && !unicode.IsDigit(rune(op[0])) {
|
if op[0] != '.' && op[0] != '"' && !unicode.IsDigit(rune(op[0])) {
|
||||||
@ -1262,7 +1288,8 @@ ArgLoop:
|
|||||||
case "scope":
|
case "scope":
|
||||||
literal = true
|
literal = true
|
||||||
break ArgLoop
|
break ArgLoop
|
||||||
case "dyntmpl":
|
// TODO: Optimise ptmpl
|
||||||
|
case "dyntmpl", "ptmpl":
|
||||||
var pageParam, headParam string
|
var pageParam, headParam string
|
||||||
// TODO: Implement string literals properly
|
// TODO: Implement string literals properly
|
||||||
// TODO: Should we check to see if pos+3 is within the bounds of the slice?
|
// TODO: Should we check to see if pos+3 is within the bounds of the slice?
|
||||||
|
@ -16,8 +16,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
p "github.com/Azareal/Gosora/common/phrases"
|
p "github.com/Azareal/Gosora/common/phrases"
|
||||||
@ -91,7 +91,7 @@ type ThemeMapTmplToDock struct {
|
|||||||
func (t *Theme) LoadStaticFiles() error {
|
func (t *Theme) LoadStaticFiles() error {
|
||||||
t.ResourceTemplates = template.New("")
|
t.ResourceTemplates = template.New("")
|
||||||
fmap := make(map[string]interface{})
|
fmap := make(map[string]interface{})
|
||||||
fmap["lang"] = func(phraseNameInt interface{}, tmplInt interface{}) interface{} {
|
fmap["lang"] = func(phraseNameInt, tmplInt interface{}) interface{} {
|
||||||
phraseName, ok := phraseNameInt.(string)
|
phraseName, ok := phraseNameInt.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("phraseNameInt is not a string")
|
panic("phraseNameInt is not a string")
|
||||||
@ -167,7 +167,7 @@ func (t *Theme) AddThemeStaticFiles() error {
|
|||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
checksum := hex.EncodeToString(hasher.Sum(nil))
|
checksum := hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
|
||||||
StaticFiles.Set("/s/"+t.Name+path, SFile{data, gzipData, checksum,t.Name+path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)),strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
StaticFiles.Set("/s/"+t.Name+path, SFile{data, gzipData, checksum, t.Name + path + "?h=" + checksum, 0, int64(len(data)), int64(len(gzipData)), strconv.Itoa(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
|
||||||
|
|
||||||
DebugLog("Added the '/" + t.Name + path + "' static file for theme " + t.Name + ".")
|
DebugLog("Added the '/" + t.Name + path + "' static file for theme " + t.Name + ".")
|
||||||
return nil
|
return nil
|
||||||
@ -252,7 +252,6 @@ func UpdateDefaultTheme(t *Theme) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return ErrNoDefaultTheme
|
return ErrNoDefaultTheme
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dtheme.setActive(false)
|
err = dtheme.setActive(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -236,7 +236,7 @@ var imageExts = ["png", "jpg", "jpe","jpeg","jif","jfi","jfif", "svg", "bmp", "g
|
|||||||
});
|
});
|
||||||
|
|
||||||
let bulkActionSender = function(action, selectedTopics, fragBit) {
|
let bulkActionSender = function(action, selectedTopics, fragBit) {
|
||||||
let url = "/topic/"+action+"/submit/"+fragBit+"?s=" + me.User.S;
|
let url = "/topic/"+action+"/submit/"+fragBit+"?s="+me.User.S;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@ -265,7 +265,7 @@ var imageExts = ["png", "jpg", "jpe","jpeg","jif","jfi","jfif", "svg", "bmp", "g
|
|||||||
let fid = this.getAttribute("data-fid");
|
let fid = this.getAttribute("data-fid");
|
||||||
if (fid == null) return;
|
if (fid == null) return;
|
||||||
this.classList.add("pane_selected");
|
this.classList.add("pane_selected");
|
||||||
console.log("fid: " + fid);
|
console.log("fid:" + fid);
|
||||||
forumToMoveTo = fid;
|
forumToMoveTo = fid;
|
||||||
|
|
||||||
$("#mover_submit").unbind("click");
|
$("#mover_submit").unbind("click");
|
||||||
@ -298,8 +298,8 @@ var imageExts = ["png", "jpg", "jpe","jpeg","jif","jfi","jfif", "svg", "bmp", "g
|
|||||||
}
|
}
|
||||||
|
|
||||||
var pollInputIndex = 1;
|
var pollInputIndex = 1;
|
||||||
$("#add_poll_button").click((event) => {
|
$("#add_poll_button").click((ev) => {
|
||||||
event.preventDefault();
|
ev.preventDefault();
|
||||||
$(".poll_content_row").removeClass("auto_hide");
|
$(".poll_content_row").removeClass("auto_hide");
|
||||||
$("#has_poll_input").val("1");
|
$("#has_poll_input").val("1");
|
||||||
$(".pollinputinput").click(addPollInput);
|
$(".pollinputinput").click(addPollInput);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
(() => {
|
(() => {
|
||||||
addInitHook("end_init", () => {
|
addInitHook("end_init", () => {
|
||||||
fetch("/api/watches/")
|
fetch("/api/watches/")
|
||||||
.then(response => {
|
.then(resp => {
|
||||||
if(response.status!==200) {
|
if(resp.status!==200) {
|
||||||
console.log("error");
|
console.log("error");
|
||||||
console.log("response:", response);
|
console.log("response:", resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
response.text().then(data => eval(data));
|
resp.text().then(data => eval(data));
|
||||||
})
|
})
|
||||||
.catch(err => console.log("err:", err));
|
.catch(err => console.log("err:", err));
|
||||||
});
|
});
|
||||||
|
@ -43,7 +43,7 @@ func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, hea
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildBasePage(w http.ResponseWriter, r *http.Request, user *c.User, titlePhrase string, zone string) (*c.BasePanelPage, c.RouteError) {
|
func buildBasePage(w http.ResponseWriter, r *http.Request, user *c.User, titlePhrase, zone string) (*c.BasePanelPage, c.RouteError) {
|
||||||
header, stats, ferr := c.PanelUserCheck(w, r, user)
|
header, stats, ferr := c.PanelUserCheck(w, r, user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return nil, ferr
|
return nil, ferr
|
||||||
|
@ -214,19 +214,19 @@ func GroupsPromotionsCreateSubmit(w http.ResponseWriter, r *http.Request, user c
|
|||||||
return c.LocalError("posts must be integer", w, r, user)
|
return c.LocalError("posts must be integer", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
registeredHours, err := strconv.Atoi(r.FormValue("registered_hours"))
|
regHours, err := strconv.Atoi(r.FormValue("registered_hours"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.LocalError("registered_hours must be integer", w, r, user)
|
return c.LocalError("registered_hours must be integer", w, r, user)
|
||||||
}
|
}
|
||||||
registeredDays, err := strconv.Atoi(r.FormValue("registered_days"))
|
regDays, err := strconv.Atoi(r.FormValue("registered_days"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.LocalError("registered_days must be integer", w, r, user)
|
return c.LocalError("registered_days must be integer", w, r, user)
|
||||||
}
|
}
|
||||||
registeredMonths, err := strconv.Atoi(r.FormValue("registered_months"))
|
regMonths, err := strconv.Atoi(r.FormValue("registered_months"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.LocalError("registered_months must be integer", w, r, user)
|
return c.LocalError("registered_months must be integer", w, r, user)
|
||||||
}
|
}
|
||||||
registeredMinutes := (registeredHours * 60) + (registeredDays * 24 * 60) + (registeredMonths * 30 * 24 * 60)
|
regMinutes := (regHours * 60) + (regDays * 24 * 60) + (regMonths * 30 * 24 * 60)
|
||||||
|
|
||||||
g, err := c.Groups.Get(from)
|
g, err := c.Groups.Get(from)
|
||||||
ferr := groupCheck(w, r, user, g, err)
|
ferr := groupCheck(w, r, user, g, err)
|
||||||
@ -238,7 +238,7 @@ func GroupsPromotionsCreateSubmit(w http.ResponseWriter, r *http.Request, user c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
pid, err := c.GroupPromotions.Create(from, to, twoWay, level, posts, registeredMinutes)
|
pid, err := c.GroupPromotions.Create(from, to, twoWay, level, posts, regMinutes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
|
|||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
logCount := c.RegLogs.Count()
|
logCount := c.RegLogs.Count()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 12
|
perPage := 12
|
||||||
@ -174,7 +173,6 @@ func LogsMod(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
|
|||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 12
|
perPage := 12
|
||||||
offset, page, lastPage := c.PageOffset(c.ModLogs.Count(), page, perPage)
|
offset, page, lastPage := c.PageOffset(c.ModLogs.Count(), page, perPage)
|
||||||
|
@ -27,7 +27,7 @@ func init() {
|
|||||||
replyStmts = ReplyStmts{
|
replyStmts = ReplyStmts{
|
||||||
// TODO: Less race-y attachment count updates
|
// TODO: Less race-y attachment count updates
|
||||||
updateAttachs: acc.Update("replies").Set("attachCount=?").Where("rid=?").Prepare(),
|
updateAttachs: acc.Update("replies").Set("attachCount=?").Where("rid=?").Prepare(),
|
||||||
createReplyPaging: acc.Select("replies").Cols("rid").Where("rid >= ? - 1 AND tid = ?").Orderby("rid ASC").Prepare(),
|
createReplyPaging: acc.Select("replies").Cols("rid").Where("rid >= ? - 1 AND tid=?").Orderby("rid ASC").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -117,7 +117,6 @@ func UnbanUser(w http.ResponseWriter, r *http.Request, user c.User, suid string)
|
|||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !targetUser.IsBanned {
|
if !targetUser.IsBanned {
|
||||||
return c.LocalError("The user you're trying to unban isn't banned.", w, r, user)
|
return c.LocalError("The user you're trying to unban isn't banned.", w, r, user)
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Forum.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" aria-label="{{lang "paginator.prev_page_aria"}}" rel="prev" href="{{.Forum.Link}}?page={{subtract .Page 1}}">{{lang "paginator.less_than"}}</a></div>{{end}}
|
||||||
{{if ne .LastPage .Page}}<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Forum.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a></div>{{end}}
|
{{if ne .LastPage .Page}}<div id="nextFloat" class="next_button"><a class="next_link" aria-label="{{lang "paginator.next_page_aria"}}" rel="next" href="{{.Forum.Link}}?page={{add .Page 1}}">{{lang "paginator.greater_than"}}</a></div>{{end}}
|
||||||
<link rel="canonical" href="//{{.Site.URL}}{{.Forum.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}" />
|
<link rel="canonical" href="//{{.Site.URL}}{{.Forum.Link}}{{if gt .Page 1}}?page={{.Page}}{{end}}"/>
|
||||||
|
|
||||||
<main id="forumItemList" itemscope itemtype="http://schema.org/ItemList">
|
<main id="forumItemList" itemscope itemtype="http://schema.org/ItemList">
|
||||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block{{if .CurrentUser.Loggedin}} has_opt{{end}}">
|
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block{{if .CurrentUser.Loggedin}} has_opt{{end}}">
|
||||||
@ -49,7 +49,7 @@
|
|||||||
{{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>
|
||||||
<a href="{{.Creator.Link}}"><img src="{{.Creator.MicroAvatar}}" height=64 alt="Avatar" title="{{.Creator.Name}}'s Avatar" aria-hidden="true" /></a>
|
<a href="{{.Creator.Link}}"><img src="{{.Creator.MicroAvatar}}" height=64 alt="Avatar" title="{{.Creator.Name}}'s Avatar" aria-hidden="true"/></a>
|
||||||
<span class="topic_inner_left">
|
<span class="topic_inner_left">
|
||||||
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement" title="{{.Title}}"><span>{{.Title}}</span></a>
|
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement" title="{{.Title}}"><span>{{.Title}}</span></a>
|
||||||
<br /><a class="rowsmall starter" href="{{.Creator.Link}}" title="{{.Creator.Name}}">{{.Creator.Name}}</a>
|
<br /><a class="rowsmall starter" href="{{.Creator.Link}}" title="{{.Creator.Name}}">{{.Creator.Name}}</a>
|
||||||
@ -72,7 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow">
|
<div class="rowitem topic_right passive datarow">
|
||||||
<div class="topic_right_inside">
|
<div class="topic_right_inside">
|
||||||
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.MicroAvatar}}" height=64 alt="Avatar" title="{{.LastUser.Name}}'s Avatar" aria-hidden="true" /></a>
|
<a href="{{.LastUser.Link}}"><img src="{{.LastUser.MicroAvatar}}" height=64 alt="Avatar" title="{{.LastUser.Name}}'s Avatar" aria-hidden="true"/></a>
|
||||||
<span>
|
<span>
|
||||||
<a href="{{.LastUser.Link}}" class="lastName" title="{{.LastUser.Name}}">{{.LastUser.Name}}</a><br>
|
<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>
|
<a href="{{.Link}}?page={{.LastPage}}{{if .LastReplyID}}#post-{{.LastReplyID}}{{end}}" class="rowsmall lastReplyAt" title="{{abstime .LastReplyAt}}">{{reltime .LastReplyAt}}</a>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{{range .ItemList}}<article {{scope "post"}} id="post-{{.ID}}" itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item{{if .ActionType}} action_item{{end}}{{if .Attachments}} has_attachs{{end}}">
|
{{range .ItemList}}<article {{scope "post"}} id="post-{{.ID}}" itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item{{if .ActionType}} action_item{{end}}{{if .Attachments}} has_attachs{{end}}">
|
||||||
{{template "topic_alt_userinfo.html" . }}
|
{{if js}}js{{/**{{ptmpl "topic_alt_userinfo" .}}**/}}{{else}}{{template "topic_alt_userinfo.html" . }}{{end}}
|
||||||
<div class="content_container">
|
<div class="content_container">
|
||||||
{{if .ActionType}}
|
{{if .ActionType}}
|
||||||
<span class="action_icon" aria-hidden="true">{{.ActionIcon}}</span>
|
<span class="action_icon" aria-hidden="true">{{.ActionIcon}}</span>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
{{range .Attachments}}
|
{{range .Attachments}}
|
||||||
<div class="attach_item attach_item_item{{if .Image}} attach_image_holder{{end}}">
|
<div class="attach_item attach_item_item{{if .Image}} attach_image_holder{{end}}">
|
||||||
{{if .Image}}<img src="//{{$.Header.Site.URL}}/attachs/{{.Path}}?sid={{.SectionID}}&stype=forums" height=24 width=24 />{{end}}
|
{{if .Image}}<img src="//{{$.Header.Site.URL}}/attachs/{{.Path}}?sid={{.SectionID}}&stype=forums" height=24 width=24 />{{end}}
|
||||||
<span class="attach_item_path" aid="{{.ID}}" fullPath="//{{$.Header.Site.URL}}/attachs/{{.Path}}">{{.Path}}</span>
|
<span class="attach_item_path" aid={{.ID}} fullPath="//{{$.Header.Site.URL}}/attachs/{{.Path}}">{{.Path}}</span>
|
||||||
<button class="attach_item_select">{{lang "topic.select_button_text"}}</button>
|
<button class="attach_item_select">{{lang "topic.select_button_text"}}</button>
|
||||||
<button class="attach_item_copy">{{lang "topic.copy_button_text"}}</button>
|
<button class="attach_item_copy">{{lang "topic.copy_button_text"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user