diff --git a/.gitignore b/.gitignore index e0915c2c..d58b0897 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ out/* *.log .DS_Store .vscode/launch.json +schema/lastSchema.json config/config.go Gosora Install diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a28c4ece..0f59dd8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,8 @@ -We're not accepting contributions right now, although you're welcome to poke me about things. I'd like to put a process together at some point. +If you want to add a contribution, you'll have to open a pull request and to sign the CLA (contributor level agreement). + +It's mainly there to deal with any legal issues which come our way and to switch licenses without having to chase down contributors who have long stopped using the internet or are deceased or incapacitated. + +Other uses may arise in the future, e.g. commercial licensing, although that's currently uncertain. # Coding Standards diff --git a/README.md b/README.md index 3c9d9eec..7a2a52d5 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Other modern features like alerts, likes, advanced dashboard with live stats (CP # Dependencies -Go 1.9 - You will need to install this. Pick the .msi, if you want everything sorted out for you rather than having to go around updating the environment settings. https://golang.org/doc/install +Go 1.10 - You will need to install this. Pick the .msi, if you want everything sorted out for you rather than having to go around updating the environment settings. https://golang.org/doc/install MySQL Database - You will need to setup a MySQL Database somewhere. A MariaDB Database works equally well and is much faster than MySQL. You could use something like WNMP / XAMPP which have a little PHP script called PhpMyAdmin for managing MySQL databases or you could install MariaDB directly. @@ -48,6 +48,8 @@ It's entirely possible that your host might already have MySQL, so you might be At some point, we'll have releases which you can download, but right now, you'll have to use the `git clone` command as mentioned down in the advanced setup section to download a copy of Gosora. +On Windows, you might also want to try the [GosoraBootstrapper](https://github.com/Azareal/GosoraBootstrapper), if you can't find the command prompt. It's just a matter of double-clicking on the bat file there and it'll download the rest of the files for you. + # Installation Instructions @@ -61,7 +63,7 @@ Follow the instructions shown on the screen. *Windows* -Run install.bat +Run install.bat, e.g. double-click on it. You will also have to start-up MySQL, which if you're using Wnmp or friends is just a matter of opening that program and starting the MySQL process via it. Follow the instructions shown on the screen. @@ -74,7 +76,7 @@ In the same directory you installed it, you simply have to type: ./run-linux *Windows* -Run run.bat +Run run.bat, e.g. double-clicking on it. *Updating Dependencies* @@ -97,6 +99,8 @@ Linux is similar, however you might need to use cd and mv a bit more like in the You also need to substitute the `gosora.exe` bits for `./Gosora` on Linux. For more info, you might want to take a gander inside the `./run-linux` and `./install-linux` shell files to see how they're implemented. +If you want to skip typing all the `go get`s, you can run `./update-deps.bat` (Windows) or `./update-deps-linux` to do that for you. + ```bash git clone https://github.com/Azareal/Gosora @@ -153,6 +157,17 @@ The update system is currently under development, however if you have Git instal In addition to this, you can update the dependencies without updating Gosora by running `update-deps.bat` or `./update-deps-linux` (.bat is for Windows, the other for Linux as the names would suggest). +If you want to manually patch Gosora rather than relying on the above scripts to do it, you'll first have to create a copy of `./schema/schema.json` named `./schema/lastSchema.json`, and then, you'll overwrite the files with the new ones. + +After that, you'll need to run `go build ./patcher` on Windows or the following code block on Linux: +``` +cd ./patcher +go build -o Patcher +mv ./Patcher .. +``` + +Once you've done that, you just need to run `patcher.exe` (Windows) or `./Patcher` to apply the latest patches to the database, etc. + # How do I install plugins? diff --git a/common/menu_item_store.go b/common/menu_item_store.go new file mode 100644 index 00000000..a792d432 --- /dev/null +++ b/common/menu_item_store.go @@ -0,0 +1,30 @@ +package common + +import "sync" + +type DefaultMenuItemStore struct { + items map[int]MenuItem + itemLock sync.RWMutex +} + +func NewDefaultMenuItemStore() *DefaultMenuItemStore { + return &DefaultMenuItemStore{ + items: make(map[int]MenuItem), + } +} + +func (store *DefaultMenuItemStore) Add(item MenuItem) { + store.itemLock.Lock() + defer store.itemLock.Unlock() + store.items[item.ID] = item +} + +func (store *DefaultMenuItemStore) Get(id int) (MenuItem, error) { + store.itemLock.RLock() + item, ok := store.items[id] + store.itemLock.RUnlock() + if ok { + return item, nil + } + return item, ErrNoRows +} diff --git a/common/menu_store.go b/common/menu_store.go new file mode 100644 index 00000000..c36a6c36 --- /dev/null +++ b/common/menu_store.go @@ -0,0 +1,76 @@ +package common + +import ( + "database/sql" + "strconv" + "sync/atomic" + + "../query_gen/lib" +) + +var Menus *DefaultMenuStore + +type DefaultMenuStore struct { + menus map[int]*atomic.Value + itemStore *DefaultMenuItemStore +} + +func NewDefaultMenuStore() *DefaultMenuStore { + return &DefaultMenuStore{ + make(map[int]*atomic.Value), + NewDefaultMenuItemStore(), + } +} + +// TODO: Add actual support for multiple menus +func (store *DefaultMenuStore) GetAllMap() (out map[int]*MenuListHolder) { + out = make(map[int]*MenuListHolder) + for mid, atom := range store.menus { + out[mid] = atom.Load().(*MenuListHolder) + } + return out +} + +func (store *DefaultMenuStore) Get(mid int) (*MenuListHolder, error) { + aStore, ok := store.menus[mid] + if ok { + return aStore.Load().(*MenuListHolder), nil + } + return nil, ErrNoRows +} + +func (store *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) { + acc := qgen.Builder.Accumulator() + err = acc.Select("menu_items").Columns("miid, name, htmlID, cssClass, position, path, aria, tooltip, order, tmplName, guestOnly, memberOnly, staffOnly, adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error { + var mitem = MenuItem{MenuID: mid} + err := rows.Scan(&mitem.ID, &mitem.Name, &mitem.HTMLID, &mitem.CSSClass, &mitem.Position, &mitem.Path, &mitem.Aria, &mitem.Tooltip, &mitem.Order, &mitem.TmplName, &mitem.GuestOnly, &mitem.MemberOnly, &mitem.SuperModOnly, &mitem.AdminOnly) + if err != nil { + return err + } + store.itemStore.Add(mitem) + mlist = append(mlist, mitem) + return nil + }) + return mlist, err +} + +func (store *DefaultMenuStore) Load(mid int) error { + mlist, err := store.Items(mid) + if err != nil { + return err + } + hold := &MenuListHolder{mlist, make(map[int]menuTmpl)} + err = hold.Preparse() + if err != nil { + return err + } + + var aStore = &atomic.Value{} + aStore.Store(hold) + store.menus[mid] = aStore + return nil +} + +func (store *DefaultMenuStore) ItemStore() *DefaultMenuItemStore { + return store.itemStore +} diff --git a/common/menus.go b/common/menus.go index 032182f9..db1f0d80 100644 --- a/common/menus.go +++ b/common/menus.go @@ -7,31 +7,12 @@ import ( "io" "io/ioutil" "strconv" - "sync/atomic" "../query_gen/lib" ) -var Menus *DefaultMenuStore - type MenuItemList []MenuItem -type DefaultMenuStore struct { - menus map[int]*atomic.Value -} - -func NewDefaultMenuStore() *DefaultMenuStore { - return &DefaultMenuStore{make(map[int]*atomic.Value)} -} - -func (store *DefaultMenuStore) Get(mid int) *MenuListHolder { - aStore, ok := store.menus[mid] - if ok { - return aStore.Load().(*MenuListHolder) - } - return nil -} - type MenuListHolder struct { List MenuItemList Variations map[int]menuTmpl // 0 = Guest Menu, 1 = Member Menu, 2 = Super Mod Menu, 3 = Admin Menu @@ -43,7 +24,10 @@ type menuTmpl struct { } type MenuItem struct { - ID int + ID int + MenuID int + + Name string HTMLID string CSSClass string Position string @@ -59,50 +43,56 @@ type MenuItem struct { AdminOnly bool } -func (store *DefaultMenuStore) Load(mid int) error { - var mlist MenuItemList - acc := qgen.Builder.Accumulator() - err := acc.Select("menu_items").Columns("htmlID, cssClass, position, path, aria, tooltip, order, tmplName, guestOnly, memberOnly, staffOnly, adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error { - var mitem = MenuItem{ID: 1} - err := rows.Scan(&mitem.HTMLID, &mitem.CSSClass, &mitem.Position, &mitem.Path, &mitem.Aria, &mitem.Tooltip, &mitem.Order, &mitem.TmplName, &mitem.GuestOnly, &mitem.MemberOnly, &mitem.SuperModOnly, &mitem.AdminOnly) +type MenuItemStmts struct { + update *sql.Stmt +} + +var menuItemStmts MenuItemStmts + +func init() { + DbInits.Add(func(acc *qgen.Accumulator) error { + menuItemStmts = MenuItemStmts{ + update: acc.Update("menu_items").Set("name = ?, htmlID = ?, cssClass = ?, position = ?, path = ?, aria = ?, tooltip = ?, tmplName = ?, guestOnly = ?, memberOnly = ?, staffOnly = ?, adminOnly = ?").Where("miid = ?").Prepare(), + } + return acc.FirstError() + }) +} + +func (item MenuItem) Commit() error { + _, err := menuItemStmts.update.Exec(item.Name, item.HTMLID, item.CSSClass, item.Position, item.Path, item.Aria, item.Tooltip, item.TmplName, item.GuestOnly, item.MemberOnly, item.SuperModOnly, item.AdminOnly, item.ID) + Menus.Load(item.MenuID) + return err +} + +func (hold *MenuListHolder) LoadTmpl(name string) (menuTmpl MenuTmpl, err error) { + data, err := ioutil.ReadFile("./templates/" + name + ".html") + if err != nil { + return menuTmpl, err + } + return hold.Parse(name, data), nil +} + +func (hold *MenuListHolder) LoadTmpls() (tmpls map[string]MenuTmpl, err error) { + tmpls = make(map[string]MenuTmpl) + var loadTmpl = func(name string) error { + menuTmpl, err := hold.LoadTmpl(name) if err != nil { return err } - mlist = append(mlist, mitem) + tmpls[name] = menuTmpl return nil - }) - if err != nil { - return err } - - hold := &MenuListHolder{mlist, make(map[int]menuTmpl)} - err = hold.Preparse() + err = loadTmpl("menu_item") if err != nil { - return err + return tmpls, err } - - var aStore = &atomic.Value{} - aStore.Store(hold) - store.menus[mid] = aStore - return nil + err = loadTmpl("menu_alerts") + return tmpls, err } // TODO: Run this in main, sync ticks, when the phrase file changes (need to implement the sync for that first), and when the settings are changed func (hold *MenuListHolder) Preparse() error { - var tmpls = make(map[string]MenuTmpl) - var loadTmpl = func(name string) error { - data, err := ioutil.ReadFile("./templates/" + name + ".html") - if err != nil { - return err - } - tmpls[name] = hold.Parse(name, data) - return nil - } - err := loadTmpl("menu_item") - if err != nil { - return err - } - err = loadTmpl("menu_alerts") + tmpls, err := hold.LoadTmpls() if err != nil { return err } @@ -194,6 +184,7 @@ func (hold *MenuListHolder) Parse(name string, tmplData []byte) (menuTmpl MenuTm // ? We only support simple properties on MenuItem right now var addVariable = func(name []byte) { //fmt.Println("appending subBuffer: ", string(subBuffer)) + // TODO: Check if the subBuffer has any items or is empty textBuffer = append(textBuffer, subBuffer) subBuffer = nil @@ -203,13 +194,17 @@ func (hold *MenuListHolder) Parse(name string, tmplData []byte) (menuTmpl MenuTm renderList = append(renderList, menuRenderItem{1, len(variableBuffer) - 1}) } + tmplData = bytes.Replace(tmplData, []byte("{{"), []byte("{"), -1) + tmplData = bytes.Replace(tmplData, []byte("}}"), []byte("}}"), -1) for i := 0; i < len(tmplData); i++ { char := tmplData[i] - if char == '{' && nextCharIs(tmplData, i, '{') { + if char == '{' { + //fmt.Println("found open fence") dotIndex, hasDot := skipUntilIfExists(tmplData, i, '.') if !hasDot { + //fmt.Println("no dot, assumed template function style") // Template function style - langIndex, hasChars := skipUntilCharsExist(tmplData, i+2, []byte("lang")) + langIndex, hasChars := skipUntilCharsExist(tmplData, i+1, []byte("lang")) if hasChars { startIndex, hasStart := skipUntilIfExists(tmplData, langIndex, '"') endIndex, hasEnd := skipUntilIfExists(tmplData, startIndex+1, '"') @@ -228,7 +223,8 @@ func (hold *MenuListHolder) Parse(name string, tmplData []byte) (menuTmpl MenuTm break } fenceIndex, hasFence := skipUntilIfExists(tmplData, dotIndex, '}') - if !hasFence || !nextCharIs(tmplData, fenceIndex, '}') { + if !hasFence { + //fmt.Println("no end fence") break } addVariable(tmplData[dotIndex:fenceIndex]) @@ -258,78 +254,88 @@ func (hold *MenuListHolder) Scan(menuTmpls map[string]MenuTmpl, showItem func(mi if !showItem(mitem) { continue } + renderBuffer, variableIndices = hold.ScanItem(menuTmpls, mitem, renderBuffer, variableIndices) + } + // TODO: Need more coalescing in the renderBuffer + return renderBuffer, variableIndices +} - menuTmpl, ok := menuTmpls[mitem.TmplName] - if !ok { - menuTmpl = menuTmpls["menu_item"] +// Note: This doesn't do a visibility check like hold.Scan() does +func (hold *MenuListHolder) ScanItem(menuTmpls map[string]MenuTmpl, mitem MenuItem, renderBuffer [][]byte, variableIndices []int) ([][]byte, []int) { + menuTmpl, ok := menuTmpls[mitem.TmplName] + if !ok { + menuTmpl = menuTmpls["menu_item"] + } + + //fmt.Println("menuTmpl: ", menuTmpl) + for _, renderItem := range menuTmpl.RenderList { + if renderItem.Type == 0 { + renderBuffer = append(renderBuffer, menuTmpl.TextBuffer[renderItem.Index]) + continue } - //fmt.Println("menuTmpl: ", menuTmpl) - for _, renderItem := range menuTmpl.RenderList { - if renderItem.Type == 0 { - renderBuffer = append(renderBuffer, menuTmpl.TextBuffer[renderItem.Index]) - continue + + variable := menuTmpl.VariableBuffer[renderItem.Index] + //fmt.Println("initial variable: ", string(variable)) + dotAt, hasDot := skipUntilIfExists(variable, 0, '.') + if !hasDot { + //fmt.Println("no dot") + continue + } + + if bytes.Equal(variable[:dotAt], []byte("lang")) { + //fmt.Println("lang: ", string(bytes.TrimPrefix(variable[dotAt:], []byte(".")))) + renderBuffer = append(renderBuffer, []byte(GetTmplPhrase(string(bytes.TrimPrefix(variable[dotAt:], []byte(".")))))) + } else { + var renderItem []byte + switch string(variable) { + case ".ID": + renderItem = []byte(strconv.Itoa(mitem.ID)) + case ".Name": + renderItem = []byte(mitem.Name) + case ".HTMLID": + renderItem = []byte(mitem.HTMLID) + case ".CSSClass": + renderItem = []byte(mitem.CSSClass) + case ".Position": + renderItem = []byte(mitem.Position) + case ".Path": + renderItem = []byte(mitem.Path) + case ".Aria": + renderItem = []byte(mitem.Aria) + case ".Tooltip": + renderItem = []byte(mitem.Tooltip) } - variable := menuTmpl.VariableBuffer[renderItem.Index] - //fmt.Println("initial variable: ", string(variable)) - dotAt, hasDot := skipUntilIfExists(variable, 0, '.') - if !hasDot { - //fmt.Println("no dot") - continue - } - - if bytes.Equal(variable[:dotAt], []byte("lang")) { - //fmt.Println("lang: ", string(bytes.TrimPrefix(variable[dotAt:], []byte(".")))) - renderBuffer = append(renderBuffer, []byte(GetTmplPhrase(string(bytes.TrimPrefix(variable[dotAt:], []byte(".")))))) - } else { - var renderItem []byte - switch string(variable) { - case ".HTMLID": - renderItem = []byte(mitem.HTMLID) - case ".CSSClass": - renderItem = []byte(mitem.CSSClass) - case ".Position": - renderItem = []byte(mitem.Position) - case ".Path": - renderItem = []byte(mitem.Path) - case ".Aria": - renderItem = []byte(mitem.Aria) - case ".Tooltip": - renderItem = []byte(mitem.Tooltip) - } - - _, hasInnerVar := skipUntilIfExists(renderItem, 0, '{') - if hasInnerVar { - //fmt.Println("inner var: ", string(renderItem)) - dotAt, hasDot := skipUntilIfExists(renderItem, 0, '.') - endFence, hasEndFence := skipUntilIfExists(renderItem, dotAt, '}') - if !hasDot || !hasEndFence || (endFence-dotAt) <= 1 { - renderBuffer = append(renderBuffer, renderItem) - variableIndices = append(variableIndices, len(renderBuffer)-1) - continue - } - - if bytes.Equal(renderItem[1:dotAt], []byte("lang")) { - //fmt.Println("lang var: ", string(renderItem[dotAt+1:endFence])) - renderBuffer = append(renderBuffer, []byte(GetTmplPhrase(string(renderItem[dotAt+1:endFence])))) - } else { - //fmt.Println("other var: ", string(variable[:dotAt])) - if len(renderItem) > 0 { - renderBuffer = append(renderBuffer, renderItem) - variableIndices = append(variableIndices, len(renderBuffer)-1) - } - } + _, hasInnerVar := skipUntilIfExists(renderItem, 0, '{') + if hasInnerVar { + //fmt.Println("inner var: ", string(renderItem)) + dotAt, hasDot := skipUntilIfExists(renderItem, 0, '.') + endFence, hasEndFence := skipUntilIfExists(renderItem, dotAt, '}') + if !hasDot || !hasEndFence || (endFence-dotAt) <= 1 { + renderBuffer = append(renderBuffer, renderItem) + variableIndices = append(variableIndices, len(renderBuffer)-1) continue } - //fmt.Println("normal var: ", string(variable[:dotAt])) - if len(renderItem) > 0 { - renderBuffer = append(renderBuffer, renderItem) + if bytes.Equal(renderItem[1:dotAt], []byte("lang")) { + //fmt.Println("lang var: ", string(renderItem[dotAt+1:endFence])) + renderBuffer = append(renderBuffer, []byte(GetTmplPhrase(string(renderItem[dotAt+1:endFence])))) + } else { + //fmt.Println("other var: ", string(variable[:dotAt])) + if len(renderItem) > 0 { + renderBuffer = append(renderBuffer, renderItem) + variableIndices = append(variableIndices, len(renderBuffer)-1) + } } + continue + } + + //fmt.Println("normal var: ", string(variable[:dotAt])) + if len(renderItem) > 0 { + renderBuffer = append(renderBuffer, renderItem) } } } - // TODO: Need more coalescing in the renderBuffer return renderBuffer, variableIndices } diff --git a/common/pages.go b/common/pages.go index 55cedb00..9c920ada 100644 --- a/common/pages.go +++ b/common/pages.go @@ -170,12 +170,10 @@ type GridElement struct { } type PanelDashboardPage struct { - Title string - CurrentUser User - Header *Header - Stats PanelStats - Zone string - GridItems []GridElement + *Header + Stats PanelStats + Zone string + GridItems []GridElement } type PanelTimeGraph struct { @@ -255,15 +253,40 @@ type PanelAnalyticsAgentPage struct { } type PanelThemesPage struct { - Title string - CurrentUser User - Header *Header + *Header Stats PanelStats Zone string PrimaryThemes []*Theme VariantThemes []*Theme } +type PanelMenuListItem struct { + ID int + ItemCount int +} + +type PanelMenuListPage struct { + *Header + Stats PanelStats + Zone string + ItemList []PanelMenuListItem +} + +type PanelMenuPage struct { + *Header + Stats PanelStats + Zone string + ID int + ItemList []MenuItem +} + +type PanelMenuItemPage struct { + *Header + Stats PanelStats + Zone string + Item MenuItem +} + type PanelUserPage struct { Title string CurrentUser User diff --git a/common/phrases.go b/common/phrases.go index ebb73770..ba8c48bf 100644 --- a/common/phrases.go +++ b/common/phrases.go @@ -49,7 +49,6 @@ type LanguagePack struct { NoticePhrases map[string]string PageTitles map[string]string TmplPhrases map[string]string - CSSPhrases map[string]string TmplIndicesToPhrases [][][]byte // [tmplID][index]phrase } @@ -231,8 +230,8 @@ func GetTmplPhrase(name string) string { return res } -func GetCSSPhrases() map[string]string { - return currentLangPack.Load().(*LanguagePack).CSSPhrases +func GetTmplPhrases() map[string]string { + return currentLangPack.Load().(*LanguagePack).TmplPhrases } func getPhrasePlaceholder(prefix string, suffix string) string { diff --git a/common/themes.go b/common/themes.go index 00591bcb..0076356c 100644 --- a/common/themes.go +++ b/common/themes.go @@ -212,7 +212,7 @@ func (theme *Theme) LoadStaticFiles() error { } func (theme *Theme) AddThemeStaticFiles() error { - phraseMap := GetCSSPhrases() + phraseMap := GetTmplPhrases() // TODO: Use a function instead of a closure to make this more testable? What about a function call inside the closure to take the theme variable into account? return filepath.Walk("./themes/"+theme.Name+"/public", func(path string, f os.FileInfo, err error) error { DebugLog("Attempting to add static file '" + path + "' for default theme '" + theme.Name + "'") diff --git a/common/user.go b/common/user.go index 474af49e..556ac2a4 100644 --- a/common/user.go +++ b/common/user.go @@ -83,24 +83,25 @@ var userStmts UserStmts func init() { DbInits.Add(func(acc *qgen.Accumulator) error { + var where = "uid = ?" userStmts = UserStmts{ - activate: acc.SimpleUpdate("users", "active = 1", "uid = ?"), - changeGroup: acc.SimpleUpdate("users", "group = ?", "uid = ?"), // TODO: Implement user_count for users_groups here - delete: acc.SimpleDelete("users", "uid = ?"), - setAvatar: acc.SimpleUpdate("users", "avatar = ?", "uid = ?"), - setUsername: acc.SimpleUpdate("users", "name = ?", "uid = ?"), - incrementTopics: acc.SimpleUpdate("users", "topics = topics + ?", "uid = ?"), - updateLevel: acc.SimpleUpdate("users", "level = ?", "uid = ?"), - incrementScore: acc.SimpleUpdate("users", "score = score + ?", "uid = ?"), - incrementPosts: acc.SimpleUpdate("users", "posts = posts + ?", "uid = ?"), - incrementBigposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?", "uid = ?"), - incrementMegaposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", "uid = ?"), - incrementLiked: acc.SimpleUpdate("users", "liked = liked + ?, lastLiked = UTC_TIMESTAMP()", "uid = ?"), - decrementLiked: acc.SimpleUpdate("users", "liked = liked - ?", "uid = ?"), + activate: acc.SimpleUpdate("users", "active = 1", where), + changeGroup: acc.SimpleUpdate("users", "group = ?", where), // TODO: Implement user_count for users_groups here + delete: acc.SimpleDelete("users", where), + setAvatar: acc.SimpleUpdate("users", "avatar = ?", where), + setUsername: acc.Update("users").Set("name = ?").Where(where).Prepare(), + incrementTopics: acc.SimpleUpdate("users", "topics = topics + ?", where), + updateLevel: acc.SimpleUpdate("users", "level = ?", where), + incrementScore: acc.SimpleUpdate("users", "score = score + ?", where), + incrementPosts: acc.SimpleUpdate("users", "posts = posts + ?", where), + incrementBigposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?", where), + incrementMegaposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", where), + incrementLiked: acc.SimpleUpdate("users", "liked = liked + ?, lastLiked = UTC_TIMESTAMP()", where), + decrementLiked: acc.SimpleUpdate("users", "liked = liked - ?", where), //recalcLastLiked: acc... - updateLastIP: acc.SimpleUpdate("users", "last_ip = ?", "uid = ?"), + updateLastIP: acc.SimpleUpdate("users", "last_ip = ?", where), - setPassword: acc.SimpleUpdate("users", "password = ?, salt = ?", "uid = ?"), + setPassword: acc.SimpleUpdate("users", "password = ?, salt = ?", where), } return acc.FirstError() }) @@ -231,12 +232,17 @@ func (user *User) Delete() error { return err } -func (user *User) ChangeName(username string) (err error) { - _, err = userStmts.setUsername.Exec(username, user.ID) +func (user *User) bindStmt(stmt *sql.Stmt, params ...interface{}) (err error) { + params = append(params, user.ID) + _, err = stmt.Exec(params...) user.CacheRemove() return err } +func (user *User) ChangeName(username string) (err error) { + return user.bindStmt(userStmts.setUsername, username) +} + func (user *User) ChangeAvatar(avatar string) (err error) { _, err = userStmts.setAvatar.Exec(avatar, user.ID) user.CacheRemove() diff --git a/common/widgets.go b/common/widgets.go index 22818b0b..b69b9f8e 100644 --- a/common/widgets.go +++ b/common/widgets.go @@ -134,8 +134,8 @@ func BuildWidget(dock string, header *Header) (sbody string) { widgets = Docks.RightOfNav case "topMenu": // 1 = id for the default menu - mhold := Menus.Get(1) - if mhold != nil { + mhold, err := Menus.Get(1) + if err == nil { err := mhold.Build(header.Writer, &header.CurrentUser) if err != nil { LogError(err) diff --git a/dev-update.bat b/dev-update.bat index b2aae510..e20c3d75 100644 --- a/dev-update.bat +++ b/dev-update.bat @@ -94,7 +94,10 @@ if %errorlevel% neq 0 ( echo Updating Gosora -copy ./schema/schema.json ./schema/lastSchema.json +cd schema +del /Q lastSchema.json +copy schema.json lastSchema.json +cd .. git pull origin master if %errorlevel% neq 0 ( pause diff --git a/new-update.bat b/experimental/new-update.bat similarity index 100% rename from new-update.bat rename to experimental/new-update.bat diff --git a/gen_router.go b/gen_router.go index 0662bf34..4c5f0015 100644 --- a/gen_router.go +++ b/gen_router.go @@ -49,6 +49,10 @@ var RouteMap = map[string]interface{}{ "routePanelWordFiltersDeleteSubmit": routePanelWordFiltersDeleteSubmit, "routePanelThemes": routePanelThemes, "routePanelThemesSetDefault": routePanelThemesSetDefault, + "routePanelThemesMenus": routePanelThemesMenus, + "routePanelThemesMenusEdit": routePanelThemesMenusEdit, + "routePanelThemesMenuItemEdit": routePanelThemesMenuItemEdit, + "routePanelThemesMenuItemEditSubmit": routePanelThemesMenuItemEditSubmit, "routePanelPlugins": routePanelPlugins, "routePanelPluginsActivate": routePanelPluginsActivate, "routePanelPluginsDeactivate": routePanelPluginsDeactivate, @@ -158,81 +162,85 @@ var routeMapEnum = map[string]int{ "routePanelWordFiltersDeleteSubmit": 27, "routePanelThemes": 28, "routePanelThemesSetDefault": 29, - "routePanelPlugins": 30, - "routePanelPluginsActivate": 31, - "routePanelPluginsDeactivate": 32, - "routePanelPluginsInstall": 33, - "routePanelUsers": 34, - "routePanelUsersEdit": 35, - "routePanelUsersEditSubmit": 36, - "routePanelAnalyticsViews": 37, - "routePanelAnalyticsRoutes": 38, - "routePanelAnalyticsAgents": 39, - "routePanelAnalyticsSystems": 40, - "routePanelAnalyticsLanguages": 41, - "routePanelAnalyticsReferrers": 42, - "routePanelAnalyticsRouteViews": 43, - "routePanelAnalyticsAgentViews": 44, - "routePanelAnalyticsForumViews": 45, - "routePanelAnalyticsSystemViews": 46, - "routePanelAnalyticsLanguageViews": 47, - "routePanelAnalyticsReferrerViews": 48, - "routePanelAnalyticsPosts": 49, - "routePanelAnalyticsTopics": 50, - "routePanelAnalyticsForums": 51, - "routePanelGroups": 52, - "routePanelGroupsEdit": 53, - "routePanelGroupsEditPerms": 54, - "routePanelGroupsEditSubmit": 55, - "routePanelGroupsEditPermsSubmit": 56, - "routePanelGroupsCreateSubmit": 57, - "routePanelBackups": 58, - "routePanelLogsMod": 59, - "routePanelDebug": 60, - "routePanelDashboard": 61, - "routes.AccountEditCritical": 62, - "routeAccountEditCriticalSubmit": 63, - "routeAccountEditAvatar": 64, - "routeAccountEditAvatarSubmit": 65, - "routeAccountEditUsername": 66, - "routeAccountEditUsernameSubmit": 67, - "routeAccountEditEmail": 68, - "routeAccountEditEmailTokenSubmit": 69, - "routes.ViewProfile": 70, - "routes.BanUserSubmit": 71, - "routes.UnbanUser": 72, - "routes.ActivateUser": 73, - "routes.IPSearch": 74, - "routes.CreateTopicSubmit": 75, - "routes.EditTopicSubmit": 76, - "routes.DeleteTopicSubmit": 77, - "routes.StickTopicSubmit": 78, - "routes.UnstickTopicSubmit": 79, - "routes.LockTopicSubmit": 80, - "routes.UnlockTopicSubmit": 81, - "routes.MoveTopicSubmit": 82, - "routeLikeTopicSubmit": 83, - "routes.ViewTopic": 84, - "routes.CreateReplySubmit": 85, - "routes.ReplyEditSubmit": 86, - "routes.ReplyDeleteSubmit": 87, - "routeReplyLikeSubmit": 88, - "routeProfileReplyCreateSubmit": 89, - "routes.ProfileReplyEditSubmit": 90, - "routes.ProfileReplyDeleteSubmit": 91, - "routes.PollVote": 92, - "routes.PollResults": 93, - "routes.AccountLogin": 94, - "routes.AccountRegister": 95, - "routeLogout": 96, - "routes.AccountLoginSubmit": 97, - "routes.AccountRegisterSubmit": 98, - "routeDynamic": 99, - "routeUploads": 100, - "routes.StaticFile": 101, - "routes.RobotsTxt": 102, - "routes.SitemapXml": 103, - "BadRoute": 104, + "routePanelThemesMenus": 30, + "routePanelThemesMenusEdit": 31, + "routePanelThemesMenuItemEdit": 32, + "routePanelThemesMenuItemEditSubmit": 33, + "routePanelPlugins": 34, + "routePanelPluginsActivate": 35, + "routePanelPluginsDeactivate": 36, + "routePanelPluginsInstall": 37, + "routePanelUsers": 38, + "routePanelUsersEdit": 39, + "routePanelUsersEditSubmit": 40, + "routePanelAnalyticsViews": 41, + "routePanelAnalyticsRoutes": 42, + "routePanelAnalyticsAgents": 43, + "routePanelAnalyticsSystems": 44, + "routePanelAnalyticsLanguages": 45, + "routePanelAnalyticsReferrers": 46, + "routePanelAnalyticsRouteViews": 47, + "routePanelAnalyticsAgentViews": 48, + "routePanelAnalyticsForumViews": 49, + "routePanelAnalyticsSystemViews": 50, + "routePanelAnalyticsLanguageViews": 51, + "routePanelAnalyticsReferrerViews": 52, + "routePanelAnalyticsPosts": 53, + "routePanelAnalyticsTopics": 54, + "routePanelAnalyticsForums": 55, + "routePanelGroups": 56, + "routePanelGroupsEdit": 57, + "routePanelGroupsEditPerms": 58, + "routePanelGroupsEditSubmit": 59, + "routePanelGroupsEditPermsSubmit": 60, + "routePanelGroupsCreateSubmit": 61, + "routePanelBackups": 62, + "routePanelLogsMod": 63, + "routePanelDebug": 64, + "routePanelDashboard": 65, + "routes.AccountEditCritical": 66, + "routeAccountEditCriticalSubmit": 67, + "routeAccountEditAvatar": 68, + "routeAccountEditAvatarSubmit": 69, + "routeAccountEditUsername": 70, + "routeAccountEditUsernameSubmit": 71, + "routeAccountEditEmail": 72, + "routeAccountEditEmailTokenSubmit": 73, + "routes.ViewProfile": 74, + "routes.BanUserSubmit": 75, + "routes.UnbanUser": 76, + "routes.ActivateUser": 77, + "routes.IPSearch": 78, + "routes.CreateTopicSubmit": 79, + "routes.EditTopicSubmit": 80, + "routes.DeleteTopicSubmit": 81, + "routes.StickTopicSubmit": 82, + "routes.UnstickTopicSubmit": 83, + "routes.LockTopicSubmit": 84, + "routes.UnlockTopicSubmit": 85, + "routes.MoveTopicSubmit": 86, + "routeLikeTopicSubmit": 87, + "routes.ViewTopic": 88, + "routes.CreateReplySubmit": 89, + "routes.ReplyEditSubmit": 90, + "routes.ReplyDeleteSubmit": 91, + "routeReplyLikeSubmit": 92, + "routeProfileReplyCreateSubmit": 93, + "routes.ProfileReplyEditSubmit": 94, + "routes.ProfileReplyDeleteSubmit": 95, + "routes.PollVote": 96, + "routes.PollResults": 97, + "routes.AccountLogin": 98, + "routes.AccountRegister": 99, + "routeLogout": 100, + "routes.AccountLoginSubmit": 101, + "routes.AccountRegisterSubmit": 102, + "routeDynamic": 103, + "routeUploads": 104, + "routes.StaticFile": 105, + "routes.RobotsTxt": 106, + "routes.SitemapXml": 107, + "BadRoute": 108, } var reverseRouteMapEnum = map[int]string{ 0: "routeAPI", @@ -265,81 +273,85 @@ var reverseRouteMapEnum = map[int]string{ 27: "routePanelWordFiltersDeleteSubmit", 28: "routePanelThemes", 29: "routePanelThemesSetDefault", - 30: "routePanelPlugins", - 31: "routePanelPluginsActivate", - 32: "routePanelPluginsDeactivate", - 33: "routePanelPluginsInstall", - 34: "routePanelUsers", - 35: "routePanelUsersEdit", - 36: "routePanelUsersEditSubmit", - 37: "routePanelAnalyticsViews", - 38: "routePanelAnalyticsRoutes", - 39: "routePanelAnalyticsAgents", - 40: "routePanelAnalyticsSystems", - 41: "routePanelAnalyticsLanguages", - 42: "routePanelAnalyticsReferrers", - 43: "routePanelAnalyticsRouteViews", - 44: "routePanelAnalyticsAgentViews", - 45: "routePanelAnalyticsForumViews", - 46: "routePanelAnalyticsSystemViews", - 47: "routePanelAnalyticsLanguageViews", - 48: "routePanelAnalyticsReferrerViews", - 49: "routePanelAnalyticsPosts", - 50: "routePanelAnalyticsTopics", - 51: "routePanelAnalyticsForums", - 52: "routePanelGroups", - 53: "routePanelGroupsEdit", - 54: "routePanelGroupsEditPerms", - 55: "routePanelGroupsEditSubmit", - 56: "routePanelGroupsEditPermsSubmit", - 57: "routePanelGroupsCreateSubmit", - 58: "routePanelBackups", - 59: "routePanelLogsMod", - 60: "routePanelDebug", - 61: "routePanelDashboard", - 62: "routes.AccountEditCritical", - 63: "routeAccountEditCriticalSubmit", - 64: "routeAccountEditAvatar", - 65: "routeAccountEditAvatarSubmit", - 66: "routeAccountEditUsername", - 67: "routeAccountEditUsernameSubmit", - 68: "routeAccountEditEmail", - 69: "routeAccountEditEmailTokenSubmit", - 70: "routes.ViewProfile", - 71: "routes.BanUserSubmit", - 72: "routes.UnbanUser", - 73: "routes.ActivateUser", - 74: "routes.IPSearch", - 75: "routes.CreateTopicSubmit", - 76: "routes.EditTopicSubmit", - 77: "routes.DeleteTopicSubmit", - 78: "routes.StickTopicSubmit", - 79: "routes.UnstickTopicSubmit", - 80: "routes.LockTopicSubmit", - 81: "routes.UnlockTopicSubmit", - 82: "routes.MoveTopicSubmit", - 83: "routeLikeTopicSubmit", - 84: "routes.ViewTopic", - 85: "routes.CreateReplySubmit", - 86: "routes.ReplyEditSubmit", - 87: "routes.ReplyDeleteSubmit", - 88: "routeReplyLikeSubmit", - 89: "routeProfileReplyCreateSubmit", - 90: "routes.ProfileReplyEditSubmit", - 91: "routes.ProfileReplyDeleteSubmit", - 92: "routes.PollVote", - 93: "routes.PollResults", - 94: "routes.AccountLogin", - 95: "routes.AccountRegister", - 96: "routeLogout", - 97: "routes.AccountLoginSubmit", - 98: "routes.AccountRegisterSubmit", - 99: "routeDynamic", - 100: "routeUploads", - 101: "routes.StaticFile", - 102: "routes.RobotsTxt", - 103: "routes.SitemapXml", - 104: "BadRoute", + 30: "routePanelThemesMenus", + 31: "routePanelThemesMenusEdit", + 32: "routePanelThemesMenuItemEdit", + 33: "routePanelThemesMenuItemEditSubmit", + 34: "routePanelPlugins", + 35: "routePanelPluginsActivate", + 36: "routePanelPluginsDeactivate", + 37: "routePanelPluginsInstall", + 38: "routePanelUsers", + 39: "routePanelUsersEdit", + 40: "routePanelUsersEditSubmit", + 41: "routePanelAnalyticsViews", + 42: "routePanelAnalyticsRoutes", + 43: "routePanelAnalyticsAgents", + 44: "routePanelAnalyticsSystems", + 45: "routePanelAnalyticsLanguages", + 46: "routePanelAnalyticsReferrers", + 47: "routePanelAnalyticsRouteViews", + 48: "routePanelAnalyticsAgentViews", + 49: "routePanelAnalyticsForumViews", + 50: "routePanelAnalyticsSystemViews", + 51: "routePanelAnalyticsLanguageViews", + 52: "routePanelAnalyticsReferrerViews", + 53: "routePanelAnalyticsPosts", + 54: "routePanelAnalyticsTopics", + 55: "routePanelAnalyticsForums", + 56: "routePanelGroups", + 57: "routePanelGroupsEdit", + 58: "routePanelGroupsEditPerms", + 59: "routePanelGroupsEditSubmit", + 60: "routePanelGroupsEditPermsSubmit", + 61: "routePanelGroupsCreateSubmit", + 62: "routePanelBackups", + 63: "routePanelLogsMod", + 64: "routePanelDebug", + 65: "routePanelDashboard", + 66: "routes.AccountEditCritical", + 67: "routeAccountEditCriticalSubmit", + 68: "routeAccountEditAvatar", + 69: "routeAccountEditAvatarSubmit", + 70: "routeAccountEditUsername", + 71: "routeAccountEditUsernameSubmit", + 72: "routeAccountEditEmail", + 73: "routeAccountEditEmailTokenSubmit", + 74: "routes.ViewProfile", + 75: "routes.BanUserSubmit", + 76: "routes.UnbanUser", + 77: "routes.ActivateUser", + 78: "routes.IPSearch", + 79: "routes.CreateTopicSubmit", + 80: "routes.EditTopicSubmit", + 81: "routes.DeleteTopicSubmit", + 82: "routes.StickTopicSubmit", + 83: "routes.UnstickTopicSubmit", + 84: "routes.LockTopicSubmit", + 85: "routes.UnlockTopicSubmit", + 86: "routes.MoveTopicSubmit", + 87: "routeLikeTopicSubmit", + 88: "routes.ViewTopic", + 89: "routes.CreateReplySubmit", + 90: "routes.ReplyEditSubmit", + 91: "routes.ReplyDeleteSubmit", + 92: "routeReplyLikeSubmit", + 93: "routeProfileReplyCreateSubmit", + 94: "routes.ProfileReplyEditSubmit", + 95: "routes.ProfileReplyDeleteSubmit", + 96: "routes.PollVote", + 97: "routes.PollResults", + 98: "routes.AccountLogin", + 99: "routes.AccountRegister", + 100: "routeLogout", + 101: "routes.AccountLoginSubmit", + 102: "routes.AccountRegisterSubmit", + 103: "routeDynamic", + 104: "routeUploads", + 105: "routes.StaticFile", + 106: "routes.RobotsTxt", + 107: "routes.SitemapXml", + 108: "BadRoute", } var osMapEnum = map[string]int{ "unknown": 0, @@ -635,7 +647,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.GlobalViewCounter.Bump() if prefix == "/static" { - counters.RouteViewCounter.Bump(101) + counters.RouteViewCounter.Bump(105) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -1038,8 +1050,26 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.RouteViewCounter.Bump(29) err = routePanelThemesSetDefault(w,req,user,extraData) - case "/panel/plugins/": + case "/panel/themes/menus/": counters.RouteViewCounter.Bump(30) + err = routePanelThemesMenus(w,req,user) + case "/panel/themes/menus/edit/": + counters.RouteViewCounter.Bump(31) + err = routePanelThemesMenusEdit(w,req,user,extraData) + case "/panel/themes/menus/item/edit/": + counters.RouteViewCounter.Bump(32) + err = routePanelThemesMenuItemEdit(w,req,user,extraData) + case "/panel/themes/menus/item/edit/submit/": + err = common.NoSessionMismatch(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + counters.RouteViewCounter.Bump(33) + err = routePanelThemesMenuItemEditSubmit(w,req,user,extraData) + case "/panel/plugins/": + counters.RouteViewCounter.Bump(34) err = routePanelPlugins(w,req,user) case "/panel/plugins/activate/": err = common.NoSessionMismatch(w,req,user) @@ -1048,7 +1078,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(31) + counters.RouteViewCounter.Bump(35) err = routePanelPluginsActivate(w,req,user,extraData) case "/panel/plugins/deactivate/": err = common.NoSessionMismatch(w,req,user) @@ -1057,7 +1087,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(32) + counters.RouteViewCounter.Bump(36) err = routePanelPluginsDeactivate(w,req,user,extraData) case "/panel/plugins/install/": err = common.NoSessionMismatch(w,req,user) @@ -1066,13 +1096,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(33) + counters.RouteViewCounter.Bump(37) err = routePanelPluginsInstall(w,req,user,extraData) case "/panel/users/": - counters.RouteViewCounter.Bump(34) + counters.RouteViewCounter.Bump(38) err = routePanelUsers(w,req,user) case "/panel/users/edit/": - counters.RouteViewCounter.Bump(35) + counters.RouteViewCounter.Bump(39) err = routePanelUsersEdit(w,req,user,extraData) case "/panel/users/edit/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1081,7 +1111,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(36) + counters.RouteViewCounter.Bump(40) err = routePanelUsersEditSubmit(w,req,user,extraData) case "/panel/analytics/views/": err = common.ParseForm(w,req,user) @@ -1090,7 +1120,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(37) + counters.RouteViewCounter.Bump(41) err = routePanelAnalyticsViews(w,req,user) case "/panel/analytics/routes/": err = common.ParseForm(w,req,user) @@ -1099,7 +1129,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(38) + counters.RouteViewCounter.Bump(42) err = routePanelAnalyticsRoutes(w,req,user) case "/panel/analytics/agents/": err = common.ParseForm(w,req,user) @@ -1108,7 +1138,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(39) + counters.RouteViewCounter.Bump(43) err = routePanelAnalyticsAgents(w,req,user) case "/panel/analytics/systems/": err = common.ParseForm(w,req,user) @@ -1117,7 +1147,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(40) + counters.RouteViewCounter.Bump(44) err = routePanelAnalyticsSystems(w,req,user) case "/panel/analytics/langs/": err = common.ParseForm(w,req,user) @@ -1126,7 +1156,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(41) + counters.RouteViewCounter.Bump(45) err = routePanelAnalyticsLanguages(w,req,user) case "/panel/analytics/referrers/": err = common.ParseForm(w,req,user) @@ -1135,25 +1165,25 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(42) + counters.RouteViewCounter.Bump(46) err = routePanelAnalyticsReferrers(w,req,user) case "/panel/analytics/route/": - counters.RouteViewCounter.Bump(43) + counters.RouteViewCounter.Bump(47) err = routePanelAnalyticsRouteViews(w,req,user,extraData) case "/panel/analytics/agent/": - counters.RouteViewCounter.Bump(44) + counters.RouteViewCounter.Bump(48) err = routePanelAnalyticsAgentViews(w,req,user,extraData) case "/panel/analytics/forum/": - counters.RouteViewCounter.Bump(45) + counters.RouteViewCounter.Bump(49) err = routePanelAnalyticsForumViews(w,req,user,extraData) case "/panel/analytics/system/": - counters.RouteViewCounter.Bump(46) + counters.RouteViewCounter.Bump(50) err = routePanelAnalyticsSystemViews(w,req,user,extraData) case "/panel/analytics/lang/": - counters.RouteViewCounter.Bump(47) + counters.RouteViewCounter.Bump(51) err = routePanelAnalyticsLanguageViews(w,req,user,extraData) case "/panel/analytics/referrer/": - counters.RouteViewCounter.Bump(48) + counters.RouteViewCounter.Bump(52) err = routePanelAnalyticsReferrerViews(w,req,user,extraData) case "/panel/analytics/posts/": err = common.ParseForm(w,req,user) @@ -1162,7 +1192,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(49) + counters.RouteViewCounter.Bump(53) err = routePanelAnalyticsPosts(w,req,user) case "/panel/analytics/topics/": err = common.ParseForm(w,req,user) @@ -1171,7 +1201,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(50) + counters.RouteViewCounter.Bump(54) err = routePanelAnalyticsTopics(w,req,user) case "/panel/analytics/forums/": err = common.ParseForm(w,req,user) @@ -1180,16 +1210,16 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(51) + counters.RouteViewCounter.Bump(55) err = routePanelAnalyticsForums(w,req,user) case "/panel/groups/": - counters.RouteViewCounter.Bump(52) + counters.RouteViewCounter.Bump(56) err = routePanelGroups(w,req,user) case "/panel/groups/edit/": - counters.RouteViewCounter.Bump(53) + counters.RouteViewCounter.Bump(57) err = routePanelGroupsEdit(w,req,user,extraData) case "/panel/groups/edit/perms/": - counters.RouteViewCounter.Bump(54) + counters.RouteViewCounter.Bump(58) err = routePanelGroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1198,7 +1228,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(55) + counters.RouteViewCounter.Bump(59) err = routePanelGroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1207,7 +1237,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(56) + counters.RouteViewCounter.Bump(60) err = routePanelGroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = common.NoSessionMismatch(w,req,user) @@ -1216,7 +1246,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(57) + counters.RouteViewCounter.Bump(61) err = routePanelGroupsCreateSubmit(w,req,user) case "/panel/backups/": err = common.SuperAdminOnly(w,req,user) @@ -1225,10 +1255,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(58) + counters.RouteViewCounter.Bump(62) err = routePanelBackups(w,req,user,extraData) case "/panel/logs/mod/": - counters.RouteViewCounter.Bump(59) + counters.RouteViewCounter.Bump(63) err = routePanelLogsMod(w,req,user) case "/panel/debug/": err = common.AdminOnly(w,req,user) @@ -1237,10 +1267,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(60) + counters.RouteViewCounter.Bump(64) err = routePanelDebug(w,req,user) default: - counters.RouteViewCounter.Bump(61) + counters.RouteViewCounter.Bump(65) err = routePanelDashboard(w,req,user) } if err != nil { @@ -1255,7 +1285,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(62) + counters.RouteViewCounter.Bump(66) err = routes.AccountEditCritical(w,req,user) case "/user/edit/critical/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1270,7 +1300,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(63) + counters.RouteViewCounter.Bump(67) err = routeAccountEditCriticalSubmit(w,req,user) case "/user/edit/avatar/": err = common.MemberOnly(w,req,user) @@ -1279,7 +1309,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(64) + counters.RouteViewCounter.Bump(68) err = routeAccountEditAvatar(w,req,user) case "/user/edit/avatar/submit/": err = common.MemberOnly(w,req,user) @@ -1299,7 +1329,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(65) + counters.RouteViewCounter.Bump(69) err = routeAccountEditAvatarSubmit(w,req,user) case "/user/edit/username/": err = common.MemberOnly(w,req,user) @@ -1308,7 +1338,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(66) + counters.RouteViewCounter.Bump(70) err = routeAccountEditUsername(w,req,user) case "/user/edit/username/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1323,7 +1353,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(67) + counters.RouteViewCounter.Bump(71) err = routeAccountEditUsernameSubmit(w,req,user) case "/user/edit/email/": err = common.MemberOnly(w,req,user) @@ -1332,7 +1362,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(68) + counters.RouteViewCounter.Bump(72) err = routeAccountEditEmail(w,req,user) case "/user/edit/token/": err = common.NoSessionMismatch(w,req,user) @@ -1347,11 +1377,11 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(69) + counters.RouteViewCounter.Bump(73) err = routeAccountEditEmailTokenSubmit(w,req,user,extraData) default: req.URL.Path += extraData - counters.RouteViewCounter.Bump(70) + counters.RouteViewCounter.Bump(74) err = routes.ViewProfile(w,req,user) } if err != nil { @@ -1372,7 +1402,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(71) + counters.RouteViewCounter.Bump(75) err = routes.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = common.NoSessionMismatch(w,req,user) @@ -1387,7 +1417,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(72) + counters.RouteViewCounter.Bump(76) err = routes.UnbanUser(w,req,user,extraData) case "/users/activate/": err = common.NoSessionMismatch(w,req,user) @@ -1402,7 +1432,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(73) + counters.RouteViewCounter.Bump(77) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = common.MemberOnly(w,req,user) @@ -1411,7 +1441,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(74) + counters.RouteViewCounter.Bump(78) err = routes.IPSearch(w,req,user) } if err != nil { @@ -1437,7 +1467,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(75) + counters.RouteViewCounter.Bump(79) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1452,7 +1482,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(76) + counters.RouteViewCounter.Bump(80) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1468,7 +1498,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } req.URL.Path += extraData - counters.RouteViewCounter.Bump(77) + counters.RouteViewCounter.Bump(81) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1483,7 +1513,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(78) + counters.RouteViewCounter.Bump(82) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1498,7 +1528,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(79) + counters.RouteViewCounter.Bump(83) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1514,7 +1544,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } req.URL.Path += extraData - counters.RouteViewCounter.Bump(80) + counters.RouteViewCounter.Bump(84) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1529,7 +1559,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(81) + counters.RouteViewCounter.Bump(85) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1544,7 +1574,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(82) + counters.RouteViewCounter.Bump(86) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1565,10 +1595,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(83) + counters.RouteViewCounter.Bump(87) err = routeLikeTopicSubmit(w,req,user,extraData) default: - counters.RouteViewCounter.Bump(84) + counters.RouteViewCounter.Bump(88) err = routes.ViewTopic(w,req,user, extraData) } if err != nil { @@ -1594,7 +1624,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(85) + counters.RouteViewCounter.Bump(89) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1609,7 +1639,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(86) + counters.RouteViewCounter.Bump(90) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1624,7 +1654,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(87) + counters.RouteViewCounter.Bump(91) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1645,7 +1675,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(88) + counters.RouteViewCounter.Bump(92) err = routeReplyLikeSubmit(w,req,user,extraData) } if err != nil { @@ -1666,7 +1696,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(89) + counters.RouteViewCounter.Bump(93) err = routeProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1681,7 +1711,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(90) + counters.RouteViewCounter.Bump(94) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = common.NoSessionMismatch(w,req,user) @@ -1696,7 +1726,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(91) + counters.RouteViewCounter.Bump(95) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } if err != nil { @@ -1717,10 +1747,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(92) + counters.RouteViewCounter.Bump(96) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - counters.RouteViewCounter.Bump(93) + counters.RouteViewCounter.Bump(97) err = routes.PollResults(w,req,user,extraData) } if err != nil { @@ -1729,10 +1759,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - counters.RouteViewCounter.Bump(94) + counters.RouteViewCounter.Bump(98) err = routes.AccountLogin(w,req,user) case "/accounts/create/": - counters.RouteViewCounter.Bump(95) + counters.RouteViewCounter.Bump(99) err = routes.AccountRegister(w,req,user) case "/accounts/logout/": err = common.NoSessionMismatch(w,req,user) @@ -1747,7 +1777,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(96) + counters.RouteViewCounter.Bump(100) err = routeLogout(w,req,user) case "/accounts/login/submit/": err = common.ParseForm(w,req,user) @@ -1756,7 +1786,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(97) + counters.RouteViewCounter.Bump(101) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/create/submit/": err = common.ParseForm(w,req,user) @@ -1765,7 +1795,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - counters.RouteViewCounter.Bump(98) + counters.RouteViewCounter.Bump(102) err = routes.AccountRegisterSubmit(w,req,user) } if err != nil { @@ -1782,7 +1812,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.NotFound(w,req,nil) return } - counters.RouteViewCounter.Bump(100) + counters.RouteViewCounter.Bump(104) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? router.UploadHandler(w,req) // TODO: Count these views @@ -1791,14 +1821,14 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO: Add support for favicons and robots.txt files switch(extraData) { case "robots.txt": - counters.RouteViewCounter.Bump(102) + counters.RouteViewCounter.Bump(106) err = routes.RobotsTxt(w,req) if err != nil { router.handleError(err,w,req,user) } return /*case "sitemap.xml": - counters.RouteViewCounter.Bump(103) + counters.RouteViewCounter.Bump(107) err = routes.SitemapXml(w,req) if err != nil { router.handleError(err,w,req,user) @@ -1827,7 +1857,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { router.RUnlock() if ok { - counters.RouteViewCounter.Bump(99) // TODO: Be more specific about *which* dynamic route it is + counters.RouteViewCounter.Bump(103) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData err = handle(w,req,user) if err != nil { @@ -1842,7 +1872,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } else { router.DumpRequest(req,"Bad Route") } - counters.RouteViewCounter.Bump(104) + counters.RouteViewCounter.Bump(108) common.NotFound(w,req,nil) } } diff --git a/gen_tables.go b/gen_tables.go index 703a8a05..55c40060 100644 --- a/gen_tables.go +++ b/gen_tables.go @@ -2,17 +2,18 @@ package main var dbTablePrimaryKeys = map[string]string{ - "users":"uid", - "topics":"tid", - "revisions":"reviseID", - "polls":"pollID", - "users_replies":"rid", - "activity_stream":"asid", - "word_filters":"wfid", "users_groups":"gid", "users_groups_scheduler":"uid", - "forums":"fid", + "users_replies":"rid", + "topics":"tid", "replies":"rid", - "attachments":"attachID", + "revisions":"reviseID", + "activity_stream":"asid", + "word_filters":"wfid", "menus":"mid", + "users":"uid", + "menu_items":"miid", + "forums":"fid", + "attachments":"attachID", + "polls":"pollID", } diff --git a/langs/english.json b/langs/english.json index 32e9a9c6..90721533 100644 --- a/langs/english.json +++ b/langs/english.json @@ -93,6 +93,8 @@ "panel_groups":"Group Manager", "panel_edit_group":"Group Editor", "panel_themes":"Theme Manager", + "panel_themes_menus":"Menu Manager", + "panel_themes_menus_edit":"Menu Editor", "panel_backups":"Backups", "panel_mod_logs":"Moderation Logs", "panel_admin_logs":"Administration Logs", @@ -246,6 +248,66 @@ }, "TmplPhrases": { + "pipe":"|", + + "menu_forums":"Forums", + "menu_topics":"Topics", + "menu_alerts":"Alerts", + "menu_account":"Account", + "menu_profile":"Profile", + "menu_panel":"Panel", + "menu_logout":"Logout", + "menu_login":"Login", + "menu_register":"Register", + + "topics_click_topics_to_select":"Click the topics to select them", + "topics_new_topic":"New Topic", + "forum_locked":"Locked", + "topics_replies_suffix":" replies", + "forums_topics_suffix":" topics", + "topics_gap_likes_suffix":" likes", + "topics_likes_suffix":"likes", + "topics_last":"Last", + "topics_starter":"Starter", + "topic_like_count_suffix":" likes", + "topic_plus":"+", + "topic_plus_one":"+1", + "topic_gap_up":" up", + "topic_level":"Level", + "topic_edit_button_text":"Edit", + "topic_delete_button_text":"Delete", + "topic_ip_button_text":"IP", + "topic_lock_button_text":"Lock", + "topic_unlock_button_text":"Unlock", + "topic_pin_button_text":"Pin", + "topic_unpin_button_text":"Unpin", + "topic_report_button_text":"Report", + "topic_flag_button_text":"Flag", + + "panel_rank_admins":"Admins", + "panel_rank_mods":"Mods", + "panel_rank_banned":"Banned", + "panel_rank_guests":"Guests", + "panel_rank_members":"Members", + + "panel_preset_announcements":"Announcements", + "panel_preset_member_only":"Member Only", + "panel_preset_staff_only":"Staff Only", + "panel_preset_admin_only":"Admin Only", + "panel_preset_archive":"Archive", + "panel_preset_public":"Public", + "panel_active_hidden":"Hidden", + + "panel_perms_no_access":"No Access", + "panel_perms_read_only":"Read Only", + "panel_perms_can_post":"Can Post", + "panel_perms_can_moderate":"Can Moderate", + "panel_perms_custom":"Custom", + "panel_perms_default":"Default", + + "panel_edit_button_text":"Edit", + "panel_delete_button_text":"Delete", + "menu_forums_tooltip":"Forum List", "menu_forums_aria":"The Forum list", "menu_topics_tooltip":"Topic List", @@ -639,6 +701,25 @@ "panel_themes_default":"Default", "panel_themes_make_default":"Make Default", + "panel_themes_menus_head":"Menus", + + "panel_themes_menus_edit_head":"Menu Editor", + "panel_themes_menus_name":"Name", + "panel_themes_menus_htmlid":"HTML ID", + "panel_themes_menus_cssclass":"CSS Class", + "panel_themes_menus_position":"Position", + "panel_themes_menus_path":"Path", + "panel_themes_menus_aria":"Aria", + "panel_themes_menus_tooltip":"Tooltip", + "panel_themes_menus_tmplname":"Template", + "panel_themes_menus_permissions":"Who Can See", + "panel_themes_menus_everyone": "Everyone", + "panel_themes_menus_guestonly":"Guests", + "panel_themes_menus_memberonly":"Members", + "panel_themes_menus_supermodonly":"Super Mods", + "panel_themes_menus_adminonly":"Admins", + "panel_themes_menus_edit_update_button":"Update", + "panel_settings_head":"Settings", "panel_setting_head":"Edit Setting", "panel_setting_name":"Setting Name", @@ -654,67 +735,5 @@ "panel_debug_uptime_label":"Uptime", "panel_debug_open_database_connections_label":"Open DB Conns", "panel_debug_adapter_label":"Adapter" - }, - - "CSSPhrases": { - "pipe":"|", - - "menu_forums":"Forums", - "menu_topics":"Topics", - "menu_alerts":"Alerts", - "menu_account":"Account", - "menu_profile":"Profile", - "menu_panel":"Panel", - "menu_logout":"Logout", - "menu_login":"Login", - "menu_register":"Register", - - "topics_click_topics_to_select":"Click the topics to select them", - "topics_new_topic":"New Topic", - "forum_locked":"Locked", - "topics_replies_suffix":" replies", - "forums_topics_suffix":" topics", - "topics_gap_likes_suffix":" likes", - "topics_likes_suffix":"likes", - "topics_last":"Last", - "topics_starter":"Starter", - "topic_like_count_suffix":" likes", - "topic_plus":"+", - "topic_plus_one":"+1", - "topic_gap_up":" up", - "topic_level":"Level", - "topic_edit_button_text":"Edit", - "topic_delete_button_text":"Delete", - "topic_ip_button_text":"IP", - "topic_lock_button_text":"Lock", - "topic_unlock_button_text":"Unlock", - "topic_pin_button_text":"Pin", - "topic_unpin_button_text":"Unpin", - "topic_report_button_text":"Report", - "topic_flag_button_text":"Flag", - - "panel_rank_admins":"Admins", - "panel_rank_mods":"Mods", - "panel_rank_banned":"Banned", - "panel_rank_guests":"Guests", - "panel_rank_members":"Members", - - "panel_preset_announcements":"Announcements", - "panel_preset_member_only":"Member Only", - "panel_preset_staff_only":"Staff Only", - "panel_preset_admin_only":"Admin Only", - "panel_preset_archive":"Archive", - "panel_preset_public":"Public", - "panel_active_hidden":"Hidden", - - "panel_perms_no_access":"No Access", - "panel_perms_read_only":"Read Only", - "panel_perms_can_post":"Can Post", - "panel_perms_can_moderate":"Can Moderate", - "panel_perms_custom":"Custom", - "panel_perms_default":"Default", - - "panel_edit_button_text":"Edit", - "panel_delete_button_text":"Delete" } } \ No newline at end of file diff --git a/main.go b/main.go index c477e60d..1be52abc 100644 --- a/main.go +++ b/main.go @@ -78,7 +78,10 @@ func afterDBInit() (err error) { if err != nil { return err } - menuHold := common.Menus.Get(1) + menuHold, err := common.Menus.Get(1) + if err != nil { + return err + } fmt.Printf("menuHold: %+v\n", menuHold) var b bytes.Buffer menuHold.Build(&b, &common.GuestUser) diff --git a/panel_routes.go b/panel_routes.go index 1fcdcb04..645d3b30 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -47,6 +47,7 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use if ferr != nil { return ferr } + headerVars.Title = common.GetTitlePhrase("panel_dashboard") // We won't calculate this on the spot anymore, as the system doesn't seem to like it if we do multiple fetches simultaneously. Should we constantly calculate this on a background thread? Perhaps, the watchdog to scale back heavy features under load? One plus side is that we'd get immediate CPU percentages here instead of waiting it to kick in with WebSockets var cpustr = "Unknown" @@ -167,7 +168,7 @@ func routePanelDashboard(w http.ResponseWriter, r *http.Request, user common.Use gridElements = append(gridElements, common.GridElement{"dash-postsperuser", "5 posts / user / week", 14, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of posts made by each active user over the past week"*/}) } - pi := common.PanelDashboardPage{common.GetTitlePhrase("panel_dashboard"), user, headerVars, stats, "dashboard", gridElements} + pi := common.PanelDashboardPage{headerVars, stats, "dashboard", gridElements} return panelRenderTemplate("panel_dashboard", w, r, user, &pi) } @@ -2288,13 +2289,14 @@ func routePanelGroupsCreateSubmit(w http.ResponseWriter, r *http.Request, user c } func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { - headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) + header, stats, ferr := common.PanelUserCheck(w, r, &user) if ferr != nil { return ferr } if !user.Perms.ManageThemes { return common.NoPermissions(w, r, user) } + header.Title = common.GetTitlePhrase("panel_themes") var pThemeList, vThemeList []*common.Theme for _, theme := range common.Themes { @@ -2309,7 +2311,7 @@ func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User) } - pi := common.PanelThemesPage{common.GetTitlePhrase("panel_themes"), user, headerVars, stats, "themes", pThemeList, vThemeList} + pi := common.PanelThemesPage{header, stats, "themes", pThemeList, vThemeList} return panelRenderTemplate("panel_themes", w, r, user, &pi) } @@ -2378,6 +2380,176 @@ func routePanelThemesSetDefault(w http.ResponseWriter, r *http.Request, user com return nil } +func routePanelThemesMenus(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { + header, stats, ferr := common.PanelUserCheck(w, r, &user) + if ferr != nil { + return ferr + } + if !user.Perms.ManageThemes { + return common.NoPermissions(w, r, user) + } + header.Title = common.GetTitlePhrase("panel_themes_menus") + + var menuList []common.PanelMenuListItem + for mid, list := range common.Menus.GetAllMap() { + menuList = append(menuList, common.PanelMenuListItem{ + ID: mid, + ItemCount: len(list.List), + }) + } + + pi := common.PanelMenuListPage{header, stats, "themes", menuList} + return panelRenderTemplate("panel_themes_menus", w, r, user, &pi) +} + +func routePanelThemesMenusEdit(w http.ResponseWriter, r *http.Request, user common.User, smid string) common.RouteError { + header, stats, ferr := common.PanelUserCheck(w, r, &user) + if ferr != nil { + return ferr + } + if !user.Perms.ManageThemes { + return common.NoPermissions(w, r, user) + } + // TODO: Something like Menu #1 for the title? + header.Title = common.GetTitlePhrase("panel_themes_menus_edit") + + mid, err := strconv.Atoi(smid) + if err != nil { + return common.LocalError("Invalid integer", w, r, user) + } + + menuHold, err := common.Menus.Get(mid) + if err == ErrNoRows { + return common.NotFound(w, r, header) + } else if err != nil { + return common.InternalError(err, w, r) + } + + var menuList []common.MenuItem + for _, item := range menuHold.List { + var menuTmpls = map[string]common.MenuTmpl{ + item.TmplName: menuHold.Parse(item.Name, []byte("{{.Name}}")), + } + var renderBuffer [][]byte + var variableIndices []int + renderBuffer, _ = menuHold.ScanItem(menuTmpls, item, renderBuffer, variableIndices) + + var out string + for _, renderItem := range renderBuffer { + out += string(renderItem) + } + item.Name = out + if item.Name == "" { + item.Name = "???" + } + menuList = append(menuList, item) + } + + pi := common.PanelMenuPage{header, stats, "themes", mid, menuList} + return panelRenderTemplate("panel_themes_menus_items", w, r, user, &pi) +} + +func routePanelThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user common.User, sitemID string) common.RouteError { + header, stats, ferr := common.PanelUserCheck(w, r, &user) + if ferr != nil { + return ferr + } + if !user.Perms.ManageThemes { + return common.NoPermissions(w, r, user) + } + // TODO: Something like Menu #1 for the title? + header.Title = common.GetTitlePhrase("panel_themes_menus_edit") + + itemID, err := strconv.Atoi(sitemID) + if err != nil { + return common.LocalError("Invalid integer", w, r, user) + } + + menuItem, err := common.Menus.ItemStore().Get(itemID) + if err == ErrNoRows { + return common.NotFound(w, r, header) + } else if err != nil { + return common.InternalError(err, w, r) + } + + pi := common.PanelMenuItemPage{header, stats, "themes", menuItem} + return panelRenderTemplate("panel_themes_menus_item_edit", w, r, user, &pi) +} + +func routePanelThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, sitemID string) common.RouteError { + _, ferr := common.SimplePanelUserCheck(w, r, &user) + if ferr != nil { + return ferr + } + isJs := (r.PostFormValue("js") == "1") + if !user.Perms.ManageThemes { + return common.NoPermissionsJSQ(w, r, user, isJs) + } + + itemID, err := strconv.Atoi(sitemID) + if err != nil { + return common.LocalErrorJSQ("Invalid integer", w, r, user, isJs) + } + + menuItem, err := common.Menus.ItemStore().Get(itemID) + if err == ErrNoRows { + return common.LocalErrorJSQ("This item doesn't exist.", w, r, user, isJs) + } else if err != nil { + return common.InternalErrorJSQ(err, w, r, isJs) + } + //menuItem = menuItem.Copy() // If we switch this for a pointer, we might need this as a scratchpad + + var getItem = func(name string) string { + return html.EscapeString(strings.Replace(r.PostFormValue("item-"+name), "\n", "", -1)) + } + menuItem.Name = getItem("name") + menuItem.HTMLID = getItem("htmlid") + menuItem.CSSClass = getItem("cssclass") + menuItem.Position = getItem("position") + if menuItem.Position != "left" && menuItem.Position != "right" { + menuItem.Position = "left" + } + menuItem.Path = getItem("path") + menuItem.Aria = getItem("aria") + menuItem.Tooltip = getItem("tooltip") + menuItem.TmplName = getItem("tmplname") + + var perms = getItem("permissions") + switch perms { + case "everyone": + menuItem.GuestOnly = false + menuItem.MemberOnly = false + menuItem.SuperModOnly = false + menuItem.AdminOnly = false + case "guest-only": + menuItem.GuestOnly = true + menuItem.MemberOnly = false + menuItem.SuperModOnly = false + menuItem.AdminOnly = false + case "member-only": + menuItem.GuestOnly = false + menuItem.MemberOnly = true + menuItem.SuperModOnly = false + menuItem.AdminOnly = false + case "supermod-only": + menuItem.GuestOnly = false + menuItem.MemberOnly = true + menuItem.SuperModOnly = true + menuItem.AdminOnly = false + case "admin-only": + menuItem.GuestOnly = false + menuItem.MemberOnly = true + menuItem.SuperModOnly = true + menuItem.AdminOnly = true + } + + err = menuItem.Commit() + if err != nil { + return common.InternalErrorJSQ(err, w, r, isJs) + } + return panelSuccessRedirect("/panel/themes/menus/item/edit/"+strconv.Itoa(itemID), w, r, isJs) +} + func routePanelBackups(w http.ResponseWriter, r *http.Request, user common.User, backupURL string) common.RouteError { headerVars, stats, ferr := common.PanelUserCheck(w, r, &user) if ferr != nil { diff --git a/patcher/main.go b/patcher/main.go index f085a77b..4dc90ca3 100644 --- a/patcher/main.go +++ b/patcher/main.go @@ -26,6 +26,7 @@ func main() { fmt.Println(r) debug.PrintStack() pressAnyKey(scanner) + log.Fatal("") return } }() @@ -73,6 +74,7 @@ type SchemaFile struct { } func patcher(scanner *bufio.Scanner) error { + fmt.Println("Loading the schema file") data, err := ioutil.ReadFile("./schema/lastSchema.json") if err != nil { return err @@ -84,6 +86,8 @@ func patcher(scanner *bufio.Scanner) error { return err } _ = schemaFile + + fmt.Println("Applying the patches") return patch0(scanner) } diff --git a/patcher/patches.go b/patcher/patches.go index f1cef946..0b896d5a 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -7,8 +7,18 @@ import ( "../query_gen/lib" ) -func patch0(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.CreateTable("menus", "", "", +func patch0(scanner *bufio.Scanner) (err error) { + err = execStmt(qgen.Builder.DropTable("menus")) + if err != nil { + return err + } + + err = execStmt(qgen.Builder.DropTable("menu_items")) + if err != nil { + return err + } + + err = execStmt(qgen.Builder.CreateTable("menus", "", "", []qgen.DBTableColumn{ qgen.DBTableColumn{"mid", "int", 0, false, true, ""}, }, @@ -22,7 +32,9 @@ func patch0(scanner *bufio.Scanner) error { err = execStmt(qgen.Builder.CreateTable("menu_items", "", "", []qgen.DBTableColumn{ + qgen.DBTableColumn{"miid", "int", 0, false, true, ""}, qgen.DBTableColumn{"mid", "int", 0, false, false, ""}, + qgen.DBTableColumn{"name", "varchar", 200, false, false, ""}, qgen.DBTableColumn{"htmlID", "varchar", 200, false, false, "''"}, qgen.DBTableColumn{"cssClass", "varchar", 200, false, false, "''"}, qgen.DBTableColumn{"position", "varchar", 100, false, false, ""}, @@ -37,7 +49,9 @@ func patch0(scanner *bufio.Scanner) error { qgen.DBTableColumn{"staffOnly", "boolean", 0, false, false, "0"}, qgen.DBTableColumn{"adminOnly", "boolean", 0, false, false, "0"}, }, - []qgen.DBTableKey{}, + []qgen.DBTableKey{ + qgen.DBTableKey{"miid", "primary"}, + }, )) if err != nil { return err @@ -49,7 +63,7 @@ func patch0(scanner *bufio.Scanner) error { } var order int - var mOrder = "mid, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly" + var mOrder = "mid, name, htmlID, cssClass, position, path, aria, tooltip, guestOnly, memberOnly, staffOnly, adminOnly" var addMenuItem = func(data map[string]interface{}) error { cols, values := qgen.InterfaceMapToInsertStrings(data, mOrder) err := execStmt(qgen.Builder.SimpleInsert("menu_items", cols+", order", values+","+strconv.Itoa(order))) @@ -57,12 +71,12 @@ func patch0(scanner *bufio.Scanner) error { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "htmlID": "menu_forums", "position": "left", "path": "/forums/", "aria": "{lang.menu_forums_aria}", "tooltip": "{lang.menu_forums_tooltip}"}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_forums}", "htmlID": "menu_forums", "position": "left", "path": "/forums/", "aria": "{lang.menu_forums_aria}", "tooltip": "{lang.menu_forums_tooltip}"}) if err != nil { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "htmlID": "menu_topics", "cssClass": "menu_topics", "position": "left", "path": "/topics/", "aria": "{lang.menu_topics_aria}", "tooltip": "{lang.menu_topics_tooltip}"}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_topics}", "htmlID": "menu_topics", "cssClass": "menu_topics", "position": "left", "path": "/topics/", "aria": "{lang.menu_topics_aria}", "tooltip": "{lang.menu_topics_tooltip}"}) if err != nil { return err } @@ -72,35 +86,39 @@ func patch0(scanner *bufio.Scanner) error { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "cssClass": "menu_account", "position": "left", "path": "/user/edit/critical/", "aria": "{lang.menu_account_aria}", "tooltip": "{lang.menu_account_tooltip}", "memberOnly": true}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_account}", "cssClass": "menu_account", "position": "left", "path": "/user/edit/critical/", "aria": "{lang.menu_account_aria}", "tooltip": "{lang.menu_account_tooltip}", "memberOnly": true}) if err != nil { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "cssClass": "menu_profile", "position": "left", "path": "{me.Link}", "aria": "{lang.menu_profile_aria}", "tooltip": "{lang.menu_profile_tooltip}", "memberOnly": true}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_profile}", "cssClass": "menu_profile", "position": "left", "path": "{me.Link}", "aria": "{lang.menu_profile_aria}", "tooltip": "{lang.menu_profile_tooltip}", "memberOnly": true}) if err != nil { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "cssClass": "menu_panel menu_account", "position": "left", "path": "/panel/", "aria": "{lang.menu_panel_aria}", "tooltip": "{lang.menu_panel_tooltip}", "memberOnly": true, "staffOnly": true}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_panel}", "cssClass": "menu_panel menu_account", "position": "left", "path": "/panel/", "aria": "{lang.menu_panel_aria}", "tooltip": "{lang.menu_panel_tooltip}", "memberOnly": true, "staffOnly": true}) if err != nil { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "cssClass": "menu_logout", "position": "left", "path": "/accounts/logout/?session={me.Session}", "aria": "{lang.menu_logout_aria}", "tooltip": "{lang.menu_logout_tooltip}", "memberOnly": true}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_logout}", "cssClass": "menu_logout", "position": "left", "path": "/accounts/logout/?session={me.Session}", "aria": "{lang.menu_logout_aria}", "tooltip": "{lang.menu_logout_tooltip}", "memberOnly": true}) if err != nil { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "cssClass": "menu_register", "position": "left", "path": "/accounts/create/", "aria": "{lang.menu_register_aria}", "tooltip": "{lang.menu_register_tooltip}", "guestOnly": true}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_register}", "cssClass": "menu_register", "position": "left", "path": "/accounts/create/", "aria": "{lang.menu_register_aria}", "tooltip": "{lang.menu_register_tooltip}", "guestOnly": true}) if err != nil { return err } - err = addMenuItem(map[string]interface{}{"mid": 1, "cssClass": "menu_login", "position": "left", "path": "/accounts/login/", "aria": "{lang.menu_login_aria}", "tooltip": "{lang.menu_login_tooltip}", "guestOnly": true}) + err = addMenuItem(map[string]interface{}{"mid": 1, "name": "{lang.menu_login}", "cssClass": "menu_login", "position": "left", "path": "/accounts/login/", "aria": "{lang.menu_login_aria}", "tooltip": "{lang.menu_login_tooltip}", "guestOnly": true}) if err != nil { return err } return nil } + +func patch1(scanner *bufio.Scanner) error { + return nil +} diff --git a/query_gen/lib/builder.go b/query_gen/lib/builder.go index a1a93edc..b8790908 100644 --- a/query_gen/lib/builder.go +++ b/query_gen/lib/builder.go @@ -96,6 +96,10 @@ func (build *builder) SimpleInnerJoin(table1 string, table2 string, columns stri return build.prepare(build.adapter.SimpleInnerJoin("_builder", table1, table2, columns, joiners, where, orderby, limit)) } +func (build *builder) DropTable(table string) (stmt *sql.Stmt, err error) { + return build.prepare(build.adapter.DropTable("_builder", table)) +} + func (build *builder) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (stmt *sql.Stmt, err error) { return build.prepare(build.adapter.CreateTable("_builder", table, charset, collation, columns, keys)) } diff --git a/query_gen/lib/mssql.go b/query_gen/lib/mssql.go index 4233273d..7951dd0d 100644 --- a/query_gen/lib/mssql.go +++ b/query_gen/lib/mssql.go @@ -44,6 +44,18 @@ func (adapter *MssqlAdapter) DbVersion() string { return "SELECT CONCAT(SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition'))" } +func (adapter *MssqlAdapter) DropTable(name string, table string) (string, error) { + if name == "" { + return "", errors.New("You need a name for this statement") + } + if table == "" { + return "", errors.New("You need a name for this table") + } + querystr := "DROP TABLE IF EXISTS [" + table + "];" + adapter.pushStatement(name, "drop-table", querystr) + return querystr, nil +} + // TODO: Convert any remaining stringy types to nvarchar // We may need to change the CreateTable API to better suit Mssql and the other database drivers which are coming up func (adapter *MssqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { diff --git a/query_gen/lib/mysql.go b/query_gen/lib/mysql.go index 79525eb3..2d05b469 100644 --- a/query_gen/lib/mysql.go +++ b/query_gen/lib/mysql.go @@ -61,6 +61,18 @@ func (adapter *MysqlAdapter) DbVersion() string { return "SELECT VERSION()" } +func (adapter *MysqlAdapter) DropTable(name string, table string) (string, error) { + if name == "" { + return "", errors.New("You need a name for this statement") + } + if table == "" { + return "", errors.New("You need a name for this table") + } + querystr := "DROP TABLE IF EXISTS `" + table + "`;" + adapter.pushStatement(name, "drop-table", querystr) + return querystr, nil +} + func (adapter *MysqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") diff --git a/query_gen/lib/pgsql.go b/query_gen/lib/pgsql.go index a5af9fdd..e0ff5fc1 100644 --- a/query_gen/lib/pgsql.go +++ b/query_gen/lib/pgsql.go @@ -38,9 +38,20 @@ func (adapter *PgsqlAdapter) BuildConn(config map[string]string) (*sql.DB, error return nil, nil } -// TODO: Implement this func (adapter *PgsqlAdapter) DbVersion() string { - return "" + return "SELECT version()" +} + +func (adapter *PgsqlAdapter) DropTable(name string, table string) (string, error) { + if name == "" { + return "", errors.New("You need a name for this statement") + } + if table == "" { + return "", errors.New("You need a name for this table") + } + querystr := "DROP TABLE IF EXISTS \"" + table + "\";" + adapter.pushStatement(name, "drop-table", querystr) + return querystr, nil } // TODO: Implement this diff --git a/query_gen/lib/querygen.go b/query_gen/lib/querygen.go index 5eac6431..31aa9ebc 100644 --- a/query_gen/lib/querygen.go +++ b/query_gen/lib/querygen.go @@ -98,11 +98,13 @@ type DBStmt struct { Type string // create-table, insert, update, delete } +// TODO: Add the DropTable, TableExists, AddColumn, ColumnExists, and RemoveColumn methods type Adapter interface { GetName() string BuildConn(config map[string]string) (*sql.DB, error) DbVersion() string + DropTable(name string, table string) (string, error) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) SimpleInsert(name string, table string, columns string, fields string) (string, error) SimpleUpdate(name string, table string, set string, where string) (string, error) diff --git a/query_gen/main.go b/query_gen/main.go index fbf08a24..f4f5fba5 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -210,47 +210,34 @@ func seedTables(adapter qgen.Adapter) error { qgen.Install.SimpleInsert("replies", "tid, content, parsed_content, createdAt, createdBy, lastUpdated, lastEdit, lastEditBy, ipaddress", "1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1'") - /* - Quick Reminder of the HTML layout, so I don't need to flip back and forth between menu_item.html, etc. - - {{range .MenuItems}} -