Alerts are now rendered via a client side transpiled template rather than being hard-coded.

Tweaked some bits to make them more 32-bit friendly for GopherJS, but this might not be necessary now.
Added notice.html
Added an alerts package to fix the import cycles, more things may be moved here soon.
Saved a few lines of accumulator code in a few stores.
Moved the AccountEditCriticalSubmit, AccountEditAvatar, AccountEditAvatarSubmit, AccountEditUsername, and AccountEditUsernameSubmit routes into the routes package.
Added a QueryRow method to AccSelectBuilder.
Tweaked the indentation in the generated templates.
Simplified the template render in the AccountEditUsernameSubmit route into a redirect back to the previous page.

Run the update script / patcher to replace the route names in the viewchunks table.
This commit is contained in:
Azareal 2018-05-14 18:56:56 +10:00
parent 185f00e019
commit 9075798128
49 changed files with 691 additions and 419 deletions

View File

@ -4,7 +4,8 @@ import (
"bytes"
"../common"
"../tmpl_gen/client"
"../common/alerts"
"../tmpl_gen"
"github.com/gopherjs/gopherjs/js"
)
@ -19,7 +20,7 @@ func main() {
js.Global.Set("renderAlert", func(asid int, path string, msg string, avatar string) string {
var buf bytes.Buffer
alertItem := common.AlertItem{asid, path, msg, avatar}
alertItem := alerts.AlertItem{asid, path, msg, avatar}
err := tmpl.Template_alert(alertItem, &buf)
if err != nil {
println(err.Error())

10
common/alerts/tmpls.go Normal file
View File

@ -0,0 +1,10 @@
package alerts
// TODO: Move the other alert related stuff to package alerts, maybe move notification logic here too?
type AlertItem struct {
ASID int
Path string
Message string
Avatar string
}

View File

@ -101,18 +101,18 @@ func (auth *DefaultAuth) ForceLogout(uid int) error {
// Logout logs you out of the computer you requested the logout for, but not the other computers you're logged in with
func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
cookie := http.Cookie{Name: "uid", Value: "", Path: "/", MaxAge: Year}
cookie := http.Cookie{Name: "uid", Value: "", Path: "/", MaxAge: int(Year)}
http.SetCookie(w, &cookie)
cookie = http.Cookie{Name: "session", Value: "", Path: "/", MaxAge: Year}
cookie = http.Cookie{Name: "session", Value: "", Path: "/", MaxAge: int(Year)}
http.SetCookie(w, &cookie)
}
// TODO: Set the cookie domain
// SetCookies sets the two cookies required for the current user to be recognised as a specific user in future requests
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: Year}
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: int(Year)}
http.SetCookie(w, &cookie)
cookie = http.Cookie{Name: "session", Value: session, Path: "/", MaxAge: Year}
cookie = http.Cookie{Name: "session", Value: session, Path: "/", MaxAge: int(Year)}
http.SetCookie(w, &cookie)
}

View File

@ -8,16 +8,16 @@ import (
)
// nolint I don't want to write comments for each of these o.o
const Hour int = 60 * 60
const Day int = Hour * 24
const Week int = Day * 7
const Month int = Day * 30
const Year int = Day * 365
const Kilobyte int = 1024
const Megabyte int = Kilobyte * 1024
const Gigabyte int = Megabyte * 1024
const Terabyte int = Gigabyte * 1024
const Petabyte int = Terabyte * 1024
const Hour int64 = 60 * 60
const Day int64 = Hour * 24
const Week int64 = Day * 7
const Month int64 = Day * 30
const Year int64 = Day * 365
const Kilobyte int64 = 1024
const Megabyte int64 = Kilobyte * 1024
const Gigabyte int64 = Megabyte * 1024
const Terabyte int64 = Gigabyte * 1024
const Petabyte int64 = Terabyte * 1024
const SaltLength int = 32
const SessionLength int = 80

View File

@ -2,6 +2,8 @@ package common
import (
"bytes"
"errors"
"fmt"
"mime"
"strings"
"sync"
@ -11,6 +13,8 @@ import (
"net/http"
"os"
"path/filepath"
"../tmpl_gen"
)
type SFileList map[string]SFile
@ -33,6 +37,153 @@ type CSSData struct {
Phrases map[string]string
}
func (list SFileList) JSTmplInit() error {
var fragMap = make(map[string][][]byte)
fragMap["alert"] = tmpl.Get_alert_frags() // TODO: Add a generic fetch function, so we don't rely on the presence of the template files for this
fmt.Println("fragMap: ", fragMap)
return filepath.Walk("./tmpl_gen", func(path string, f os.FileInfo, err error) error {
if f.IsDir() {
return nil
}
if strings.HasSuffix(path, "template_list.go") {
return nil
}
path = strings.Replace(path, "\\", "/", -1)
DebugLog("Processing client template " + path)
data, err := ioutil.ReadFile(path)
if err != nil {
return err
}
var replace = func(data []byte, replaceThis string, withThis string) []byte {
return bytes.Replace(data, []byte(replaceThis), []byte(withThis), -1)
}
startIndex, hasFunc := skipAllUntilCharsExist(data, 0, []byte("func Template"))
if !hasFunc {
return errors.New("no template function found")
}
data = data[startIndex-len([]byte("func Template")):]
data = replace(data, "func ", "function ")
data = replace(data, " error {\n", " {\nlet out = \"\"\n")
spaceIndex, hasSpace := skipUntilIfExists(data, 10, ' ')
if !hasSpace {
return errors.New("no spaces found after the template function name")
}
endBrace, hasBrace := skipUntilIfExists(data, spaceIndex, ')')
if !hasBrace {
return errors.New("no right brace found after the template function name")
}
fmt.Println("spaceIndex: ", spaceIndex)
fmt.Println("endBrace: ", endBrace)
fmt.Println("string(data[spaceIndex:endBrace]): ", string(data[spaceIndex:endBrace]))
preLen := len(data)
data = replace(data, string(data[spaceIndex:endBrace]), "")
data = replace(data, "))\n", "\n")
endBrace -= preLen - len(data) // Offset it as we've deleted portions
var showPos = func(data []byte, index int) (out string) {
out = "["
for j, char := range data {
if index == j {
out += "[" + string(char) + "] "
} else {
out += string(char) + " "
}
}
return out + "]"
}
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
var each = func(phrase string, handle func(index int)) {
fmt.Println("find each '" + phrase + "'")
var index = endBrace
var foundIt bool
for {
fmt.Println("in index: ", index)
fmt.Println("pos: ", showPos(data, index))
index, foundIt = skipAllUntilCharsExist(data, index, []byte(phrase))
if !foundIt {
break
}
handle(index)
}
}
each("strconv.Itoa(", func(index int) {
braceAt, hasEndBrace := skipUntilIfExists(data, index, ')')
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
if hasEndBrace {
data[braceAt] = ' ' // Blank it
}
})
each("w.Write([]byte(", func(index int) {
braceAt, hasEndBrace := skipUntilIfExists(data, index, ')')
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
if hasEndBrace {
data[braceAt] = ' ' // Blank it
}
braceAt, hasEndBrace = skipUntilIfExists(data, braceAt, ')')
if hasEndBrace {
data[braceAt] = ' ' // Blank this one too
}
})
each("w.Write(", func(index int) {
braceAt, hasEndBrace := skipUntilIfExists(data, index, ')')
// TODO: Make sure we don't go onto the next line in case someone misplaced a brace
if hasEndBrace {
data[braceAt] = ' ' // Blank it
}
})
each("if ", func(index int) {
fmt.Println("if index: ", index)
braceAt, hasBrace := skipUntilIfExists(data, index, '{')
if hasBrace {
if data[braceAt-1] != ' ' {
panic("couldn't find space before brace, found ' " + string(data[braceAt-1]) + "' instead")
}
data[braceAt-1] = ')' // Drop a brace here to satisfy JS
}
})
data = replace(data, "w.Write([]byte(", "out += ")
data = replace(data, "w.Write(", "out += ")
data = replace(data, "strconv.Itoa(", "")
data = replace(data, "if ", "if(")
data = replace(data, "return nil", "return out")
data = replace(data, " )", ")")
data = replace(data, " \n", "\n")
data = replace(data, "\n", ";\n")
data = replace(data, "{;", "{")
data = replace(data, "};", "}")
data = replace(data, ";;", ";")
path = strings.TrimPrefix(path, "tmpl_gen/")
tmplName := strings.TrimSuffix(path, ".go")
fragset, ok := fragMap[strings.TrimPrefix(tmplName, "template_")]
if !ok {
fmt.Println("tmplName: ", tmplName)
return errors.New("couldn't find template in fragmap")
}
var sfrags = []byte("let alert_frags = [];\n")
for _, frags := range fragset {
sfrags = append(sfrags, []byte("alert_frags.push(`"+string(frags)+"`);\n")...)
}
data = append(sfrags, data...)
data = replace(data, "\n;", "\n")
path = tmplName + ".js"
DebugLog("js path: ", path)
var ext = filepath.Ext("/tmpl_gen/" + path)
gzipData := compressBytesGzip(data)
list.Set("/static/"+path, SFile{data, gzipData, 0, int64(len(data)), int64(len(gzipData)), mime.TypeByExtension(ext), f, f.ModTime().UTC().Format(http.TimeFormat)})
DebugLogf("Added the '%s' static file.", path)
return nil
})
}
func (list SFileList) Init() error {
return filepath.Walk("./public", func(path string, f os.FileInfo, err error) error {
if f.IsDir() {

View File

@ -168,6 +168,13 @@ func nextCharIs(tmplData []byte, i int, expects byte) bool {
return tmplData[i+1] == expects
}
func peekNextChar(tmplData []byte, i int) byte {
if len(tmplData) <= (i + 1) {
return 0
}
return tmplData[i+1]
}
func skipUntilIfExists(tmplData []byte, i int, expects byte) (newI int, hasIt bool) {
j := i
for ; j < len(tmplData); j++ {
@ -182,14 +189,47 @@ func skipUntilCharsExist(tmplData []byte, i int, expects []byte) (newI int, hasI
j := i
expectIndex := 0
for ; j < len(tmplData) && expectIndex < len(expects); j++ {
//fmt.Println("tmplData[j]: ", string(tmplData[j]))
if tmplData[j] != expects[expectIndex] {
return j, false
}
//fmt.Printf("found %+v at %d\n", string(expects[expectIndex]), expectIndex)
expectIndex++
}
return j, true
}
func skipAllUntilCharsExist(tmplData []byte, i int, expects []byte) (newI int, hasIt bool) {
j := i
expectIndex := 0
//fmt.Printf("tmplData: %+v\n", string(tmplData))
for ; j < len(tmplData) && expectIndex < len(expects); j++ {
//fmt.Println("tmplData[j]: ", string(tmplData[j]) + " ")
if tmplData[j] == expects[expectIndex] {
//fmt.Printf("expects[expectIndex]: %+v - %d\n", string(expects[expectIndex]), expectIndex)
expectIndex++
if len(expects) <= expectIndex {
//fmt.Println("breaking")
break
}
} else {
/*if expectIndex != 0 {
fmt.Println("broke expectations")
fmt.Println("expected: ", string(expects[expectIndex]))
fmt.Println("got: ", string(tmplData[j]))
fmt.Println("next: ", string(peekNextChar(tmplData, j)))
fmt.Println("next: ", string(peekNextChar(tmplData, j+1)))
fmt.Println("next: ", string(peekNextChar(tmplData, j+2)))
fmt.Println("next: ", string(peekNextChar(tmplData, j+3)))
}*/
expectIndex = 0
}
}
//fmt.Println("len(expects): ", len(expects))
//fmt.Println("expectIndex: ", expectIndex)
return j, len(expects) == expectIndex
}
type menuRenderItem struct {
Type int // 0: text, 1: variable
Index int

View File

@ -59,13 +59,6 @@ type ExtData struct {
sync.RWMutex
}
type AlertItem struct {
ASID int
Path string
Message string
Avatar string
}
type Page struct {
Title string
CurrentUser User

View File

@ -100,7 +100,6 @@ func InitPhrases() error {
return nil
})
if err != nil {
return err
}

View File

@ -20,8 +20,7 @@ type SQLProfileReplyStore struct {
create *sql.Stmt
}
func NewSQLProfileReplyStore() (*SQLProfileReplyStore, error) {
acc := qgen.Builder.Accumulator()
func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplyStore, error) {
return &SQLProfileReplyStore{
get: acc.Select("users_replies").Columns("uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress").Where("rid = ?").Prepare(),
create: acc.Insert("users_replies").Columns("uid, content, parsed_content, createdAt, createdBy, ipaddress").Fields("?,?,?,UTC_TIMESTAMP(),?,?").Prepare(),

View File

@ -15,8 +15,7 @@ type SQLReplyStore struct {
create *sql.Stmt
}
func NewSQLReplyStore() (*SQLReplyStore, error) {
acc := qgen.Builder.Accumulator()
func NewSQLReplyStore(acc *qgen.Accumulator) (*SQLReplyStore, error) {
return &SQLReplyStore{
get: acc.Select("replies").Columns("tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount").Where("rid = ?").Prepare(),
create: acc.Insert("replies").Columns("tid, content, parsed_content, createdAt, lastUpdated, ipaddress, words, createdBy").Fields("?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?").Prepare(),

View File

@ -52,7 +52,7 @@ type config struct {
SslPrivkey string
SslFullchain string
MaxRequestSize int
MaxRequestSize int64
CacheTopicUser int
UserCacheCapacity int
TopicCacheCapacity int

View File

@ -10,6 +10,7 @@ import (
"sync"
"time"
"./alerts"
"./templates"
)
@ -297,24 +298,25 @@ func CompileJSTemplates() error {
config.Minify = Config.MinifyTemplates
config.SuperDebug = Dev.TemplateDebug
config.SkipHandles = true
config.SkipInitBlock = true
config.PackageName = "tmpl"
c := tmpl.NewCTemplateSet()
c.SetConfig(config)
c.SetBaseImportMap(map[string]string{
"io": "io",
"../../common": "../../common",
"io": "io",
"../common/alerts": "../common/alerts",
})
var varList = make(map[string]tmpl.VarItem)
// TODO: Check what sort of path is sent exactly and use it here
alertItem := AlertItem{Avatar: "", ASID: 1, Path: "/", Message: "uh oh, something happened"}
alertTmpl, err := c.Compile("alert.html", "templates/", "common.AlertItem", alertItem, varList)
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
}
var dirPrefix = "./tmpl_gen/client/"
var dirPrefix = "./tmpl_gen/"
var wg sync.WaitGroup
var writeTemplate = func(name string, content string) {
log.Print("Writing template '" + name + "'")
@ -343,6 +345,7 @@ func writeTemplateList(c *tmpl.CTemplateSet, wg *sync.WaitGroup, prefix string)
out := "package " + c.GetConfig().PackageName + "\n\n"
for templateName, count := range c.TemplateFragmentCount {
out += "var " + templateName + "_frags = make([][]byte," + strconv.Itoa(count) + ")\n"
out += "\n// nolint\nfunc Get_" + templateName + "_frags() [][]byte {\nreturn " + templateName + "_frags\n}\n"
}
out += "\n// nolint\nfunc init() {\n" + c.FragOut + "}\n"
err := writeFile(prefix+"template_list.go", out)

View File

@ -107,7 +107,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
if c.config.Debug {
fmt.Println("Compiling template '" + name + "'")
}
c.importMap = map[string]string{}
for index, item := range c.baseImportMap {
c.importMap[index] = item
@ -135,7 +134,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
return "", err
}
}
content := string(res)
if c.config.Minify {
content = minify(content)
@ -149,7 +147,6 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
}
c.detail(name)
out = ""
fname := strings.TrimSuffix(name, filepath.Ext(name))
c.templateList = map[string]*parse.Tree{fname: tree}
varholder := "tmpl_" + fname + "_vars"
@ -212,7 +209,7 @@ func (c *CTemplateSet) Compile(name string, fileDir string, expects string, expe
if len(c.langIndexToName) > 0 {
fout += "var phrases = common.GetTmplPhrasesBytes(" + fname + "_tmpl_phrase_id)\n"
}
fout += varString + out + "\treturn nil\n}\n"
fout += varString + out + "return nil\n}\n"
fout = strings.Replace(fout, `))
w.Write([]byte(`, " + ", -1)
@ -278,7 +275,6 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value
c.detail("Selected Branch 1")
return out + "\n"
}
c.detail("Selected Branch 2")
return out + " else {\n" + c.compileSwitch(varholder, holdreflect, templateName, node.ElseList) + "}\n"
case *parse.ListNode:
@ -947,7 +943,6 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
log.Fatal(err)
}
}
content := string(res)
if c.config.Minify {
content = minify(content)

View File

@ -148,10 +148,11 @@ func ConvertByteInUnit(bytes float64, unit string) (count float64) {
}
// TODO: Write a test for this
// TODO: Re-add T as int64
func ConvertUnit(num int) (int, string) {
switch {
case num >= 1000000000000:
return num / 1000000000000, "T"
//case num >= 1000000000000:
// return num / 1000000000000, "T"
case num >= 1000000000:
return num / 1000000000, "B"
case num >= 1000000:
@ -164,12 +165,14 @@ func ConvertUnit(num int) (int, string) {
}
// TODO: Write a test for this
// TODO: Re-add quadrillion as int64
// TODO: Re-add trillion as int64
func ConvertFriendlyUnit(num int) (int, string) {
switch {
case num >= 1000000000000000:
return 0, " quadrillion"
case num >= 1000000000000:
return 0, " trillion"
//case num >= 1000000000000000:
// return 0, " quadrillion"
//case num >= 1000000000000:
// return 0, " trillion"
case num >= 1000000000:
return num / 1000000000, " billion"
case num >= 1000000:

View File

@ -9,7 +9,6 @@ import "./common"
// nolint
type Stmts struct {
getPassword *sql.Stmt
isPluginActive *sql.Stmt
getUsersOffset *sql.Stmt
isThemeDefault *sql.Stmt
@ -59,14 +58,6 @@ type Stmts struct {
func _gen_mssql() (err error) {
common.DebugLog("Building the generated statements")
common.DebugLog("Preparing getPassword statement.")
stmts.getPassword, err = db.Prepare("SELECT [password],[salt] FROM [users] WHERE [uid] = ?1")
if err != nil {
log.Print("Error in getPassword statement.")
log.Print("Bad Query: ","SELECT [password],[salt] FROM [users] WHERE [uid] = ?1")
return err
}
common.DebugLog("Preparing isPluginActive statement.")
stmts.isPluginActive, err = db.Prepare("SELECT [active] FROM [plugins] WHERE [uname] = ?1")
if err != nil {

View File

@ -11,7 +11,6 @@ import "./common"
// nolint
type Stmts struct {
getPassword *sql.Stmt
isPluginActive *sql.Stmt
getUsersOffset *sql.Stmt
isThemeDefault *sql.Stmt
@ -61,13 +60,6 @@ type Stmts struct {
func _gen_mysql() (err error) {
common.DebugLog("Building the generated statements")
common.DebugLog("Preparing getPassword statement.")
stmts.getPassword, err = db.Prepare("SELECT `password`,`salt` FROM `users` WHERE `uid` = ?")
if err != nil {
log.Print("Error in getPassword statement.")
return err
}
common.DebugLog("Preparing isPluginActive statement.")
stmts.isPluginActive, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?")
if err != nil {

View File

@ -89,11 +89,11 @@ var RouteMap = map[string]interface{}{
"routePanelDebug": routePanelDebug,
"routePanelDashboard": routePanelDashboard,
"routes.AccountEditCritical": routes.AccountEditCritical,
"routeAccountEditCriticalSubmit": routeAccountEditCriticalSubmit,
"routeAccountEditAvatar": routeAccountEditAvatar,
"routeAccountEditAvatarSubmit": routeAccountEditAvatarSubmit,
"routeAccountEditUsername": routeAccountEditUsername,
"routeAccountEditUsernameSubmit": routeAccountEditUsernameSubmit,
"routes.AccountEditCriticalSubmit": routes.AccountEditCriticalSubmit,
"routes.AccountEditAvatar": routes.AccountEditAvatar,
"routes.AccountEditAvatarSubmit": routes.AccountEditAvatarSubmit,
"routes.AccountEditUsername": routes.AccountEditUsername,
"routes.AccountEditUsernameSubmit": routes.AccountEditUsernameSubmit,
"routeAccountEditEmail": routeAccountEditEmail,
"routeAccountEditEmailTokenSubmit": routeAccountEditEmailTokenSubmit,
"routes.ViewProfile": routes.ViewProfile,
@ -205,11 +205,11 @@ var routeMapEnum = map[string]int{
"routePanelDebug": 67,
"routePanelDashboard": 68,
"routes.AccountEditCritical": 69,
"routeAccountEditCriticalSubmit": 70,
"routeAccountEditAvatar": 71,
"routeAccountEditAvatarSubmit": 72,
"routeAccountEditUsername": 73,
"routeAccountEditUsernameSubmit": 74,
"routes.AccountEditCriticalSubmit": 70,
"routes.AccountEditAvatar": 71,
"routes.AccountEditAvatarSubmit": 72,
"routes.AccountEditUsername": 73,
"routes.AccountEditUsernameSubmit": 74,
"routeAccountEditEmail": 75,
"routeAccountEditEmailTokenSubmit": 76,
"routes.ViewProfile": 77,
@ -319,11 +319,11 @@ var reverseRouteMapEnum = map[int]string{
67: "routePanelDebug",
68: "routePanelDashboard",
69: "routes.AccountEditCritical",
70: "routeAccountEditCriticalSubmit",
71: "routeAccountEditAvatar",
72: "routeAccountEditAvatarSubmit",
73: "routeAccountEditUsername",
74: "routeAccountEditUsernameSubmit",
70: "routes.AccountEditCriticalSubmit",
71: "routes.AccountEditAvatar",
72: "routes.AccountEditAvatarSubmit",
73: "routes.AccountEditUsername",
74: "routes.AccountEditUsernameSubmit",
75: "routeAccountEditEmail",
76: "routeAccountEditEmailTokenSubmit",
77: "routes.ViewProfile",
@ -529,7 +529,7 @@ func NewGenRouter(uploads http.Handler) (*GenRouter, error) {
writ := NewWriterIntercept(w)
http.StripPrefix("/uploads/",uploads).ServeHTTP(writ,req)
if writ.GetCode() == 200 {
w.Header().Set("Cache-Control", "max-age=" + strconv.Itoa(common.Day))
w.Header().Set("Cache-Control", "max-age=" + strconv.Itoa(int(common.Day)))
w.Header().Set("Vary", "Accept-Encoding")
}
},
@ -1337,7 +1337,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
counters.RouteViewCounter.Bump(70)
err = routeAccountEditCriticalSubmit(w,req,user)
err = routes.AccountEditCriticalSubmit(w,req,user)
case "/user/edit/avatar/":
err = common.MemberOnly(w,req,user)
if err != nil {
@ -1346,7 +1346,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
counters.RouteViewCounter.Bump(71)
err = routeAccountEditAvatar(w,req,user)
err = routes.AccountEditAvatar(w,req,user)
case "/user/edit/avatar/submit/":
err = common.MemberOnly(w,req,user)
if err != nil {
@ -1354,7 +1354,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
err = common.HandleUploadRoute(w,req,user,common.Config.MaxRequestSize)
err = common.HandleUploadRoute(w,req,user,int(common.Config.MaxRequestSize))
if err != nil {
router.handleError(err,w,req,user)
return
@ -1366,7 +1366,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
counters.RouteViewCounter.Bump(72)
err = routeAccountEditAvatarSubmit(w,req,user)
err = routes.AccountEditAvatarSubmit(w,req,user)
case "/user/edit/username/":
err = common.MemberOnly(w,req,user)
if err != nil {
@ -1375,7 +1375,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
counters.RouteViewCounter.Bump(73)
err = routeAccountEditUsername(w,req,user)
err = routes.AccountEditUsername(w,req,user)
case "/user/edit/username/submit/":
err = common.NoSessionMismatch(w,req,user)
if err != nil {
@ -1390,7 +1390,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
counters.RouteViewCounter.Bump(74)
err = routeAccountEditUsernameSubmit(w,req,user)
err = routes.AccountEditUsernameSubmit(w,req,user)
case "/user/edit/email/":
err = common.MemberOnly(w,req,user)
if err != nil {
@ -1492,7 +1492,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
err = common.HandleUploadRoute(w,req,user,common.Config.MaxRequestSize)
err = common.HandleUploadRoute(w,req,user,int(common.Config.MaxRequestSize))
if err != nil {
router.handleError(err,w,req,user)
return
@ -1649,7 +1649,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
err = common.HandleUploadRoute(w,req,user,common.Config.MaxRequestSize)
err = common.HandleUploadRoute(w,req,user,int(common.Config.MaxRequestSize))
if err != nil {
router.handleError(err,w,req,user)
return

10
main.go
View File

@ -22,6 +22,7 @@ import (
"./common"
"./common/counters"
"./config"
"./query_gen/lib"
"github.com/fsnotify/fsnotify"
)
@ -38,11 +39,12 @@ type Globs struct {
}
func afterDBInit() (err error) {
common.Rstore, err = common.NewSQLReplyStore()
acc := qgen.Builder.Accumulator()
common.Rstore, err = common.NewSQLReplyStore(acc)
if err != nil {
return err
}
common.Prstore, err = common.NewSQLProfileReplyStore()
common.Prstore, err = common.NewSQLProfileReplyStore(acc)
if err != nil {
return err
}
@ -65,6 +67,10 @@ func afterDBInit() (err error) {
if err != nil {
return err
}
err = common.StaticFiles.JSTmplInit()
if err != nil {
return err
}
log.Print("Initialising the widgets")
err = common.InitWidgets()

View File

@ -1,12 +1,8 @@
package main
import (
"html"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
@ -258,179 +254,6 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user common.User,
return nil
}
func routeAccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
_, ferr := common.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
var realPassword, salt string
currentPassword := r.PostFormValue("account-current-password")
newPassword := r.PostFormValue("account-new-password")
confirmPassword := r.PostFormValue("account-confirm-password")
err := stmts.getPassword.QueryRow(user.ID).Scan(&realPassword, &salt)
if err == ErrNoRows {
return common.LocalError("Your account no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
err = common.CheckPassword(realPassword, currentPassword, salt)
if err == common.ErrMismatchedHashAndPassword {
return common.LocalError("That's not the correct password.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
if newPassword != confirmPassword {
return common.LocalError("The two passwords don't match.", w, r, user)
}
common.SetPassword(user.ID, newPassword)
// Log the user out as a safety precaution
common.Auth.ForceLogout(user.ID)
http.Redirect(w, r, "/", http.StatusSeeOther)
return nil
}
func routeAccountEditAvatar(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
return nil
}
err := common.Templates.ExecuteTemplate(w, "account_own_edit_avatar.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routeAccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
var filename, ext string
for _, fheaders := range r.MultipartForm.File {
for _, hdr := range fheaders {
if hdr.Filename == "" {
continue
}
infile, err := hdr.Open()
if err != nil {
return common.LocalError("Upload failed", w, r, user)
}
defer infile.Close()
// We don't want multiple files
// TODO: Check the length of r.MultipartForm.File and error rather than doing this x.x
if filename != "" {
if filename != hdr.Filename {
os.Remove("./uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext)
return common.LocalError("You may only upload one avatar", w, r, user)
}
} else {
filename = hdr.Filename
}
if ext == "" {
extarr := strings.Split(hdr.Filename, ".")
if len(extarr) < 2 {
return common.LocalError("Bad file", w, r, user)
}
ext = extarr[len(extarr)-1]
// TODO: Can we do this without a regex?
reg, err := regexp.Compile("[^A-Za-z0-9]+")
if err != nil {
return common.LocalError("Bad file extension", w, r, user)
}
ext = reg.ReplaceAllString(ext, "")
ext = strings.ToLower(ext)
}
outfile, err := os.Create("./uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext)
if err != nil {
return common.LocalError("Upload failed [File Creation Failed]", w, r, user)
}
defer outfile.Close()
_, err = io.Copy(outfile, infile)
if err != nil {
return common.LocalError("Upload failed [Copy Failed]", w, r, user)
}
}
}
if ext == "" {
return common.LocalError("No file", w, r, user)
}
err := user.ChangeAvatar("." + ext)
if err != nil {
return common.InternalError(err, w, r)
}
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_avatar_updated"))
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
return nil
}
err = common.Templates.ExecuteTemplate(w, "account_own_edit_avatar.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routeAccountEditUsername(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
pi := common.Page{"Edit Username", user, headerVars, tList, user.Name}
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
return nil
}
err := common.Templates.ExecuteTemplate(w, "account_own_edit_username.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routeAccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
newUsername := html.EscapeString(strings.Replace(r.PostFormValue("account-new-username"), "\n", "", -1))
err := user.ChangeName(newUsername)
if err != nil {
return common.LocalError("Unable to change the username. Does someone else already have this name?", w, r, user)
}
user.Name = newUsername
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_username_updated"))
pi := common.Page{"Edit Username", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
return nil
}
err = common.Templates.ExecuteTemplate(w, "account_own_edit_username.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routeAccountEditEmail(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {

View File

@ -9,6 +9,7 @@ import (
"log"
"os"
"runtime/debug"
"strconv"
"../common"
"../config"
@ -85,10 +86,19 @@ func patcher(scanner *bufio.Scanner) error {
if err != nil {
return err
}
_ = schemaFile
dbVersion, err := strconv.Atoi(schemaFile.DBVersion)
if err != nil {
return err
}
fmt.Println("Applying the patches")
return patch0(scanner)
if dbVersion < 1 {
err := patch0(scanner)
if err != nil {
return err
}
}
return patch1(scanner)
}
func execStmt(stmt *sql.Stmt, err error) error {

View File

@ -120,5 +120,35 @@ func patch0(scanner *bufio.Scanner) (err error) {
}
func patch1(scanner *bufio.Scanner) error {
// ! Don't reuse this function blindly, it doesn't escape apostrophes
var replaceTextWhere = func(replaceThis string, withThis string) error {
return execStmt(qgen.Builder.SimpleUpdate("viewchunks", "route = '"+withThis+"'", "route = '"+replaceThis+"'"))
}
err := replaceTextWhere("routeAccountEditCriticalSubmit", "routes.AccountEditCriticalSubmit")
if err != nil {
return err
}
err = replaceTextWhere("routeAccountEditAvatar", "routes.AccountEditAvatar")
if err != nil {
return err
}
err = replaceTextWhere("routeAccountEditAvatarSubmit", "routes.AccountEditAvatarSubmit")
if err != nil {
return err
}
err = replaceTextWhere("routeAccountEditUsername", "routes.AccountEditUsername")
if err != nil {
return err
}
err = replaceTextWhere("routeAccountEditUsernameSubmit", "routes.AccountEditUsernameSubmit")
if err != nil {
return err
}
return nil
}

View File

@ -49,9 +49,12 @@ function bindToAlerts() {
});
}
var alertsInitted = false;
// TODO: Add the ability for users to dismiss alerts
function loadAlerts(menuAlerts)
{
if(!alertsInitted) return;
var alertListNode = menuAlerts.getElementsByClassName("alertList")[0];
var alertCounterNode = menuAlerts.getElementsByClassName("alert_counter")[0];
alertCounterNode.textContent = "0";
@ -59,7 +62,7 @@ function loadAlerts(menuAlerts)
type: 'get',
dataType: 'json',
url:'/api/?action=get&module=alerts',
success: function(data) {
success: (data) => {
if("errmsg" in data) {
alertListNode.innerHTML = "<div class='alertItem'>"+data.errmsg+"</div>";
return;
@ -76,14 +79,12 @@ function loadAlerts(menuAlerts)
//console.log("Sub #" + i + ":",msg.sub[i]);
}
}
if("avatar" in msg) {
alist += "<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><img src='"+msg.avatar+"' class='bgsub' /><a class='text' data-asid='"+msg.asid+"' href=\""+msg.path+"\">"+mmsg+"</a></div>";
alertList.push("<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><img src='"+msg.avatar+"' class='bgsub' /><a class='text' data-asid='"+msg.asid+"' href=\""+msg.path+"\">"+mmsg+"</a></div>");
} else {
alist += "<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>";
alertList.push("<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>");
}
alist += Template_alert({
ASID: msg.asid || 0,
Path: msg.path,
Avatar: msg.avatar || "",
Message: mmsg
})
//console.log(msg);
//console.log(mmsg);
}
@ -101,7 +102,7 @@ function loadAlerts(menuAlerts)
bindToAlerts();
},
error: function(magic,theStatus,error) {
error: (magic,theStatus,error) => {
let errtxt
try {
var data = JSON.parse(magic.responseText);
@ -218,6 +219,14 @@ function runWebSockets() {
$(document).ready(function(){
runHook("start_init");
$.getScript( "./static/template_alert.js", () => {
console.log("Loaded template_alert.js");
alertsInitted = true;
var alertMenuList = document.getElementsByClassName("menu_alerts");
for(var i = 0; i < alertMenuList.length; i++) {
loadAlerts(alertMenuList[i]);
}
});
if(window["WebSocket"]) runWebSockets();
else conn = false;
@ -445,11 +454,6 @@ $(document).ready(function(){
}
});
var alertMenuList = document.getElementsByClassName("menu_alerts");
for(var i = 0; i < alertMenuList.length; i++) {
loadAlerts(alertMenuList[i]);
}
$(".menu_alerts").click(function(event) {
event.stopPropagation();
if($(this).hasClass("selectedAlert")) return;

View File

@ -0,0 +1 @@
This file is here so that Git will include this folder in the repository.

View File

@ -132,6 +132,27 @@ func (selectItem *AccSelectBuilder) Query(args ...interface{}) (*sql.Rows, error
return nil, selectItem.build.FirstError()
}
type AccRowWrap struct {
row *sql.Row
err error
}
func (wrap *AccRowWrap) Scan(dest ...interface{}) error {
if wrap.err != nil {
return wrap.err
}
return wrap.row.Scan(dest...)
}
// TODO: Test to make sure the errors are passed up properly
func (selectItem *AccSelectBuilder) QueryRow(args ...interface{}) *AccRowWrap {
stmt := selectItem.Prepare()
if stmt != nil {
return &AccRowWrap{stmt.QueryRow(args...), nil}
}
return &AccRowWrap{nil, selectItem.build.FirstError()}
}
// Experimental, reduces lines
func (selectItem *AccSelectBuilder) Each(handle func(*sql.Rows) error) error {
rows, err := selectItem.Query()

View File

@ -257,8 +257,6 @@ func writeSelects(adapter qgen.Adapter) error {
// Looking for getTopic? Your statement is in another castle
build.Select("getPassword").Table("users").Columns("password, salt").Where("uid = ?").Parse()
build.Select("isPluginActive").Table("plugins").Columns("active").Where("uname = ?").Parse()
//build.Select("isPluginInstalled").Table("plugins").Columns("installed").Where("uname = ?").Parse()

View File

@ -348,7 +348,7 @@ func NewGenRouter(uploads http.Handler) (*GenRouter, error) {
writ := NewWriterIntercept(w)
http.StripPrefix("/uploads/",uploads).ServeHTTP(writ,req)
if writ.GetCode() == 200 {
w.Header().Set("Cache-Control", "max-age=" + strconv.Itoa(common.Day))
w.Header().Set("Cache-Control", "max-age=" + strconv.Itoa(int(common.Day)))
w.Header().Set("Vary", "Accept-Encoding")
}
},

View File

@ -41,11 +41,11 @@ func buildUserRoutes() {
userGroup.Routes(
View("routes.ViewProfile", "/user/").LitBefore("req.URL.Path += extraData"),
MemberView("routes.AccountEditCritical", "/user/edit/critical/"),
Action("routeAccountEditCriticalSubmit", "/user/edit/critical/submit/"), // TODO: Full test this
MemberView("routeAccountEditAvatar", "/user/edit/avatar/"),
UploadAction("routeAccountEditAvatarSubmit", "/user/edit/avatar/submit/").MaxSizeVar("common.Config.MaxRequestSize"),
MemberView("routeAccountEditUsername", "/user/edit/username/"),
Action("routeAccountEditUsernameSubmit", "/user/edit/username/submit/"), // TODO: Full test this
Action("routes.AccountEditCriticalSubmit", "/user/edit/critical/submit/"), // TODO: Full test this
MemberView("routes.AccountEditAvatar", "/user/edit/avatar/"),
UploadAction("routes.AccountEditAvatarSubmit", "/user/edit/avatar/submit/").MaxSizeVar("int(common.Config.MaxRequestSize)"),
MemberView("routes.AccountEditUsername", "/user/edit/username/"),
Action("routes.AccountEditUsernameSubmit", "/user/edit/username/submit/"), // TODO: Full test this
MemberView("routeAccountEditEmail", "/user/edit/email/"),
Action("routeAccountEditEmailTokenSubmit", "/user/edit/token/", "extraData"),
)
@ -66,7 +66,7 @@ func buildTopicRoutes() {
topicGroup := newRouteGroup("/topic/")
topicGroup.Routes(
View("routes.ViewTopic", "/topic/", "extraData"),
UploadAction("routes.CreateTopicSubmit", "/topic/create/submit/").MaxSizeVar("common.Config.MaxRequestSize"),
UploadAction("routes.CreateTopicSubmit", "/topic/create/submit/").MaxSizeVar("int(common.Config.MaxRequestSize)"),
Action("routes.EditTopicSubmit", "/topic/edit/submit/", "extraData"),
Action("routes.DeleteTopicSubmit", "/topic/delete/submit/").LitBefore("req.URL.Path += extraData"),
Action("routes.StickTopicSubmit", "/topic/stick/submit/", "extraData"),
@ -85,7 +85,7 @@ func buildReplyRoutes() {
replyGroup := newRouteGroup("/reply/")
replyGroup.Routes(
// TODO: Reduce this to 1MB for attachments for each file?
UploadAction("routes.CreateReplySubmit", "/reply/create/").MaxSizeVar("common.Config.MaxRequestSize"), // TODO: Rename the route so it's /reply/create/submit/
UploadAction("routes.CreateReplySubmit", "/reply/create/").MaxSizeVar("int(common.Config.MaxRequestSize)"), // TODO: Rename the route so it's /reply/create/submit/
Action("routes.ReplyEditSubmit", "/reply/edit/submit/", "extraData"),
Action("routes.ReplyDeleteSubmit", "/reply/delete/submit/", "extraData"),
Action("routeReplyLikeSubmit", "/reply/like/submit/", "extraData").Before("ParseForm"),

View File

@ -53,7 +53,7 @@ func routeChangeTheme(w http.ResponseWriter, r *http.Request, user common.User)
return common.LocalErrorJSQ("That theme doesn't exist", w, r, user, isJs)
}
cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: common.Year}
cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: int(common.Year)}
http.SetCookie(w, &cookie)
if !isJs {

View File

@ -1,9 +1,13 @@
package routes
import (
"database/sql"
"html"
"io"
"log"
"net/http"
"os"
"regexp"
"strconv"
"strings"
@ -184,3 +188,172 @@ func AccountEditCritical(w http.ResponseWriter, r *http.Request, user common.Use
}
return nil
}
func AccountEditCriticalSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
_, ferr := common.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
var realPassword, salt string
currentPassword := r.PostFormValue("account-current-password")
newPassword := r.PostFormValue("account-new-password")
confirmPassword := r.PostFormValue("account-confirm-password")
// TODO: Use a reusable statement
acc := qgen.Builder.Accumulator()
err := acc.Select("users").Columns("password, salt").Where("uid = ?").QueryRow(user.ID).Scan(&realPassword, &salt)
if err == sql.ErrNoRows {
return common.LocalError("Your account no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
err = common.CheckPassword(realPassword, currentPassword, salt)
if err == common.ErrMismatchedHashAndPassword {
return common.LocalError("That's not the correct password.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
if newPassword != confirmPassword {
return common.LocalError("The two passwords don't match.", w, r, user)
}
common.SetPassword(user.ID, newPassword)
// Log the user out as a safety precaution
common.Auth.ForceLogout(user.ID)
http.Redirect(w, r, "/", http.StatusSeeOther)
return nil
}
func AccountEditAvatar(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
return nil
}
err := common.Templates.ExecuteTemplate(w, "account_own_edit_avatar.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func AccountEditAvatarSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
var filename, ext string
for _, fheaders := range r.MultipartForm.File {
for _, hdr := range fheaders {
if hdr.Filename == "" {
continue
}
infile, err := hdr.Open()
if err != nil {
return common.LocalError("Upload failed", w, r, user)
}
defer infile.Close()
// We don't want multiple files
// TODO: Check the length of r.MultipartForm.File and error rather than doing this x.x
if filename != "" {
if filename != hdr.Filename {
os.Remove("./uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext)
return common.LocalError("You may only upload one avatar", w, r, user)
}
} else {
filename = hdr.Filename
}
if ext == "" {
extarr := strings.Split(hdr.Filename, ".")
if len(extarr) < 2 {
return common.LocalError("Bad file", w, r, user)
}
ext = extarr[len(extarr)-1]
// TODO: Can we do this without a regex?
reg, err := regexp.Compile("[^A-Za-z0-9]+")
if err != nil {
return common.LocalError("Bad file extension", w, r, user)
}
ext = reg.ReplaceAllString(ext, "")
ext = strings.ToLower(ext)
}
outfile, err := os.Create("./uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext)
if err != nil {
return common.LocalError("Upload failed [File Creation Failed]", w, r, user)
}
defer outfile.Close()
_, err = io.Copy(outfile, infile)
if err != nil {
return common.LocalError("Upload failed [Copy Failed]", w, r, user)
}
}
}
if ext == "" {
return common.LocalError("No file", w, r, user)
}
err := user.ChangeAvatar("." + ext)
if err != nil {
return common.InternalError(err, w, r)
}
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + "." + ext
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_avatar_updated"))
pi := common.Page{"Edit Avatar", user, headerVars, tList, nil}
if common.RunPreRenderHook("pre_render_account_own_edit_avatar", w, r, &user, &pi) {
return nil
}
err = common.Templates.ExecuteTemplate(w, "account_own_edit_avatar.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func AccountEditUsername(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
if r.FormValue("updated") == "1" {
headerVars.NoticeList = append(headerVars.NoticeList, common.GetNoticePhrase("account_username_updated"))
}
pi := common.Page{"Edit Username", user, headerVars, tList, user.Name}
if common.RunPreRenderHook("pre_render_account_own_edit_username", w, r, &user, &pi) {
return nil
}
err := common.Templates.ExecuteTemplate(w, "account_own_edit_username.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func AccountEditUsernameSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
_, ferr := common.SimpleUserCheck(w, r, &user)
if ferr != nil {
return ferr
}
newUsername := html.EscapeString(strings.Replace(r.PostFormValue("account-new-username"), "\n", "", -1))
err := user.ChangeName(newUsername)
if err != nil {
return common.LocalError("Unable to change the username. Does someone else already have this name?", w, r, user)
}
http.Redirect(w, r, "/user/edit/username/?updated=1", http.StatusSeeOther)
return nil
}

View File

@ -11,7 +11,7 @@ import (
"../common"
)
var cacheControlMaxAge = "max-age=" + strconv.Itoa(common.Day) // TODO: Make this a common.Config value
var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(common.Day)) // TODO: Make this a common.Config value
// GET functions
func StaticFile(w http.ResponseWriter, r *http.Request) {

View File

@ -60,9 +60,9 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user common.User, sui
duration, _ = time.ParseDuration("0")
} else {
var seconds int
seconds += durationDays * common.Day
seconds += durationWeeks * common.Week
seconds += durationMonths * common.Month
seconds += durationDays * int(common.Day)
seconds += durationWeeks * int(common.Week)
seconds += durationMonths * int(common.Month)
duration, _ = time.ParseDuration(strconv.Itoa(seconds) + "s")
}

View File

@ -1,5 +1,5 @@
{
"DBVersion":"1",
"DBVersion":"2",
"DynamicFileVersion":"0",
"MinGoVersion":"1.10",
"MinVersion":""

View File

@ -3,8 +3,8 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "./common"
import "io"
import "./common"
var error_tmpl_phrase_id int
@ -79,12 +79,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_error_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_error_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(error_frags[0])
w.Write(phrases[1])
w.Write(error_frags[1])
@ -117,5 +117,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_error_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "./common"
import "io"
import "strconv"
import "io"
import "./common"
var forum_tmpl_phrase_id int
@ -112,12 +112,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_forum_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_forum_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
if tmpl_forum_vars.Page > 1 {
w.Write(forum_frags[0])
w.Write(phrases[1])
@ -366,5 +366,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_forum_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -82,12 +82,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_forums_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_forums_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(forums_frags[0])
w.Write(phrases[1])
w.Write(forums_frags[1])
@ -170,5 +170,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_forums_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -77,12 +77,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_guilds_guild_list_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_guilds_guild_list_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(guilds_guild_list_frags[0])
if len(tmpl_guilds_guild_list_vars.GuildList) != 0 {
for _, item := range tmpl_guilds_guild_list_vars.GuildList {
@ -129,5 +129,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_guilds_guild_list_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -3,8 +3,8 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "./common"
import "io"
import "./common"
var ip_search_tmpl_phrase_id int
@ -81,12 +81,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_ip_search_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_ip_search_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(ip_search_frags[0])
w.Write(phrases[1])
w.Write(ip_search_frags[1])
@ -150,5 +150,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_ip_search_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -1,21 +1,102 @@
package main
var guilds_guild_list_frags = make([][]byte,10)
var forums_frags = make([][]byte,26)
var topics_frags = make([][]byte,98)
var register_frags = make([][]byte,9)
var header_frags = make([][]byte,28)
var topic_frags = make([][]byte,199)
var topic_alt_frags = make([][]byte,200)
var login_frags = make([][]byte,8)
var error_frags = make([][]byte,4)
var footer_frags = make([][]byte,13)
// nolint
func Get_error_frags() [][]byte {
return error_frags
}
var profile_comments_row_frags = make([][]byte,51)
var paginator_frags = make([][]byte,16)
// nolint
func Get_profile_comments_row_frags() [][]byte {
return profile_comments_row_frags
}
var topic_alt_frags = make([][]byte,200)
// nolint
func Get_topic_alt_frags() [][]byte {
return topic_alt_frags
}
var profile_frags = make([][]byte,50)
var forum_frags = make([][]byte,90)
// nolint
func Get_profile_frags() [][]byte {
return profile_frags
}
var ip_search_frags = make([][]byte,18)
// nolint
func Get_ip_search_frags() [][]byte {
return ip_search_frags
}
var header_frags = make([][]byte,26)
// nolint
func Get_header_frags() [][]byte {
return header_frags
}
var forums_frags = make([][]byte,26)
// nolint
func Get_forums_frags() [][]byte {
return forums_frags
}
var login_frags = make([][]byte,8)
// nolint
func Get_login_frags() [][]byte {
return login_frags
}
var register_frags = make([][]byte,9)
// nolint
func Get_register_frags() [][]byte {
return register_frags
}
var footer_frags = make([][]byte,13)
// nolint
func Get_footer_frags() [][]byte {
return footer_frags
}
var topic_frags = make([][]byte,199)
// nolint
func Get_topic_frags() [][]byte {
return topic_frags
}
var paginator_frags = make([][]byte,16)
// nolint
func Get_paginator_frags() [][]byte {
return paginator_frags
}
var topics_frags = make([][]byte,98)
// nolint
func Get_topics_frags() [][]byte {
return topics_frags
}
var forum_frags = make([][]byte,90)
// nolint
func Get_forum_frags() [][]byte {
return forum_frags
}
var guilds_guild_list_frags = make([][]byte,10)
// nolint
func Get_guilds_guild_list_frags() [][]byte {
return guilds_guild_list_frags
}
var notice_frags = make([][]byte,3)
// nolint
func Get_notice_frags() [][]byte {
return notice_frags
}
// nolint
func init() {
header_frags[0] = []byte(`<!doctype html>
@ -81,10 +162,9 @@ header_frags[21] = []byte(`</div>
header_frags[22] = []byte(`class="shrink_main"`)
header_frags[23] = []byte(`>
<div class="alertbox">`)
notice_frags[0] = []byte(`<div class="alert">`)
notice_frags[1] = []byte(`</div>`)
header_frags[24] = []byte(`
<div class="alert">`)
header_frags[25] = []byte(`</div>`)
header_frags[26] = []byte(`
</div>
`)
topic_frags[0] = []byte(`

View File

@ -84,12 +84,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_login_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_login_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(login_frags[0])
w.Write(phrases[1])
w.Write(login_frags[1])
@ -130,5 +130,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_login_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "strconv"
import "io"
import "./common"
import "strconv"
var profile_tmpl_phrase_id int
@ -107,12 +107,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_profile_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_profile_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(profile_frags[0])
w.Write([]byte(tmpl_profile_vars.ProfileOwner.Avatar))
w.Write(profile_frags[1])
@ -343,5 +343,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_profile_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -85,12 +85,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_register_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_register_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(register_frags[0])
w.Write(phrases[1])
w.Write(register_frags[1])
@ -133,5 +133,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_register_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "./common"
import "io"
import "strconv"
import "io"
import "./common"
var topic_tmpl_phrase_id int
@ -138,12 +138,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_topic_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_topic_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(topic_frags[0])
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.ID)))
w.Write(topic_frags[1])
@ -586,5 +586,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_topic_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -3,9 +3,9 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "./common"
import "strconv"
import "io"
import "./common"
var topic_alt_tmpl_phrase_id int
@ -125,12 +125,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_topic_alt_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_topic_alt_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
if tmpl_topic_alt_vars.Page > 1 {
w.Write(topic_alt_frags[0])
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
@ -579,5 +579,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_topic_alt_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -3,8 +3,8 @@
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main
import "./common"
import "io"
import "./common"
import "strconv"
var topics_tmpl_phrase_id int
@ -112,12 +112,12 @@ w.Write(header_frags[22])
w.Write(header_frags[23])
if len(tmpl_topics_vars.Header.NoticeList) != 0 {
for _, item := range tmpl_topics_vars.Header.NoticeList {
w.Write(header_frags[24])
w.Write(notice_frags[0])
w.Write([]byte(item))
w.Write(header_frags[25])
w.Write(notice_frags[1])
}
}
w.Write(header_frags[26])
w.Write(header_frags[24])
w.Write(topics_frags[0])
if tmpl_topics_vars.CurrentUser.ID != 0 {
w.Write(topics_frags[1])
@ -384,5 +384,5 @@ w.Write(footer_frags[9])
w.Write(footer_frags[10])
w.Write([]byte(common.BuildWidget("rightSidebar",tmpl_topics_vars.Header)))
w.Write(footer_frags[11])
return nil
return nil
}

View File

@ -1,2 +1,2 @@
{{if .Avatar}}<div class='alertItem withAvatar' style='background-image:url("{{.Avatar}}");'><a class='text' data-asid='{{.ASID}}' href="{{.Path}}">{{.Message}}</a></div>{{else}}
{{if .Avatar}}<div class='alertItem withAvatar' style='background-image:url("{{.Avatar}}");'><img src='{{.Avatar}}' class='bgsub' /><a class='text' data-asid='{{.ASID}}' href="{{.Path}}">{{.Message}}</a></div>{{else}}
<div class='alertItem'><a href="{{.Path}}" class='text'>{{.Message}}</a></div>{{end}}

View File

@ -38,5 +38,5 @@
<div class="right_of_nav">{{dock "rightOfNav" .Header }}</div>
<div id="back"><div id="main" {{if .Header.Widgets.RightSidebar}}class="shrink_main"{{end}}>
<div class="alertbox">{{range .Header.NoticeList}}
<div class="alert">{{.}}</div>{{end}}
{{template "notice.html" . }}{{end}}
</div>

1
templates/notice.html Normal file
View File

@ -0,0 +1 @@
<div class="alert">{{.}}</div>

View File

@ -1,35 +0,0 @@
// +build !no_templategen
// Code generated by Gosora. More below:
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package tmpl
import "../../common"
import "io"
import "strconv"
// nolint
func init() {
common.TmplPtrMap["o_alert"] = Template_alert
}
// nolint
func Template_alert(tmpl_alert_vars common.AlertItem, w io.Writer) error {
if tmpl_alert_vars.Avatar != "" {
w.Write(alert_frags[0])
w.Write([]byte(tmpl_alert_vars.Avatar))
w.Write(alert_frags[1])
w.Write([]byte(strconv.Itoa(tmpl_alert_vars.ASID)))
w.Write(alert_frags[2])
w.Write([]byte(tmpl_alert_vars.Path))
w.Write(alert_frags[3])
w.Write([]byte(tmpl_alert_vars.Message))
w.Write(alert_frags[4])
} else {
w.Write(alert_frags[5])
w.Write([]byte(tmpl_alert_vars.Path))
w.Write(alert_frags[6])
w.Write([]byte(tmpl_alert_vars.Message))
w.Write(alert_frags[7])
}
return nil
}

View File

@ -1,16 +0,0 @@
package tmpl
var alert_frags = make([][]byte,9)
// nolint
func init() {
alert_frags[0] = []byte(`<div class='alertItem withAvatar' style='background-image:url("`)
alert_frags[1] = []byte(`");'><a class='text' data-asid='`)
alert_frags[2] = []byte(`' href="`)
alert_frags[3] = []byte(`">`)
alert_frags[4] = []byte(`</a></div>`)
alert_frags[5] = []byte(`
<div class='alertItem'><a href="`)
alert_frags[6] = []byte(`" class='text'>`)
alert_frags[7] = []byte(`</a></div>`)
}