From 27a4a74840950143c1cd60ff1f155fc293774ec6 Mon Sep 17 00:00:00 2001 From: Azareal Date: Sat, 27 Apr 2019 16:32:26 +1000 Subject: [PATCH] You can now re-order forums by dragging them in the Forum Manager. Added some visual and textual hints to make it clearer that Menu Items and Forums can be dragged. Added a hint to flush the page after pushing the Added the notice client template and pushNotice client function. Used a pointer instead of a struct for AnalyticsTimeRange in the analytics routes. Caught a potential missing error check in InitPhrases. Use struct{} instead of bool in some of the user mapping maps for WebSockets to save space. Added the buildUserExprs function to eliminate a bit of duplication. Fixed a typo in ForumsEdit where it referenced a non-existent notice phrase. Client hooks can now sort of return things. Panel phrases are now fetched by init.js, but only in the control panel. Reduced the number of unused phrases loaded in both the front-end and the control panel. Plugin hyperdrive should handle Gzip better now. Added the panel.ForumsOrderSubmit route. Added the panel_hints_reorder phrase. Moved the panel_forums phrases into the panel. namespace. Added the panel.forums_order_updated phrase. Renamed the panel_themes_menus_item_edit_button_aria phrase to panel_themes_menus_items_edit_button_aria Renamed the panel_themes_menus_item_delete_button_aria phrase to panel_themes_menus_items_delete_button_aria Added the panel_themes_menus_items_update_button phrase. You will need to run the patcher / updater for this commit. --- cmd/query_gen/tables.go | 2 + common/forum.go | 19 +- common/forum_store.go | 27 +- common/phrases/phrases.go | 4 + common/routes_common.go | 2 + common/template_init.go | 6 + common/templates/templates.go | 61 +- common/websockets.go | 14 +- experimental/plugin_hyperdrive.go | 40 +- gen_router.go | 749 ++++++++++++------------ langs/english.json | 41 +- patcher/patches.go | 5 + public/global.js | 8 + public/init.js | 45 +- public/panel_forums.js | 59 ++ router_gen/routes.go | 1 + routes.go | 24 +- routes/panel/analytics.go | 5 +- routes/panel/forums.go | 29 +- schema/mssql/query_forums.sql | 1 + schema/mysql/query_forums.sql | 1 + schema/pgsql/query_forums.sql | 1 + templates/header.html | 4 +- templates/panel_forums.html | 41 +- templates/panel_themes_menus_items.html | 44 +- themes/cosora/public/panel.css | 6 + themes/nox/public/main.css | 5 + themes/nox/public/misc.js | 23 +- themes/nox/public/panel.css | 33 ++ themes/shadow/public/panel.css | 3 + themes/tempra_simple/public/panel.css | 6 + 31 files changed, 798 insertions(+), 511 deletions(-) create mode 100644 public/panel_forums.js diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 752e1057..06c18125 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -182,6 +182,7 @@ func createTables(adapter qgen.Adapter) error { tblColumn{"name", "varchar", 100, false, false, ""}, tblColumn{"desc", "varchar", 200, false, false, ""}, tblColumn{"active", "boolean", 0, false, false, "1"}, + tblColumn{"order", "int", 0, false, false, "0"}, tblColumn{"topicCount", "int", 0, false, false, "0"}, tblColumn{"preset", "varchar", 100, false, false, "''"}, tblColumn{"parentID", "int", 0, false, false, "0"}, @@ -427,6 +428,7 @@ func createTables(adapter qgen.Adapter) error { []tblColumn{ tblColumn{"uname", "varchar", 180, false, false, ""}, tblColumn{"default", "boolean", 0, false, false, "0"}, + //tblColumn{"profileUserVars", "text", 0, false, false, "''"}, }, []tblKey{ tblKey{"uname", "unique"}, diff --git a/common/forum.go b/common/forum.go index 2c6bb11e..6ca23a7f 100644 --- a/common/forum.go +++ b/common/forum.go @@ -1,7 +1,7 @@ package common -//import "fmt" import ( + //"log" "database/sql" "errors" "strconv" @@ -28,6 +28,7 @@ type Forum struct { Name string Desc string Active bool + Order int Preset string ParentID int ParentType string @@ -135,8 +136,22 @@ func (sf SortForum) Len() int { func (sf SortForum) Swap(i, j int) { sf[i], sf[j] = sf[j], sf[i] } +/*func (sf SortForum) Less(i,j int) bool { + l := sf.less(i,j) + if l { + log.Printf("%s is less than %s. order: %d. id: %d.",sf[i].Name, sf[j].Name, sf[i].Order, sf[i].ID) + } else { + log.Printf("%s is not less than %s. order: %d. id: %d.",sf[i].Name, sf[j].Name, sf[i].Order, sf[i].ID) + } + return l +}*/ func (sf SortForum) Less(i, j int) bool { - return sf[i].ID < sf[j].ID + if sf[i].Order < sf[j].Order { + return true + } else if sf[i].Order == sf[j].Order { + return sf[i].ID < sf[j].ID + } + return false } // ! Don't use this outside of tests and possibly template_init.go diff --git a/common/forum_store.go b/common/forum_store.go index 88661ecb..e14780a6 100644 --- a/common/forum_store.go +++ b/common/forum_store.go @@ -42,6 +42,7 @@ type ForumStore interface { //GetChildren(parentID int, parentType string) ([]*Forum,error) //GetFirstChild(parentID int, parentType string) (*Forum,error) Create(forumName string, forumDesc string, active bool, preset string) (int, error) + UpdateOrder(updateMap map[int]int) error GlobalCount() int } @@ -66,6 +67,7 @@ type MemoryForumStore struct { updateCache *sql.Stmt addTopics *sql.Stmt removeTopics *sql.Stmt + updateOrder *sql.Stmt } // NewMemoryForumStore gives you a new instance of MemoryForumStore @@ -73,17 +75,19 @@ func NewMemoryForumStore() (*MemoryForumStore, error) { acc := qgen.NewAcc() // TODO: Do a proper delete return &MemoryForumStore{ - get: acc.Select("forums").Columns("name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Where("fid = ?").Prepare(), - getAll: acc.Select("forums").Columns("fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Orderby("fid ASC").Prepare(), + get: acc.Select("forums").Columns("name, desc, active, order, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Where("fid = ?").Prepare(), + getAll: acc.Select("forums").Columns("fid, name, desc, active, order, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID").Orderby("order ASC, fid ASC").Prepare(), delete: acc.Update("forums").Set("name= '', active = 0").Where("fid = ?").Prepare(), create: acc.Insert("forums").Columns("name, desc, active, preset").Fields("?,?,?,?").Prepare(), count: acc.Count("forums").Where("name != ''").Prepare(), updateCache: acc.Update("forums").Set("lastTopicID = ?, lastReplyerID = ?").Where("fid = ?").Prepare(), addTopics: acc.Update("forums").Set("topicCount = topicCount + ?").Where("fid = ?").Prepare(), removeTopics: acc.Update("forums").Set("topicCount = topicCount - ?").Where("fid = ?").Prepare(), + updateOrder: acc.Update("forums").Set("order = ?").Where("fid = ?").Prepare(), }, acc.FirstError() } +// TODO: Rename to ReloadAll? // TODO: Add support for subforums func (mfs *MemoryForumStore) LoadForums() error { var forumView []*Forum @@ -103,7 +107,7 @@ func (mfs *MemoryForumStore) LoadForums() error { var i = 0 for ; rows.Next(); i++ { forum := &Forum{ID: 0, Active: true, Preset: "all"} - err = rows.Scan(&forum.ID, &forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) + err = rows.Scan(&forum.ID, &forum.Name, &forum.Desc, &forum.Active, &forum.Order, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) if err != nil { return err } @@ -161,7 +165,7 @@ func (mfs *MemoryForumStore) Get(id int) (*Forum, error) { fint, ok := mfs.forums.Load(id) if !ok || fint.(*Forum).Name == "" { var forum = &Forum{ID: id} - err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Order, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) if err != nil { return forum, err } @@ -178,7 +182,7 @@ func (mfs *MemoryForumStore) Get(id int) (*Forum, error) { func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) { var forum = &Forum{ID: id} - err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Order, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) if err != nil { return nil, err } @@ -206,7 +210,7 @@ func (mfs *MemoryForumStore) BulkGetCopy(ids []int) (forums []Forum, err error) func (mfs *MemoryForumStore) Reload(id int) error { var forum = &Forum{ID: id} - err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Order, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopicID, &forum.LastReplyerID) if err != nil { return err } @@ -348,6 +352,17 @@ func (mfs *MemoryForumStore) Create(forumName string, forumDesc string, active b return fid, nil } +// TODO: Make this atomic, maybe with a transaction? +func (s *MemoryForumStore) UpdateOrder(updateMap map[int]int) error { + for fid, order := range updateMap { + _, err := s.updateOrder.Exec(order, fid) + if err != nil { + return err + } + } + return s.LoadForums() +} + // ! Might be slightly inaccurate, if the sync.Map is constantly shifting and churning, but it'll stabilise eventually. Also, slow. Don't use this on every request x.x // Length returns the number of forums in the memory cache func (mfs *MemoryForumStore) Length() (length int) { diff --git a/common/phrases/phrases.go b/common/phrases/phrases.go index 1a4dfd23..e5625aba 100644 --- a/common/phrases/phrases.go +++ b/common/phrases/phrases.go @@ -39,6 +39,7 @@ type LevelPhrases struct { type LanguagePack struct { Name string IsoCode string + //LastUpdated string // Should we use a sync map or a struct for these? It would be nice, if we could keep all the phrases consistent. Levels LevelPhrases @@ -70,6 +71,9 @@ func InitPhrases(lang string) error { if f.IsDir() { return nil } + if err != nil { + return err + } data, err := ioutil.ReadFile(path) if err != nil { diff --git a/common/routes_common.go b/common/routes_common.go index b4a1fbee..61b56b40 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -158,6 +158,7 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header header.AddPreScriptAsync("template_" + name + tname + ".js") } addPreScript("alert") + addPreScript("notice") return header, stats, nil } @@ -267,6 +268,7 @@ func PrepResources(user *User, header *Header, theme *Theme) { addPreScript("topics_topic") addPreScript("paginator") addPreScript("alert") + addPreScript("notice") if user.Loggedin { addPreScript("topic_c_edit_post") addPreScript("topic_c_attach_item") diff --git a/common/template_init.go b/common/template_init.go index d6836d50..aa45d6a1 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -481,6 +481,8 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri tmpls.AddStd("topic_c_attach_item", "common.TopicCAttachItem", TopicCAttachItem{ID: 1, ImgSrc: "", Path: "", FullPath: ""}) + tmpls.AddStd("notice", "string", "nonono") + var dirPrefix = "./tmpl_client/" var writeTemplate = func(name string, content string) { log.Print("Writing template '" + name + "'") @@ -711,6 +713,10 @@ func initDefaultTmplFuncMap() { return "" } + fmap["flush"] = func() interface{} { + return nil + } + DefaultTemplateFuncMap = fmap } diff --git a/common/templates/templates.go b/common/templates/templates.go index 87fdc6c8..69dd2b74 100644 --- a/common/templates/templates.go +++ b/common/templates/templates.go @@ -74,6 +74,7 @@ type CTemplateSet struct { logger *log.Logger loggerf *os.File + lang string } func NewCTemplateSet(in string) *CTemplateSet { @@ -112,9 +113,11 @@ func NewCTemplateSet(in string) *CTemplateSet { "scope": true, "dyntmpl": true, "index": true, + "flush": true, }, logger: log.New(f, "", log.LstdFlags), loggerf: f, + lang:in, } } @@ -445,6 +448,16 @@ func (c *CTemplateSet) compile(name string, content string, expects string, expe return errors.New("invalid page struct value") } ` + if c.lang == "normal" { + fout += `var iw http.ResponseWriter + gzw, ok := w.(common.GzipResponseWriter) + if ok { + iw = gzw.ResponseWriter + } + _ = iw +` + } + if len(c.langIndexToName) > 0 { fout += "var plist = phrases.GetTmplPhrasesBytes(" + fname + "_tmpl_phrase_id)\n" } @@ -587,16 +600,7 @@ func (c *CTemplateSet) compileSwitch(con CContext, node parse.Node) { c.detail("Expression:", expr) // Simple member / guest optimisation for now // TODO: Expand upon this - var userExprs = []string{ - con.RootHolder + ".CurrentUser.Loggedin", - con.RootHolder + ".CurrentUser.IsSuperMod", - con.RootHolder + ".CurrentUser.IsAdmin", - } - var negUserExprs = []string{ - "!" + con.RootHolder + ".CurrentUser.Loggedin", - "!" + con.RootHolder + ".CurrentUser.IsSuperMod", - "!" + con.RootHolder + ".CurrentUser.IsAdmin", - } + userExprs, negUserExprs := buildUserExprs(con.RootHolder) if c.guestOnly { c.detail("optimising away member branch") if inSlice(userExprs, expr) { @@ -1170,6 +1174,16 @@ ArgLoop: out += "if err != nil {\nreturn err\n}\n}\n" literal = true break ArgLoop + case "flush": + if c.lang == "js" { + continue + } + out = "if fl, ok := iw.(http.Flusher); ok {\n" + out += "fl.Flush()\n" + out += "}\n" + literal = true + c.importMap["net/http"] = "net/http" + break ArgLoop default: c.detail("Variable!") if len(node.Args) > (pos + 1) { @@ -1391,6 +1405,20 @@ func (c *CTemplateSet) retCall(name string, params ...interface{}) { c.detail("returned from " + name + " => (" + pstr + ")") } +func buildUserExprs(holder string) ([]string,[]string) { + var userExprs = []string{ + holder + ".CurrentUser.Loggedin", + holder + ".CurrentUser.IsSuperMod", + holder + ".CurrentUser.IsAdmin", + } + var negUserExprs = []string{ + "!" + holder + ".CurrentUser.Loggedin", + "!" + holder + ".CurrentUser.IsSuperMod", + "!" + holder + ".CurrentUser.IsAdmin", + } + return userExprs, negUserExprs +} + func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.Value, assLines string, onEnd func(string) string) { c.dumpCall("compileVarSub", con, varname, val, assLines, onEnd) defer c.retCall("compileVarSub") @@ -1438,16 +1466,7 @@ func (c *CTemplateSet) compileVarSub(con CContext, varname string, val reflect.V // TODO: Take c.memberOnly into account // TODO: Make this a template fragment so more optimisations can be applied to this // TODO: De-duplicate this logic - var userExprs = []string{ - con.RootHolder + ".CurrentUser.Loggedin", - con.RootHolder + ".CurrentUser.IsSuperMod", - con.RootHolder + ".CurrentUser.IsAdmin", - } - var negUserExprs = []string{ - "!" + con.RootHolder + ".CurrentUser.Loggedin", - "!" + con.RootHolder + ".CurrentUser.IsSuperMod", - "!" + con.RootHolder + ".CurrentUser.IsAdmin", - } + userExprs, negUserExprs := buildUserExprs(con.RootHolder) if c.guestOnly { c.detail("optimising away member branch") if inSlice(userExprs, varname) { @@ -1804,4 +1823,4 @@ func (c *CTemplateSet) error(args ...interface{}) { func (c *CTemplateSet) critical(args ...interface{}) { c.logger.Println(args...) -} +} \ No newline at end of file diff --git a/common/websockets.go b/common/websockets.go index 1797941f..9aa898d2 100644 --- a/common/websockets.go +++ b/common/websockets.go @@ -33,8 +33,8 @@ var errWsNouser = errors.New("This user isn't connected via WebSockets") func init() { adminStatsWatchers = make(map[*websocket.Conn]*WSUser) - topicListWatchers = make(map[*WSUser]bool) - topicWatchers = make(map[int]map[*WSUser]bool) + topicListWatchers = make(map[*WSUser]struct{}) + topicWatchers = make(map[int]map[*WSUser]struct{}) } //easyjson:json @@ -130,7 +130,7 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) { // TODO: Optimise this to reduce the amount of contention case page == "/topics/": topicListMutex.Lock() - topicListWatchers[wsUser] = true + topicListWatchers[wsUser] = struct{}{} topicListMutex.Unlock() // TODO: Evict from page when permissions change? Or check user perms every-time before sending data? case strings.HasPrefix(page, "/topic/"): @@ -169,9 +169,9 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) { topicMutex.Lock() _, ok := topicWatchers[topic.ID] if !ok { - topicWatchers[topic.ID] = make(map[*WSUser]bool) + topicWatchers[topic.ID] = make(map[*WSUser]struct{}) } - topicWatchers[topic.ID][wsUser] = true + topicWatchers[topic.ID][wsUser] = struct{}{} topicMutex.Unlock() case page == "/panel/": if !wsUser.User.IsSuperMod { @@ -243,9 +243,9 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) { // TODO: Abstract this // TODO: Use odd-even sharding -var topicListWatchers map[*WSUser]bool +var topicListWatchers map[*WSUser]struct{} var topicListMutex sync.RWMutex -var topicWatchers map[int]map[*WSUser]bool // map[tid]watchers +var topicWatchers map[int]map[*WSUser]struct{} // map[tid]watchers var topicMutex sync.RWMutex var adminStatsWatchers map[*websocket.Conn]*WSUser var adminStatsMutex sync.RWMutex diff --git a/experimental/plugin_hyperdrive.go b/experimental/plugin_hyperdrive.go index fd767d87..1e9eda76 100644 --- a/experimental/plugin_hyperdrive.go +++ b/experimental/plugin_hyperdrive.go @@ -35,6 +35,7 @@ func deactivateHdrive(plugin *c.Plugin) { type Hyperspace struct { topicList atomic.Value + gzipTopicList atomic.Value } func newHyperspace() *Hyperspace { @@ -48,10 +49,7 @@ func tickHdriveWol(args ...interface{}) (skip bool, rerr c.RouteError) { return tickHdrive(args) } -// TODO: Find a better way of doing this -func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { - c.DebugLog("Refueling...") - w := httptest.NewRecorder() +func dummyReqHdrive() http.ResponseWriter { req := httptest.NewRequest("get", "/topics/", bytes.NewReader(nil)) user := c.GuestUser @@ -68,17 +66,48 @@ func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { } if w.Code != 200 { c.LogWarning(err) + return false, nil } + return w +} +// TODO: Find a better way of doing this +func tickHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { + c.DebugLog("Refueling...") + + w := httptest.NewRecorder() + dummyReqHdrive(w) buf := new(bytes.Buffer) buf.ReadFrom(w.Result().Body) hyperspace.topicList.Store(buf.Bytes()) + w = httptest.NewRecorder() + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Content-Type", "text/html; charset=utf-8") + gz := gzip.NewWriter(w) + w = c.GzipResponseWriter{Writer: gz, ResponseWriter: w} + + dummyReqHdrive(w) + buf = new(bytes.Buffer) + buf.ReadFrom(w.Result().Body) + hyperspace.gzipTopicList.Store(buf.Bytes()) + + if w.Header().Get("Content-Encoding") == "gzip" { + gz.Close() + } + return false, nil } func jumpHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { - tList := hyperspace.topicList.Load().([]byte) + var tList []byte + w := args[0].(http.ResponseWriter) + _, ok := w.(c.GzipResponseWriter) + if ok { + tList = hyperspace.gzipTopicList.Load().([]byte) + } else { + tList = hyperspace.topicList.Load().([]byte) + } if len(tList) == 0 { c.DebugLog("no topiclist in hyperspace") return false, nil @@ -101,7 +130,6 @@ func jumpHdrive(args ...interface{}) (skip bool, rerr c.RouteError) { //c.DebugLog c.DebugLog("Successful jump") - w := args[0].(http.ResponseWriter) header := args[3].(*c.Header) routes.FootHeaders(w, header) w.Write(tList) diff --git a/gen_router.go b/gen_router.go index b69dd796..38515f1e 100644 --- a/gen_router.go +++ b/gen_router.go @@ -42,6 +42,7 @@ var RouteMap = map[string]interface{}{ "panel.ForumsCreateSubmit": panel.ForumsCreateSubmit, "panel.ForumsDelete": panel.ForumsDelete, "panel.ForumsDeleteSubmit": panel.ForumsDeleteSubmit, + "panel.ForumsOrderSubmit": panel.ForumsOrderSubmit, "panel.ForumsEdit": panel.ForumsEdit, "panel.ForumsEditSubmit": panel.ForumsEditSubmit, "panel.ForumsEditPermsSubmit": panel.ForumsEditPermsSubmit, @@ -188,129 +189,130 @@ var routeMapEnum = map[string]int{ "panel.ForumsCreateSubmit": 16, "panel.ForumsDelete": 17, "panel.ForumsDeleteSubmit": 18, - "panel.ForumsEdit": 19, - "panel.ForumsEditSubmit": 20, - "panel.ForumsEditPermsSubmit": 21, - "panel.ForumsEditPermsAdvance": 22, - "panel.ForumsEditPermsAdvanceSubmit": 23, - "panel.Settings": 24, - "panel.SettingEdit": 25, - "panel.SettingEditSubmit": 26, - "panel.WordFilters": 27, - "panel.WordFiltersCreateSubmit": 28, - "panel.WordFiltersEdit": 29, - "panel.WordFiltersEditSubmit": 30, - "panel.WordFiltersDeleteSubmit": 31, - "panel.Pages": 32, - "panel.PagesCreateSubmit": 33, - "panel.PagesEdit": 34, - "panel.PagesEditSubmit": 35, - "panel.PagesDeleteSubmit": 36, - "panel.Themes": 37, - "panel.ThemesSetDefault": 38, - "panel.ThemesMenus": 39, - "panel.ThemesMenusEdit": 40, - "panel.ThemesMenuItemEdit": 41, - "panel.ThemesMenuItemEditSubmit": 42, - "panel.ThemesMenuItemCreateSubmit": 43, - "panel.ThemesMenuItemDeleteSubmit": 44, - "panel.ThemesMenuItemOrderSubmit": 45, - "panel.ThemesWidgets": 46, - "panel.ThemesWidgetsEditSubmit": 47, - "panel.ThemesWidgetsCreateSubmit": 48, - "panel.ThemesWidgetsDeleteSubmit": 49, - "panel.Plugins": 50, - "panel.PluginsActivate": 51, - "panel.PluginsDeactivate": 52, - "panel.PluginsInstall": 53, - "panel.Users": 54, - "panel.UsersEdit": 55, - "panel.UsersEditSubmit": 56, - "panel.AnalyticsViews": 57, - "panel.AnalyticsRoutes": 58, - "panel.AnalyticsAgents": 59, - "panel.AnalyticsSystems": 60, - "panel.AnalyticsLanguages": 61, - "panel.AnalyticsReferrers": 62, - "panel.AnalyticsRouteViews": 63, - "panel.AnalyticsAgentViews": 64, - "panel.AnalyticsForumViews": 65, - "panel.AnalyticsSystemViews": 66, - "panel.AnalyticsLanguageViews": 67, - "panel.AnalyticsReferrerViews": 68, - "panel.AnalyticsPosts": 69, - "panel.AnalyticsTopics": 70, - "panel.AnalyticsForums": 71, - "panel.Groups": 72, - "panel.GroupsEdit": 73, - "panel.GroupsEditPerms": 74, - "panel.GroupsEditSubmit": 75, - "panel.GroupsEditPermsSubmit": 76, - "panel.GroupsCreateSubmit": 77, - "panel.Backups": 78, - "panel.LogsRegs": 79, - "panel.LogsMod": 80, - "panel.Debug": 81, - "panel.Dashboard": 82, - "routes.AccountEdit": 83, - "routes.AccountEditPassword": 84, - "routes.AccountEditPasswordSubmit": 85, - "routes.AccountEditAvatarSubmit": 86, - "routes.AccountEditUsernameSubmit": 87, - "routes.AccountEditMFA": 88, - "routes.AccountEditMFASetup": 89, - "routes.AccountEditMFASetupSubmit": 90, - "routes.AccountEditMFADisableSubmit": 91, - "routes.AccountEditEmail": 92, - "routes.AccountEditEmailTokenSubmit": 93, - "routes.AccountLogins": 94, - "routes.LevelList": 95, - "routes.ViewProfile": 96, - "routes.BanUserSubmit": 97, - "routes.UnbanUser": 98, - "routes.ActivateUser": 99, - "routes.IPSearch": 100, - "routes.CreateTopicSubmit": 101, - "routes.EditTopicSubmit": 102, - "routes.DeleteTopicSubmit": 103, - "routes.StickTopicSubmit": 104, - "routes.UnstickTopicSubmit": 105, - "routes.LockTopicSubmit": 106, - "routes.UnlockTopicSubmit": 107, - "routes.MoveTopicSubmit": 108, - "routes.LikeTopicSubmit": 109, - "routes.AddAttachToTopicSubmit": 110, - "routes.RemoveAttachFromTopicSubmit": 111, - "routes.ViewTopic": 112, - "routes.CreateReplySubmit": 113, - "routes.ReplyEditSubmit": 114, - "routes.ReplyDeleteSubmit": 115, - "routes.ReplyLikeSubmit": 116, - "routes.AddAttachToReplySubmit": 117, - "routes.RemoveAttachFromReplySubmit": 118, - "routes.ProfileReplyCreateSubmit": 119, - "routes.ProfileReplyEditSubmit": 120, - "routes.ProfileReplyDeleteSubmit": 121, - "routes.PollVote": 122, - "routes.PollResults": 123, - "routes.AccountLogin": 124, - "routes.AccountRegister": 125, - "routes.AccountLogout": 126, - "routes.AccountLoginSubmit": 127, - "routes.AccountLoginMFAVerify": 128, - "routes.AccountLoginMFAVerifySubmit": 129, - "routes.AccountRegisterSubmit": 130, - "routes.AccountPasswordReset": 131, - "routes.AccountPasswordResetSubmit": 132, - "routes.AccountPasswordResetToken": 133, - "routes.AccountPasswordResetTokenSubmit": 134, - "routes.DynamicRoute": 135, - "routes.UploadedFile": 136, - "routes.StaticFile": 137, - "routes.RobotsTxt": 138, - "routes.SitemapXml": 139, - "routes.BadRoute": 140, - "routes.HTTPSRedirect": 141, + "panel.ForumsOrderSubmit": 19, + "panel.ForumsEdit": 20, + "panel.ForumsEditSubmit": 21, + "panel.ForumsEditPermsSubmit": 22, + "panel.ForumsEditPermsAdvance": 23, + "panel.ForumsEditPermsAdvanceSubmit": 24, + "panel.Settings": 25, + "panel.SettingEdit": 26, + "panel.SettingEditSubmit": 27, + "panel.WordFilters": 28, + "panel.WordFiltersCreateSubmit": 29, + "panel.WordFiltersEdit": 30, + "panel.WordFiltersEditSubmit": 31, + "panel.WordFiltersDeleteSubmit": 32, + "panel.Pages": 33, + "panel.PagesCreateSubmit": 34, + "panel.PagesEdit": 35, + "panel.PagesEditSubmit": 36, + "panel.PagesDeleteSubmit": 37, + "panel.Themes": 38, + "panel.ThemesSetDefault": 39, + "panel.ThemesMenus": 40, + "panel.ThemesMenusEdit": 41, + "panel.ThemesMenuItemEdit": 42, + "panel.ThemesMenuItemEditSubmit": 43, + "panel.ThemesMenuItemCreateSubmit": 44, + "panel.ThemesMenuItemDeleteSubmit": 45, + "panel.ThemesMenuItemOrderSubmit": 46, + "panel.ThemesWidgets": 47, + "panel.ThemesWidgetsEditSubmit": 48, + "panel.ThemesWidgetsCreateSubmit": 49, + "panel.ThemesWidgetsDeleteSubmit": 50, + "panel.Plugins": 51, + "panel.PluginsActivate": 52, + "panel.PluginsDeactivate": 53, + "panel.PluginsInstall": 54, + "panel.Users": 55, + "panel.UsersEdit": 56, + "panel.UsersEditSubmit": 57, + "panel.AnalyticsViews": 58, + "panel.AnalyticsRoutes": 59, + "panel.AnalyticsAgents": 60, + "panel.AnalyticsSystems": 61, + "panel.AnalyticsLanguages": 62, + "panel.AnalyticsReferrers": 63, + "panel.AnalyticsRouteViews": 64, + "panel.AnalyticsAgentViews": 65, + "panel.AnalyticsForumViews": 66, + "panel.AnalyticsSystemViews": 67, + "panel.AnalyticsLanguageViews": 68, + "panel.AnalyticsReferrerViews": 69, + "panel.AnalyticsPosts": 70, + "panel.AnalyticsTopics": 71, + "panel.AnalyticsForums": 72, + "panel.Groups": 73, + "panel.GroupsEdit": 74, + "panel.GroupsEditPerms": 75, + "panel.GroupsEditSubmit": 76, + "panel.GroupsEditPermsSubmit": 77, + "panel.GroupsCreateSubmit": 78, + "panel.Backups": 79, + "panel.LogsRegs": 80, + "panel.LogsMod": 81, + "panel.Debug": 82, + "panel.Dashboard": 83, + "routes.AccountEdit": 84, + "routes.AccountEditPassword": 85, + "routes.AccountEditPasswordSubmit": 86, + "routes.AccountEditAvatarSubmit": 87, + "routes.AccountEditUsernameSubmit": 88, + "routes.AccountEditMFA": 89, + "routes.AccountEditMFASetup": 90, + "routes.AccountEditMFASetupSubmit": 91, + "routes.AccountEditMFADisableSubmit": 92, + "routes.AccountEditEmail": 93, + "routes.AccountEditEmailTokenSubmit": 94, + "routes.AccountLogins": 95, + "routes.LevelList": 96, + "routes.ViewProfile": 97, + "routes.BanUserSubmit": 98, + "routes.UnbanUser": 99, + "routes.ActivateUser": 100, + "routes.IPSearch": 101, + "routes.CreateTopicSubmit": 102, + "routes.EditTopicSubmit": 103, + "routes.DeleteTopicSubmit": 104, + "routes.StickTopicSubmit": 105, + "routes.UnstickTopicSubmit": 106, + "routes.LockTopicSubmit": 107, + "routes.UnlockTopicSubmit": 108, + "routes.MoveTopicSubmit": 109, + "routes.LikeTopicSubmit": 110, + "routes.AddAttachToTopicSubmit": 111, + "routes.RemoveAttachFromTopicSubmit": 112, + "routes.ViewTopic": 113, + "routes.CreateReplySubmit": 114, + "routes.ReplyEditSubmit": 115, + "routes.ReplyDeleteSubmit": 116, + "routes.ReplyLikeSubmit": 117, + "routes.AddAttachToReplySubmit": 118, + "routes.RemoveAttachFromReplySubmit": 119, + "routes.ProfileReplyCreateSubmit": 120, + "routes.ProfileReplyEditSubmit": 121, + "routes.ProfileReplyDeleteSubmit": 122, + "routes.PollVote": 123, + "routes.PollResults": 124, + "routes.AccountLogin": 125, + "routes.AccountRegister": 126, + "routes.AccountLogout": 127, + "routes.AccountLoginSubmit": 128, + "routes.AccountLoginMFAVerify": 129, + "routes.AccountLoginMFAVerifySubmit": 130, + "routes.AccountRegisterSubmit": 131, + "routes.AccountPasswordReset": 132, + "routes.AccountPasswordResetSubmit": 133, + "routes.AccountPasswordResetToken": 134, + "routes.AccountPasswordResetTokenSubmit": 135, + "routes.DynamicRoute": 136, + "routes.UploadedFile": 137, + "routes.StaticFile": 138, + "routes.RobotsTxt": 139, + "routes.SitemapXml": 140, + "routes.BadRoute": 141, + "routes.HTTPSRedirect": 142, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -332,129 +334,130 @@ var reverseRouteMapEnum = map[int]string{ 16: "panel.ForumsCreateSubmit", 17: "panel.ForumsDelete", 18: "panel.ForumsDeleteSubmit", - 19: "panel.ForumsEdit", - 20: "panel.ForumsEditSubmit", - 21: "panel.ForumsEditPermsSubmit", - 22: "panel.ForumsEditPermsAdvance", - 23: "panel.ForumsEditPermsAdvanceSubmit", - 24: "panel.Settings", - 25: "panel.SettingEdit", - 26: "panel.SettingEditSubmit", - 27: "panel.WordFilters", - 28: "panel.WordFiltersCreateSubmit", - 29: "panel.WordFiltersEdit", - 30: "panel.WordFiltersEditSubmit", - 31: "panel.WordFiltersDeleteSubmit", - 32: "panel.Pages", - 33: "panel.PagesCreateSubmit", - 34: "panel.PagesEdit", - 35: "panel.PagesEditSubmit", - 36: "panel.PagesDeleteSubmit", - 37: "panel.Themes", - 38: "panel.ThemesSetDefault", - 39: "panel.ThemesMenus", - 40: "panel.ThemesMenusEdit", - 41: "panel.ThemesMenuItemEdit", - 42: "panel.ThemesMenuItemEditSubmit", - 43: "panel.ThemesMenuItemCreateSubmit", - 44: "panel.ThemesMenuItemDeleteSubmit", - 45: "panel.ThemesMenuItemOrderSubmit", - 46: "panel.ThemesWidgets", - 47: "panel.ThemesWidgetsEditSubmit", - 48: "panel.ThemesWidgetsCreateSubmit", - 49: "panel.ThemesWidgetsDeleteSubmit", - 50: "panel.Plugins", - 51: "panel.PluginsActivate", - 52: "panel.PluginsDeactivate", - 53: "panel.PluginsInstall", - 54: "panel.Users", - 55: "panel.UsersEdit", - 56: "panel.UsersEditSubmit", - 57: "panel.AnalyticsViews", - 58: "panel.AnalyticsRoutes", - 59: "panel.AnalyticsAgents", - 60: "panel.AnalyticsSystems", - 61: "panel.AnalyticsLanguages", - 62: "panel.AnalyticsReferrers", - 63: "panel.AnalyticsRouteViews", - 64: "panel.AnalyticsAgentViews", - 65: "panel.AnalyticsForumViews", - 66: "panel.AnalyticsSystemViews", - 67: "panel.AnalyticsLanguageViews", - 68: "panel.AnalyticsReferrerViews", - 69: "panel.AnalyticsPosts", - 70: "panel.AnalyticsTopics", - 71: "panel.AnalyticsForums", - 72: "panel.Groups", - 73: "panel.GroupsEdit", - 74: "panel.GroupsEditPerms", - 75: "panel.GroupsEditSubmit", - 76: "panel.GroupsEditPermsSubmit", - 77: "panel.GroupsCreateSubmit", - 78: "panel.Backups", - 79: "panel.LogsRegs", - 80: "panel.LogsMod", - 81: "panel.Debug", - 82: "panel.Dashboard", - 83: "routes.AccountEdit", - 84: "routes.AccountEditPassword", - 85: "routes.AccountEditPasswordSubmit", - 86: "routes.AccountEditAvatarSubmit", - 87: "routes.AccountEditUsernameSubmit", - 88: "routes.AccountEditMFA", - 89: "routes.AccountEditMFASetup", - 90: "routes.AccountEditMFASetupSubmit", - 91: "routes.AccountEditMFADisableSubmit", - 92: "routes.AccountEditEmail", - 93: "routes.AccountEditEmailTokenSubmit", - 94: "routes.AccountLogins", - 95: "routes.LevelList", - 96: "routes.ViewProfile", - 97: "routes.BanUserSubmit", - 98: "routes.UnbanUser", - 99: "routes.ActivateUser", - 100: "routes.IPSearch", - 101: "routes.CreateTopicSubmit", - 102: "routes.EditTopicSubmit", - 103: "routes.DeleteTopicSubmit", - 104: "routes.StickTopicSubmit", - 105: "routes.UnstickTopicSubmit", - 106: "routes.LockTopicSubmit", - 107: "routes.UnlockTopicSubmit", - 108: "routes.MoveTopicSubmit", - 109: "routes.LikeTopicSubmit", - 110: "routes.AddAttachToTopicSubmit", - 111: "routes.RemoveAttachFromTopicSubmit", - 112: "routes.ViewTopic", - 113: "routes.CreateReplySubmit", - 114: "routes.ReplyEditSubmit", - 115: "routes.ReplyDeleteSubmit", - 116: "routes.ReplyLikeSubmit", - 117: "routes.AddAttachToReplySubmit", - 118: "routes.RemoveAttachFromReplySubmit", - 119: "routes.ProfileReplyCreateSubmit", - 120: "routes.ProfileReplyEditSubmit", - 121: "routes.ProfileReplyDeleteSubmit", - 122: "routes.PollVote", - 123: "routes.PollResults", - 124: "routes.AccountLogin", - 125: "routes.AccountRegister", - 126: "routes.AccountLogout", - 127: "routes.AccountLoginSubmit", - 128: "routes.AccountLoginMFAVerify", - 129: "routes.AccountLoginMFAVerifySubmit", - 130: "routes.AccountRegisterSubmit", - 131: "routes.AccountPasswordReset", - 132: "routes.AccountPasswordResetSubmit", - 133: "routes.AccountPasswordResetToken", - 134: "routes.AccountPasswordResetTokenSubmit", - 135: "routes.DynamicRoute", - 136: "routes.UploadedFile", - 137: "routes.StaticFile", - 138: "routes.RobotsTxt", - 139: "routes.SitemapXml", - 140: "routes.BadRoute", - 141: "routes.HTTPSRedirect", + 19: "panel.ForumsOrderSubmit", + 20: "panel.ForumsEdit", + 21: "panel.ForumsEditSubmit", + 22: "panel.ForumsEditPermsSubmit", + 23: "panel.ForumsEditPermsAdvance", + 24: "panel.ForumsEditPermsAdvanceSubmit", + 25: "panel.Settings", + 26: "panel.SettingEdit", + 27: "panel.SettingEditSubmit", + 28: "panel.WordFilters", + 29: "panel.WordFiltersCreateSubmit", + 30: "panel.WordFiltersEdit", + 31: "panel.WordFiltersEditSubmit", + 32: "panel.WordFiltersDeleteSubmit", + 33: "panel.Pages", + 34: "panel.PagesCreateSubmit", + 35: "panel.PagesEdit", + 36: "panel.PagesEditSubmit", + 37: "panel.PagesDeleteSubmit", + 38: "panel.Themes", + 39: "panel.ThemesSetDefault", + 40: "panel.ThemesMenus", + 41: "panel.ThemesMenusEdit", + 42: "panel.ThemesMenuItemEdit", + 43: "panel.ThemesMenuItemEditSubmit", + 44: "panel.ThemesMenuItemCreateSubmit", + 45: "panel.ThemesMenuItemDeleteSubmit", + 46: "panel.ThemesMenuItemOrderSubmit", + 47: "panel.ThemesWidgets", + 48: "panel.ThemesWidgetsEditSubmit", + 49: "panel.ThemesWidgetsCreateSubmit", + 50: "panel.ThemesWidgetsDeleteSubmit", + 51: "panel.Plugins", + 52: "panel.PluginsActivate", + 53: "panel.PluginsDeactivate", + 54: "panel.PluginsInstall", + 55: "panel.Users", + 56: "panel.UsersEdit", + 57: "panel.UsersEditSubmit", + 58: "panel.AnalyticsViews", + 59: "panel.AnalyticsRoutes", + 60: "panel.AnalyticsAgents", + 61: "panel.AnalyticsSystems", + 62: "panel.AnalyticsLanguages", + 63: "panel.AnalyticsReferrers", + 64: "panel.AnalyticsRouteViews", + 65: "panel.AnalyticsAgentViews", + 66: "panel.AnalyticsForumViews", + 67: "panel.AnalyticsSystemViews", + 68: "panel.AnalyticsLanguageViews", + 69: "panel.AnalyticsReferrerViews", + 70: "panel.AnalyticsPosts", + 71: "panel.AnalyticsTopics", + 72: "panel.AnalyticsForums", + 73: "panel.Groups", + 74: "panel.GroupsEdit", + 75: "panel.GroupsEditPerms", + 76: "panel.GroupsEditSubmit", + 77: "panel.GroupsEditPermsSubmit", + 78: "panel.GroupsCreateSubmit", + 79: "panel.Backups", + 80: "panel.LogsRegs", + 81: "panel.LogsMod", + 82: "panel.Debug", + 83: "panel.Dashboard", + 84: "routes.AccountEdit", + 85: "routes.AccountEditPassword", + 86: "routes.AccountEditPasswordSubmit", + 87: "routes.AccountEditAvatarSubmit", + 88: "routes.AccountEditUsernameSubmit", + 89: "routes.AccountEditMFA", + 90: "routes.AccountEditMFASetup", + 91: "routes.AccountEditMFASetupSubmit", + 92: "routes.AccountEditMFADisableSubmit", + 93: "routes.AccountEditEmail", + 94: "routes.AccountEditEmailTokenSubmit", + 95: "routes.AccountLogins", + 96: "routes.LevelList", + 97: "routes.ViewProfile", + 98: "routes.BanUserSubmit", + 99: "routes.UnbanUser", + 100: "routes.ActivateUser", + 101: "routes.IPSearch", + 102: "routes.CreateTopicSubmit", + 103: "routes.EditTopicSubmit", + 104: "routes.DeleteTopicSubmit", + 105: "routes.StickTopicSubmit", + 106: "routes.UnstickTopicSubmit", + 107: "routes.LockTopicSubmit", + 108: "routes.UnlockTopicSubmit", + 109: "routes.MoveTopicSubmit", + 110: "routes.LikeTopicSubmit", + 111: "routes.AddAttachToTopicSubmit", + 112: "routes.RemoveAttachFromTopicSubmit", + 113: "routes.ViewTopic", + 114: "routes.CreateReplySubmit", + 115: "routes.ReplyEditSubmit", + 116: "routes.ReplyDeleteSubmit", + 117: "routes.ReplyLikeSubmit", + 118: "routes.AddAttachToReplySubmit", + 119: "routes.RemoveAttachFromReplySubmit", + 120: "routes.ProfileReplyCreateSubmit", + 121: "routes.ProfileReplyEditSubmit", + 122: "routes.ProfileReplyDeleteSubmit", + 123: "routes.PollVote", + 124: "routes.PollResults", + 125: "routes.AccountLogin", + 126: "routes.AccountRegister", + 127: "routes.AccountLogout", + 128: "routes.AccountLoginSubmit", + 129: "routes.AccountLoginMFAVerify", + 130: "routes.AccountLoginMFAVerifySubmit", + 131: "routes.AccountRegisterSubmit", + 132: "routes.AccountPasswordReset", + 133: "routes.AccountPasswordResetSubmit", + 134: "routes.AccountPasswordResetToken", + 135: "routes.AccountPasswordResetTokenSubmit", + 136: "routes.DynamicRoute", + 137: "routes.UploadedFile", + 138: "routes.StaticFile", + 139: "routes.RobotsTxt", + 140: "routes.SitemapXml", + 141: "routes.BadRoute", + 142: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -609,7 +612,7 @@ type HTTPSRedirect struct { func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - counters.RouteViewCounter.Bump(141) + counters.RouteViewCounter.Bump(142) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -813,7 +816,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.GlobalViewCounter.Bump() if prefix == "/static" { - counters.RouteViewCounter.Bump(137) + counters.RouteViewCounter.Bump(138) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -1169,8 +1172,16 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c counters.RouteViewCounter.Bump(18) err = panel.ForumsDeleteSubmit(w,req,user,extraData) - case "/panel/forums/edit/": + case "/panel/forums/order/edit/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + counters.RouteViewCounter.Bump(19) + err = panel.ForumsOrderSubmit(w,req,user) + case "/panel/forums/edit/": + counters.RouteViewCounter.Bump(20) err = panel.ForumsEdit(w,req,user,extraData) case "/panel/forums/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1178,7 +1189,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(20) + counters.RouteViewCounter.Bump(21) err = panel.ForumsEditSubmit(w,req,user,extraData) case "/panel/forums/edit/perms/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1186,10 +1197,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(21) + counters.RouteViewCounter.Bump(22) err = panel.ForumsEditPermsSubmit(w,req,user,extraData) case "/panel/forums/edit/perms/": - counters.RouteViewCounter.Bump(22) + counters.RouteViewCounter.Bump(23) err = panel.ForumsEditPermsAdvance(w,req,user,extraData) case "/panel/forums/edit/perms/adv/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1197,13 +1208,13 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(23) + counters.RouteViewCounter.Bump(24) err = panel.ForumsEditPermsAdvanceSubmit(w,req,user,extraData) case "/panel/settings/": - counters.RouteViewCounter.Bump(24) + counters.RouteViewCounter.Bump(25) err = panel.Settings(w,req,user) case "/panel/settings/edit/": - counters.RouteViewCounter.Bump(25) + counters.RouteViewCounter.Bump(26) err = panel.SettingEdit(w,req,user,extraData) case "/panel/settings/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1211,10 +1222,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(26) + counters.RouteViewCounter.Bump(27) err = panel.SettingEditSubmit(w,req,user,extraData) case "/panel/settings/word-filters/": - counters.RouteViewCounter.Bump(27) + counters.RouteViewCounter.Bump(28) err = panel.WordFilters(w,req,user) case "/panel/settings/word-filters/create/": err = c.NoSessionMismatch(w,req,user) @@ -1222,10 +1233,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(28) + counters.RouteViewCounter.Bump(29) err = panel.WordFiltersCreateSubmit(w,req,user) case "/panel/settings/word-filters/edit/": - counters.RouteViewCounter.Bump(29) + counters.RouteViewCounter.Bump(30) err = panel.WordFiltersEdit(w,req,user,extraData) case "/panel/settings/word-filters/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1233,7 +1244,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(30) + counters.RouteViewCounter.Bump(31) err = panel.WordFiltersEditSubmit(w,req,user,extraData) case "/panel/settings/word-filters/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1241,7 +1252,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(31) + counters.RouteViewCounter.Bump(32) err = panel.WordFiltersDeleteSubmit(w,req,user,extraData) case "/panel/pages/": err = c.AdminOnly(w,req,user) @@ -1249,7 +1260,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(32) + counters.RouteViewCounter.Bump(33) err = panel.Pages(w,req,user) case "/panel/pages/create/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1262,7 +1273,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(33) + counters.RouteViewCounter.Bump(34) err = panel.PagesCreateSubmit(w,req,user) case "/panel/pages/edit/": err = c.AdminOnly(w,req,user) @@ -1270,7 +1281,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(34) + counters.RouteViewCounter.Bump(35) err = panel.PagesEdit(w,req,user,extraData) case "/panel/pages/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1283,7 +1294,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(35) + counters.RouteViewCounter.Bump(36) err = panel.PagesEditSubmit(w,req,user,extraData) case "/panel/pages/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1296,10 +1307,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(36) + counters.RouteViewCounter.Bump(37) err = panel.PagesDeleteSubmit(w,req,user,extraData) case "/panel/themes/": - counters.RouteViewCounter.Bump(37) + counters.RouteViewCounter.Bump(38) err = panel.Themes(w,req,user) case "/panel/themes/default/": err = c.NoSessionMismatch(w,req,user) @@ -1307,16 +1318,16 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(38) + counters.RouteViewCounter.Bump(39) err = panel.ThemesSetDefault(w,req,user,extraData) case "/panel/themes/menus/": - counters.RouteViewCounter.Bump(39) + counters.RouteViewCounter.Bump(40) err = panel.ThemesMenus(w,req,user) case "/panel/themes/menus/edit/": - counters.RouteViewCounter.Bump(40) + counters.RouteViewCounter.Bump(41) err = panel.ThemesMenusEdit(w,req,user,extraData) case "/panel/themes/menus/item/edit/": - counters.RouteViewCounter.Bump(41) + counters.RouteViewCounter.Bump(42) err = panel.ThemesMenuItemEdit(w,req,user,extraData) case "/panel/themes/menus/item/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1324,7 +1335,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(42) + counters.RouteViewCounter.Bump(43) err = panel.ThemesMenuItemEditSubmit(w,req,user,extraData) case "/panel/themes/menus/item/create/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1332,7 +1343,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(43) + counters.RouteViewCounter.Bump(44) err = panel.ThemesMenuItemCreateSubmit(w,req,user) case "/panel/themes/menus/item/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1340,7 +1351,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(44) + counters.RouteViewCounter.Bump(45) err = panel.ThemesMenuItemDeleteSubmit(w,req,user,extraData) case "/panel/themes/menus/item/order/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1348,10 +1359,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(45) + counters.RouteViewCounter.Bump(46) err = panel.ThemesMenuItemOrderSubmit(w,req,user,extraData) case "/panel/themes/widgets/": - counters.RouteViewCounter.Bump(46) + counters.RouteViewCounter.Bump(47) err = panel.ThemesWidgets(w,req,user) case "/panel/themes/widgets/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1359,7 +1370,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(47) + counters.RouteViewCounter.Bump(48) err = panel.ThemesWidgetsEditSubmit(w,req,user,extraData) case "/panel/themes/widgets/create/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1367,7 +1378,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(48) + counters.RouteViewCounter.Bump(49) err = panel.ThemesWidgetsCreateSubmit(w,req,user) case "/panel/themes/widgets/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1375,10 +1386,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(49) + counters.RouteViewCounter.Bump(50) err = panel.ThemesWidgetsDeleteSubmit(w,req,user,extraData) case "/panel/plugins/": - counters.RouteViewCounter.Bump(50) + counters.RouteViewCounter.Bump(51) err = panel.Plugins(w,req,user) case "/panel/plugins/activate/": err = c.NoSessionMismatch(w,req,user) @@ -1386,7 +1397,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(51) + counters.RouteViewCounter.Bump(52) err = panel.PluginsActivate(w,req,user,extraData) case "/panel/plugins/deactivate/": err = c.NoSessionMismatch(w,req,user) @@ -1394,7 +1405,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(52) + counters.RouteViewCounter.Bump(53) err = panel.PluginsDeactivate(w,req,user,extraData) case "/panel/plugins/install/": err = c.NoSessionMismatch(w,req,user) @@ -1402,13 +1413,13 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(53) + counters.RouteViewCounter.Bump(54) err = panel.PluginsInstall(w,req,user,extraData) case "/panel/users/": - counters.RouteViewCounter.Bump(54) + counters.RouteViewCounter.Bump(55) err = panel.Users(w,req,user) case "/panel/users/edit/": - counters.RouteViewCounter.Bump(55) + counters.RouteViewCounter.Bump(56) err = panel.UsersEdit(w,req,user,extraData) case "/panel/users/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1416,7 +1427,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(56) + counters.RouteViewCounter.Bump(57) err = panel.UsersEditSubmit(w,req,user,extraData) case "/panel/analytics/views/": err = c.ParseForm(w,req,user) @@ -1424,7 +1435,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(57) + counters.RouteViewCounter.Bump(58) err = panel.AnalyticsViews(w,req,user) case "/panel/analytics/routes/": err = c.ParseForm(w,req,user) @@ -1432,7 +1443,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(58) + counters.RouteViewCounter.Bump(59) err = panel.AnalyticsRoutes(w,req,user) case "/panel/analytics/agents/": err = c.ParseForm(w,req,user) @@ -1440,7 +1451,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(59) + counters.RouteViewCounter.Bump(60) err = panel.AnalyticsAgents(w,req,user) case "/panel/analytics/systems/": err = c.ParseForm(w,req,user) @@ -1448,7 +1459,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(60) + counters.RouteViewCounter.Bump(61) err = panel.AnalyticsSystems(w,req,user) case "/panel/analytics/langs/": err = c.ParseForm(w,req,user) @@ -1456,7 +1467,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(61) + counters.RouteViewCounter.Bump(62) err = panel.AnalyticsLanguages(w,req,user) case "/panel/analytics/referrers/": err = c.ParseForm(w,req,user) @@ -1464,25 +1475,25 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(62) + counters.RouteViewCounter.Bump(63) err = panel.AnalyticsReferrers(w,req,user) case "/panel/analytics/route/": - counters.RouteViewCounter.Bump(63) + counters.RouteViewCounter.Bump(64) err = panel.AnalyticsRouteViews(w,req,user,extraData) case "/panel/analytics/agent/": - counters.RouteViewCounter.Bump(64) + counters.RouteViewCounter.Bump(65) err = panel.AnalyticsAgentViews(w,req,user,extraData) case "/panel/analytics/forum/": - counters.RouteViewCounter.Bump(65) + counters.RouteViewCounter.Bump(66) err = panel.AnalyticsForumViews(w,req,user,extraData) case "/panel/analytics/system/": - counters.RouteViewCounter.Bump(66) + counters.RouteViewCounter.Bump(67) err = panel.AnalyticsSystemViews(w,req,user,extraData) case "/panel/analytics/lang/": - counters.RouteViewCounter.Bump(67) + counters.RouteViewCounter.Bump(68) err = panel.AnalyticsLanguageViews(w,req,user,extraData) case "/panel/analytics/referrer/": - counters.RouteViewCounter.Bump(68) + counters.RouteViewCounter.Bump(69) err = panel.AnalyticsReferrerViews(w,req,user,extraData) case "/panel/analytics/posts/": err = c.ParseForm(w,req,user) @@ -1490,7 +1501,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(69) + counters.RouteViewCounter.Bump(70) err = panel.AnalyticsPosts(w,req,user) case "/panel/analytics/topics/": err = c.ParseForm(w,req,user) @@ -1498,7 +1509,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(70) + counters.RouteViewCounter.Bump(71) err = panel.AnalyticsTopics(w,req,user) case "/panel/analytics/forums/": err = c.ParseForm(w,req,user) @@ -1506,16 +1517,16 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(71) + counters.RouteViewCounter.Bump(72) err = panel.AnalyticsForums(w,req,user) case "/panel/groups/": - counters.RouteViewCounter.Bump(72) + counters.RouteViewCounter.Bump(73) err = panel.Groups(w,req,user) case "/panel/groups/edit/": - counters.RouteViewCounter.Bump(73) + counters.RouteViewCounter.Bump(74) err = panel.GroupsEdit(w,req,user,extraData) case "/panel/groups/edit/perms/": - counters.RouteViewCounter.Bump(74) + counters.RouteViewCounter.Bump(75) err = panel.GroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1523,7 +1534,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(75) + counters.RouteViewCounter.Bump(76) err = panel.GroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1531,7 +1542,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(76) + counters.RouteViewCounter.Bump(77) err = panel.GroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = c.NoSessionMismatch(w,req,user) @@ -1539,7 +1550,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(77) + counters.RouteViewCounter.Bump(78) err = panel.GroupsCreateSubmit(w,req,user) case "/panel/backups/": err = c.SuperAdminOnly(w,req,user) @@ -1553,13 +1564,13 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c w.Header().Del("Content-Type") w.Header().Del("Content-Encoding") } - counters.RouteViewCounter.Bump(78) + counters.RouteViewCounter.Bump(79) err = panel.Backups(w,req,user,extraData) case "/panel/logs/regs/": - counters.RouteViewCounter.Bump(79) + counters.RouteViewCounter.Bump(80) err = panel.LogsRegs(w,req,user) case "/panel/logs/mod/": - counters.RouteViewCounter.Bump(80) + counters.RouteViewCounter.Bump(81) err = panel.LogsMod(w,req,user) case "/panel/debug/": err = c.AdminOnly(w,req,user) @@ -1567,10 +1578,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(81) + counters.RouteViewCounter.Bump(82) err = panel.Debug(w,req,user) default: - counters.RouteViewCounter.Bump(82) + counters.RouteViewCounter.Bump(83) err = panel.Dashboard(w,req,user) } case "/user": @@ -1581,7 +1592,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(83) + counters.RouteViewCounter.Bump(84) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1593,7 +1604,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(84) + counters.RouteViewCounter.Bump(85) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1610,7 +1621,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(85) + counters.RouteViewCounter.Bump(86) err = routes.AccountEditPasswordSubmit(w,req,user) case "/user/edit/avatar/submit/": err = c.MemberOnly(w,req,user) @@ -1627,7 +1638,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(86) + counters.RouteViewCounter.Bump(87) err = routes.AccountEditAvatarSubmit(w,req,user) case "/user/edit/username/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1640,7 +1651,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(87) + counters.RouteViewCounter.Bump(88) err = routes.AccountEditUsernameSubmit(w,req,user) case "/user/edit/mfa/": err = c.MemberOnly(w,req,user) @@ -1648,7 +1659,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(88) + counters.RouteViewCounter.Bump(89) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1660,7 +1671,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(89) + counters.RouteViewCounter.Bump(90) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1677,7 +1688,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(90) + counters.RouteViewCounter.Bump(91) err = routes.AccountEditMFASetupSubmit(w,req,user) case "/user/edit/mfa/disable/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1690,7 +1701,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(91) + counters.RouteViewCounter.Bump(92) err = routes.AccountEditMFADisableSubmit(w,req,user) case "/user/edit/email/": err = c.MemberOnly(w,req,user) @@ -1698,14 +1709,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(92) + counters.RouteViewCounter.Bump(93) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountEditEmail(w,req,user,head) case "/user/edit/token/": - counters.RouteViewCounter.Bump(93) + counters.RouteViewCounter.Bump(94) err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData) case "/user/edit/logins/": err = c.MemberOnly(w,req,user) @@ -1713,7 +1724,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(94) + counters.RouteViewCounter.Bump(95) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1725,7 +1736,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(95) + counters.RouteViewCounter.Bump(96) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1733,7 +1744,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c err = routes.LevelList(w,req,user,head) default: req.URL.Path += extraData - counters.RouteViewCounter.Bump(96) + counters.RouteViewCounter.Bump(97) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1753,7 +1764,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(97) + counters.RouteViewCounter.Bump(98) err = routes.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = c.NoSessionMismatch(w,req,user) @@ -1766,7 +1777,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(98) + counters.RouteViewCounter.Bump(99) err = routes.UnbanUser(w,req,user,extraData) case "/users/activate/": err = c.NoSessionMismatch(w,req,user) @@ -1779,7 +1790,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(99) + counters.RouteViewCounter.Bump(100) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = c.MemberOnly(w,req,user) @@ -1787,7 +1798,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(100) + counters.RouteViewCounter.Bump(101) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1811,7 +1822,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(101) + counters.RouteViewCounter.Bump(102) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1824,7 +1835,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(102) + counters.RouteViewCounter.Bump(103) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1838,7 +1849,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(103) + counters.RouteViewCounter.Bump(104) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1851,7 +1862,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(104) + counters.RouteViewCounter.Bump(105) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1864,7 +1875,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(105) + counters.RouteViewCounter.Bump(106) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1878,7 +1889,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(106) + counters.RouteViewCounter.Bump(107) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1891,7 +1902,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(107) + counters.RouteViewCounter.Bump(108) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1904,7 +1915,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(108) + counters.RouteViewCounter.Bump(109) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1917,7 +1928,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(109) + counters.RouteViewCounter.Bump(110) err = routes.LikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -1934,7 +1945,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(110) + counters.RouteViewCounter.Bump(111) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1947,10 +1958,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(111) + counters.RouteViewCounter.Bump(112) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - counters.RouteViewCounter.Bump(112) + counters.RouteViewCounter.Bump(113) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1974,7 +1985,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(113) + counters.RouteViewCounter.Bump(114) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1987,7 +1998,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(114) + counters.RouteViewCounter.Bump(115) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2000,7 +2011,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(115) + counters.RouteViewCounter.Bump(116) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2013,7 +2024,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(116) + counters.RouteViewCounter.Bump(117) err = routes.ReplyLikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2030,7 +2041,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(117) + counters.RouteViewCounter.Bump(118) err = routes.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2043,7 +2054,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(118) + counters.RouteViewCounter.Bump(119) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2059,7 +2070,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(119) + counters.RouteViewCounter.Bump(120) err = routes.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2072,7 +2083,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(120) + counters.RouteViewCounter.Bump(121) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2085,7 +2096,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(121) + counters.RouteViewCounter.Bump(122) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2101,23 +2112,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(122) + counters.RouteViewCounter.Bump(123) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - counters.RouteViewCounter.Bump(123) + counters.RouteViewCounter.Bump(124) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - counters.RouteViewCounter.Bump(124) + counters.RouteViewCounter.Bump(125) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountLogin(w,req,user,head) case "/accounts/create/": - counters.RouteViewCounter.Bump(125) + counters.RouteViewCounter.Bump(126) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2134,7 +2145,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(126) + counters.RouteViewCounter.Bump(127) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2142,10 +2153,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(127) + counters.RouteViewCounter.Bump(128) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - counters.RouteViewCounter.Bump(128) + counters.RouteViewCounter.Bump(129) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2157,7 +2168,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(129) + counters.RouteViewCounter.Bump(130) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2165,10 +2176,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(130) + counters.RouteViewCounter.Bump(131) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - counters.RouteViewCounter.Bump(131) + counters.RouteViewCounter.Bump(132) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2180,10 +2191,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(132) + counters.RouteViewCounter.Bump(133) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - counters.RouteViewCounter.Bump(133) + counters.RouteViewCounter.Bump(134) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2195,7 +2206,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(134) + counters.RouteViewCounter.Bump(135) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2211,7 +2222,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c w.Header().Del("Content-Type") w.Header().Del("Content-Encoding") } - counters.RouteViewCounter.Bump(136) + counters.RouteViewCounter.Bump(137) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2221,7 +2232,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c // TODO: Add support for favicons and robots.txt files switch(extraData) { case "robots.txt": - counters.RouteViewCounter.Bump(138) + counters.RouteViewCounter.Bump(139) return routes.RobotsTxt(w,req) case "favicon.ico": req.URL.Path = "/static"+req.URL.Path+extraData @@ -2229,7 +2240,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c routes.StaticFile(w,req) return nil /*case "sitemap.xml": - counters.RouteViewCounter.Bump(139) + counters.RouteViewCounter.Bump(140) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2240,7 +2251,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - counters.RouteViewCounter.Bump(135) // TODO: Be more specific about *which* dynamic route it is + counters.RouteViewCounter.Bump(136) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2251,7 +2262,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - counters.RouteViewCounter.Bump(140) + counters.RouteViewCounter.Bump(141) return c.NotFound(w,req,nil) } return err diff --git a/langs/english.json b/langs/english.json index b04e481b..de6efdf7 100644 --- a/langs/english.json +++ b/langs/english.json @@ -715,6 +715,8 @@ "option_yes":"Yes", "option_no":"No", + "panel_hints_reorder":"Drag to change the order", + "panel_back_to_site":"Back to Site", "panel_welcome":"Welcome ", "panel_menu_head":"Control Panel", @@ -769,22 +771,24 @@ "panel_user_group":"Group", "panel_user_update_button":"Update User", - "panel_forums_head":"Forums", - "panel_forums_hidden":"Hidden", - "panel_forums_edit_button_tooltip":"Edit Forum", - "panel_forums_edit_button_aria":"Edit Forum", - "panel_forums_update_button":"Update", - "panel_forums_delete_button_tooltip":"Delete Forum", - "panel_forums_delete_button_aria":"Delete Forum", - "panel_forums_full_edit_button":"Full Edit", - "panel_forums_create_head":"Add Forum", - "panel_forums_create_name_label":"Name", - "panel_forums_create_name":"Super Secret Forum", - "panel_forums_create_description_label":"Description", - "panel_forums_create_description":"Where all the super secret stuff happens", - "panel_forums_active_label":"Active", - "panel_forums_preset_label":"Preset", - "panel_forums_create_button":"Add Forum", + "panel.forums_head":"Forums", + "panel.forums_hidden":"Hidden", + "panel.forums_edit_button_tooltip":"Edit Forum", + "panel.forums_edit_button_aria":"Edit Forum", + "panel.forums_update_button":"Update", + "panel.forums_delete_button_tooltip":"Delete Forum", + "panel.forums_delete_button_aria":"Delete Forum", + "panel.forums_full_edit_button":"Full Edit", + "panel.forums_create_head":"Add Forum", + "panel.forums_create_name_label":"Name", + "panel.forums_create_name":"Super Secret Forum", + "panel.forums_create_description_label":"Description", + "panel.forums_create_description":"Where all the super secret stuff happens", + "panel.forums_active_label":"Active", + "panel.forums_preset_label":"Preset", + "panel.forums_create_button":"Add Forum", + "panel.forums_update_order_button":"Update Order", + "panel.forums_order_updated":"The forums have been successfully updated", "panel_forum_head_suffix":" Forum", "panel_forum_name":"Name", @@ -942,8 +946,9 @@ "panel_themes_menus_head":"Menus", "panel_themes_menus_main":"Main Menu", "panel_themes_menus_items_head":"Menu Items", - "panel_themes_menus_item_edit_button_aria":"Edit menu item", - "panel_themes_menus_item_delete_button_aria":"Delete menu item", + "panel_themes_menus_items_edit_button_aria":"Edit menu item", + "panel_themes_menus_items_delete_button_aria":"Delete menu item", + "panel_themes_menus_items_update_button":"Update Order", "panel_themes_menus_edit_head":"Menu Editor", "panel_themes_menus_create_head":"Create Menu Item", diff --git a/patcher/patches.go b/patcher/patches.go index 3206c51a..5cf757b1 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -30,6 +30,7 @@ func init() { addPatch(15, patch15) addPatch(16, patch16) addPatch(17, patch17) + addPatch(18, patch18) } func patch0(scanner *bufio.Scanner) (err error) { @@ -588,3 +589,7 @@ func patch17(scanner *bufio.Scanner) error { return err }) } + +func patch18(scanner *bufio.Scanner) error { + return execStmt(qgen.Builder.AddColumn("forums", tblColumn{"order", "int", 0, false, false, "0"}, nil)) +} \ No newline at end of file diff --git a/public/global.js b/public/global.js index b7e393d7..ed07f3a4 100644 --- a/public/global.js +++ b/public/global.js @@ -14,6 +14,14 @@ var wsBackoff = 0; // Topic move var forumToMoveTo = 0; +function pushNotice(msg) { + let aBox = document.getElementsByClassName("alertbox")[0]; + let div = document.createElement('div'); + div.innerHTML = Template_notice(msg).trim(); + aBox.appendChild(div); + runInitHook("after_notice"); +} + // TODO: Write a friendlier error handler which uses a .notice or something, we could have a specialised one for alerts function ajaxError(xhr,status,errstr) { console.log("The AJAX request failed"); diff --git a/public/init.js b/public/init.js index 43242a1c..2428f4c7 100644 --- a/public/init.js +++ b/public/init.js @@ -28,9 +28,9 @@ function runHook(name, ...args) { console.log("Running hook '"+name+"'"); let hook = hooks[name]; - for (const index in hook) { - hook[index](...args); - } + let ret; + for (const index in hook) ret = hook[index](...args); + return ret; } function addHook(name, callback) { @@ -40,15 +40,14 @@ function addHook(name, callback) { // InitHooks are slightly special, as if they are run, then any adds after the initial run will run immediately, this is to deal with the async nature of script loads function runInitHook(name, ...args) { - runHook(name,...args); + let ret = runHook(name,...args); ranInitHooks[name] = true; + return ret; } function addInitHook(name, callback) { addHook(name, callback); - if(name in ranInitHooks) { - callback(); - } + if(name in ranInitHooks) callback(); } // Temporary hack for templates @@ -175,14 +174,14 @@ function RelativeTime(date) { return date; } -function initPhrases(loggedIn) { +function initPhrases(loggedIn, panel = false) { console.log("in initPhrases") console.log("tmlInits:",tmplInits) let e = ""; - if(loggedIn) { - e = ",topic" - } - fetchPhrases("status,topic_list,alerts,paginator,analytics"+e) // TODO: Break this up? + if(loggedIn && !panel) e = ",topic_list,topic"; + else if(panel) e = ",analytics,panel"; // TODO: Request phrases for just one section of the control panel? + else e = ",topic_list"; + fetchPhrases("status,alerts,paginator"+e) // TODO: Break this up? } function fetchPhrases(plist) { @@ -219,15 +218,18 @@ function fetchPhrases(plist) { (() => { runInitHook("pre_iife"); let loggedIn = document.head.querySelector("[property='x-loggedin']").content == "true"; + let panel = window.location.pathname.startsWith("/panel/"); - if(!window.location.pathname.startsWith("/panel/")) { - let toLoad = 2; - // TODO: Shunt this into loggedIn if there aren't any search and filter widgets? - let q = (f) => { - toLoad--; - if(toLoad===0) initPhrases(loggedIn); - if(f) throw("template function not found"); - }; + let toLoad = 1; + // TODO: Shunt this into loggedIn if there aren't any search and filter widgets? + let q = (f) => { + toLoad--; + if(toLoad===0) initPhrases(loggedIn,panel); + if(f) throw("template function not found"); + }; + + if(!panel) { + toLoad += 2; if(loggedIn) { toLoad += 2; notifyOnScriptW("template_topic_c_edit_post", () => q(!Template_topic_c_edit_post)); @@ -235,9 +237,8 @@ function fetchPhrases(plist) { } notifyOnScriptW("template_topics_topic", () => q(!Template_topics_topic)); notifyOnScriptW("template_paginator", () => q(!Template_paginator)); - } else { - initPhrases(false); } + notifyOnScriptW("template_notice", () => q(!Template_notice)); if(loggedIn) { fetch("/api/me/") diff --git a/public/panel_forums.js b/public/panel_forums.js new file mode 100644 index 00000000..ed8fc5bd --- /dev/null +++ b/public/panel_forums.js @@ -0,0 +1,59 @@ +(() => { +addInitHook("end_init", () => { + +formVars = { + 'forum_active': ['Hide','Show'], + 'forum_preset': ['all','announce','members','staff','admins','archive','custom'] +}; +var forums = {}; +let items = document.getElementsByClassName("panel_forum_item"); +for(let i = 0; item = items[i]; i++) forums[i] = item.getAttribute("data-fid"); +console.log("forums:",forums); + +Sortable.create(document.getElementById("panel_forums"), { + sort: true, + onEnd: (evt) => { + console.log("pre forums: ", forums) + console.log("evt: ", evt) + let oldFid = forums[evt.newIndex]; + forums[evt.oldIndex] = oldFid; + let newFid = evt.item.getAttribute("data-fid"); + console.log("newFid: ", newFid); + forums[evt.newIndex] = newFid; + console.log("post forums: ", forums); + } +}); + +document.getElementById("panel_forums_order_button").addEventListener("click", () => { + let req = new XMLHttpRequest(); + if(!req) { + console.log("Failed to create request"); + return false; + } + req.onreadystatechange = () => { + try { + if(req.readyState!==XMLHttpRequest.DONE) return; + // TODO: Signal the error with a notice + if(req.status!==200) return; + + let resp = JSON.parse(req.responseText); + console.log("resp: ", resp); + // TODO: Should we move other notices into TmplPhrases like this one? + pushNotice(phraseBox["panel"]["panel.forums_order_updated"]); + if(resp.success==1) return; + } catch(ex) { + console.error("exception: ", ex) + } + console.trace(); + } + // ? - Is encodeURIComponent the right function for this? + req.open("POST","/panel/forums/order/edit/submit/?session=" + encodeURIComponent(me.User.Session)); + req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + let items = ""; + for(let i = 0; item = forums[i];i++) items += item+","; + if(items.length > 0) items = items.slice(0,-1); + req.send("js=1&items={"+items+"}"); +}); + +}); +})(); \ No newline at end of file diff --git a/router_gen/routes.go b/router_gen/routes.go index a71b7378..6bacc3da 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -151,6 +151,7 @@ func panelRoutes() *RouteGroup { Action("panel.ForumsCreateSubmit", "/panel/forums/create/"), Action("panel.ForumsDelete", "/panel/forums/delete/", "extraData"), Action("panel.ForumsDeleteSubmit", "/panel/forums/delete/submit/", "extraData"), + Action("panel.ForumsOrderSubmit", "/panel/forums/order/edit/submit/"), View("panel.ForumsEdit", "/panel/forums/edit/", "extraData"), Action("panel.ForumsEditSubmit", "/panel/forums/edit/submit/", "extraData"), Action("panel.ForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extraData"), diff --git a/routes.go b/routes.go index dce901cf..dacdb059 100644 --- a/routes.go +++ b/routes.go @@ -145,6 +145,8 @@ var phraseWhitelist = []string{ "alerts", "paginator", "analytics", + + "panel", // We're going to handle this specially below as this is a security boundary } func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { @@ -199,13 +201,22 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout var ok = false for _, item := range phraseWhitelist { if strings.HasPrefix(positive, item) { - ok = true + // TODO: Break this down into smaller security boundaries based on control panel sections? + if strings.HasPrefix(positive,"panel") { + if user.IsSuperMod { + ok = true + w.Header().Set("Cache-Control", "private") + } + } else { + ok = true + } break } } if !ok { return c.PreErrorJS("Outside of phrase prefix whitelist", w, r) } + pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positive) if !ok { return c.PreErrorJS("No such prefix", w, r) @@ -219,13 +230,22 @@ func routeAPIPhrases(w http.ResponseWriter, r *http.Request, user c.User) c.Rout var ok = false for _, item := range phraseWhitelist { if strings.HasPrefix(positives[0], item) { - ok = true + // TODO: Break this down into smaller security boundaries based on control panel sections? + if strings.HasPrefix(positives[0],"panel") { + if user.IsSuperMod { + ok = true + w.Header().Set("Cache-Control", "private") + } + } else { + ok = true + } break } } if !ok { return c.PreErrorJS("Outside of phrase prefix whitelist", w, r) } + pPhrases, ok := phrases.GetTmplPhrasesByPrefix(positives[0]) if !ok { return c.PreErrorJS("No such prefix", w, r) diff --git a/routes/panel/analytics.go b/routes/panel/analytics.go index 59a3deec..6d6c1a01 100644 --- a/routes/panel/analytics.go +++ b/routes/panel/analytics.go @@ -22,7 +22,8 @@ type AnalyticsTimeRange struct { Range string } -func analyticsTimeRange(rawTimeRange string) (timeRange AnalyticsTimeRange, err error) { +func analyticsTimeRange(rawTimeRange string) (*AnalyticsTimeRange, error) { + timeRange := &AnalyticsTimeRange{} timeRange.Quantity = 6 timeRange.Unit = "hour" timeRange.Slices = 12 @@ -78,7 +79,7 @@ func analyticsTimeRange(rawTimeRange string) (timeRange AnalyticsTimeRange, err return timeRange, nil } -func analyticsTimeRangeToLabelList(timeRange AnalyticsTimeRange) (revLabelList []int64, labelList []int64, viewMap map[int64]int64) { +func analyticsTimeRangeToLabelList(timeRange *AnalyticsTimeRange) (revLabelList []int64, labelList []int64, viewMap map[int64]int64) { viewMap = make(map[int64]int64) var currentTime = time.Now().Unix() for i := 1; i <= timeRange.Slices; i++ { diff --git a/routes/panel/forums.go b/routes/panel/forums.go index 6f23ea14..357561da 100644 --- a/routes/panel/forums.go +++ b/routes/panel/forums.go @@ -19,6 +19,8 @@ func Forums(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { if !user.Perms.ManageForums { return c.NoPermissions(w, r, user) } + basePage.Header.AddScript("Sortable-1.4.0/Sortable.min.js") + basePage.Header.AddScriptAsync("panel_forums.js") // TODO: Paginate this? var forumList []interface{} @@ -130,6 +132,31 @@ func ForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.User, sfi return nil } +func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, &user) + if ferr != nil { + return ferr + } + isJs := (r.PostFormValue("js") == "1") + if !user.Perms.ManageForums { + return c.NoPermissionsJSQ(w, r, user, isJs) + } + sitems := strings.TrimSuffix(strings.TrimPrefix(r.PostFormValue("items"), "{"), "}") + //fmt.Printf("sitems: %+v\n", sitems) + + var updateMap = make(map[int]int) + for index, sfid := range strings.Split(sitems, ",") { + fid, err := strconv.Atoi(sfid) + if err != nil { + return c.LocalErrorJSQ("Invalid integer in forum list", w, r, user, isJs) + } + updateMap[fid] = index + } + c.Forums.UpdateOrder(updateMap) + + return successRedirect("/panel/forums/", w, r, isJs) +} + func ForumsEdit(w http.ResponseWriter, r *http.Request, user c.User, sfid string) c.RouteError { basePage, ferr := buildBasePage(w, r, &user, "edit_forum", "forums") if ferr != nil { @@ -333,7 +360,7 @@ func ForumsEditPermsAdvance(w http.ResponseWriter, r *http.Request, user c.User, addNameLangToggle("MoveTopic", forumPerms.MoveTopic) if r.FormValue("updated") == "1" { - basePage.AddNotice("panel_forums_perms_updated") + basePage.AddNotice("panel_forum_perms_updated") } pi := c.PanelEditForumGroupPage{basePage, forum.ID, gid, forum.Name, forum.Desc, forum.Active, forum.Preset, formattedPermList} diff --git a/schema/mssql/query_forums.sql b/schema/mssql/query_forums.sql index 0a5f9c33..d5fdb520 100644 --- a/schema/mssql/query_forums.sql +++ b/schema/mssql/query_forums.sql @@ -3,6 +3,7 @@ CREATE TABLE [forums] ( [name] nvarchar (100) not null, [desc] nvarchar (200) not null, [active] bit DEFAULT 1 not null, + [order] int DEFAULT 0 not null, [topicCount] int DEFAULT 0 not null, [preset] nvarchar (100) DEFAULT '' not null, [parentID] int DEFAULT 0 not null, diff --git a/schema/mysql/query_forums.sql b/schema/mysql/query_forums.sql index 848f42a1..c7692742 100644 --- a/schema/mysql/query_forums.sql +++ b/schema/mysql/query_forums.sql @@ -3,6 +3,7 @@ CREATE TABLE `forums` ( `name` varchar(100) not null, `desc` varchar(200) not null, `active` boolean DEFAULT 1 not null, + `order` int DEFAULT 0 not null, `topicCount` int DEFAULT 0 not null, `preset` varchar(100) DEFAULT '' not null, `parentID` int DEFAULT 0 not null, diff --git a/schema/pgsql/query_forums.sql b/schema/pgsql/query_forums.sql index 171a5458..c9ae5927 100644 --- a/schema/pgsql/query_forums.sql +++ b/schema/pgsql/query_forums.sql @@ -3,6 +3,7 @@ CREATE TABLE "forums" ( `name` varchar (100) not null, `desc` varchar (200) not null, `active` boolean DEFAULT 1 not null, + `order` int DEFAULT 0 not null, `topicCount` int DEFAULT 0 not null, `preset` varchar (100) DEFAULT '' not null, `parentID` int DEFAULT 0 not null, diff --git a/templates/header.html b/templates/header.html index c338b878..4fae1421 100644 --- a/templates/header.html +++ b/templates/header.html @@ -7,7 +7,7 @@ {{range .Header.PreScriptsAsync}} {{end}} - + {{range .Header.ScriptsAsync}} {{end}} @@ -25,7 +25,7 @@ {{if .GoogSiteVerify}}{{end}} - {{if not .CurrentUser.IsSuperMod}}{{end}} + {{if not .CurrentUser.IsSuperMod}}{{end}}{{flush}}
{{/****/}}
{{dock "leftOfNav" .Header }}
diff --git a/templates/panel_forums.html b/templates/panel_forums.html index 8580a61f..5734f2b3 100644 --- a/templates/panel_forums.html +++ b/templates/panel_forums.html @@ -2,60 +2,63 @@
{{template "panel_menu.html" . }} -
{{template "panel_before_head.html" . }}
-

{{lang "panel_forums_head"}}

+
+

{{lang "panel.forums_head"}}

+

{{lang "panel_hints_reorder"}}

+
{{range .ItemList}} -
+
+ {{/** TODO: Make sure the forum_active_name class is set and unset when the activity status of this forum is changed **/}} {{.Name}}
{{.Desc}}
- + - - - {{if gt .ID 1}}{{end}} - + + + {{if gt .ID 1}}{{end}} +
{{end}}
+
+
+
-

{{lang "panel_forums_create_head"}}

+

{{lang "panel.forums_create_head"}}

- +
- +
-
+
diff --git a/templates/panel_themes_menus_items.html b/templates/panel_themes_menus_items.html index fc368118..fcb89165 100644 --- a/templates/panel_themes_menus_items.html +++ b/templates/panel_themes_menus_items.html @@ -4,20 +4,24 @@
{{template "panel_before_head.html" . }}
-

{{lang "panel_themes_menus_items_head"}}

+
+

{{lang "panel_themes_menus_items_head"}}

+

{{lang "panel_hints_reorder"}}

+
{{range .ItemList}}
+ {{.Name}} - - - + + +
{{end}}
-
+

{{lang "panel_themes_menus_create_head"}}

@@ -80,10 +84,8 @@ // TODO: Move this into a JS file to reduce the number of possible problems var menuItems = {}; let items = document.getElementsByClassName("panel_menu_item"); -for(let i = 0; item = items[i];i++) { - let miid = item.getAttribute("data-miid"); - menuItems[i] = miid; -} +for(let i = 0; item = items[i]; i++) menuItems[i] = item.getAttribute("data-miid"); + Sortable.create(document.getElementById("panel_menu_item_holder"), { sort: true, onEnd: (evt) => { @@ -92,11 +94,12 @@ Sortable.create(document.getElementById("panel_menu_item_holder"), { let oldMiid = menuItems[evt.newIndex]; menuItems[evt.oldIndex] = oldMiid; let newMiid = evt.item.getAttribute("data-miid"); - console.log("newMiid: ", newMiid) + console.log("newMiid: ", newMiid); menuItems[evt.newIndex] = newMiid; - console.log("post menuItems: ", menuItems) + console.log("post menuItems: ", menuItems); } }); + document.getElementById("panel_menu_items_order_button").addEventListener("click", () => { let req = new XMLHttpRequest(); if(!req) { @@ -105,18 +108,13 @@ document.getElementById("panel_menu_items_order_button").addEventListener("click } req.onreadystatechange = () => { try { - if(req.readyState!==XMLHttpRequest.DONE) { - return; - } + if(req.readyState!==XMLHttpRequest.DONE) return; // TODO: Signal the error with a notice if(req.status===200) { let resp = JSON.parse(req.responseText); console.log("resp: ", resp); - if(resp.success==1) { - // TODO: Have a successfully updated notice - console.log("success"); - return; - } + // TODO: Have a successfully updated notice + if(resp.success==1) return; } } catch(ex) { console.error("exception: ", ex) @@ -127,12 +125,8 @@ document.getElementById("panel_menu_items_order_button").addEventListener("click req.open("POST","/panel/themes/menus/item/order/edit/submit/{{.MenuID}}?session=" + encodeURIComponent(me.User.Session)); req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); let items = ""; - for(let i = 0; item = menuItems[i];i++) { - items += item+","; - } - if(items.length > 0) { - items = items.slice(0,-1); - } + for(let i = 0; item = menuItems[i];i++) items += item+","; + if(items.length > 0) items = items.slice(0,-1); req.send("js=1&items={"+items+"}"); }); diff --git a/themes/cosora/public/panel.css b/themes/cosora/public/panel.css index e045a425..3f58dfaa 100644 --- a/themes/cosora/public/panel.css +++ b/themes/cosora/public/panel.css @@ -62,6 +62,12 @@ /*margin-top: -4px;*/ margin-bottom: 14px; } +.colstack_right .colstack_head .rowitem { + display: flex; +} +.colstack_right .colstack_head h1 + h2.hguide { + margin-left: auto; +} .footer { margin-top: 0px; } diff --git a/themes/nox/public/main.css b/themes/nox/public/main.css index a69090d5..ca5cd0ea 100644 --- a/themes/nox/public/main.css +++ b/themes/nox/public/main.css @@ -358,6 +358,11 @@ h2 { margin-bottom: 8px; margin-left: 8px; } +.rowhead h2, .colstack_head h2 { + margin-top: 0px; + margin-bottom: 0px; + margin-left: 0px; +} .topic_create_form { display: flex; diff --git a/themes/nox/public/misc.js b/themes/nox/public/misc.js index cf0507da..3145136e 100644 --- a/themes/nox/public/misc.js +++ b/themes/nox/public/misc.js @@ -30,6 +30,17 @@ function noxMenuBind() { } (() => { + function moveAlerts() { + // Move the alerts above the first header + let colSel = $(".colstack_right .colstack_head:first"); + let colSelAlt = $(".colstack_right .colstack_item:first"); + let colSelAltAlt = $(".colstack_right .coldyn_block:first"); + if(colSel.length > 0) $('.alert').insertBefore(colSel); + else if (colSelAlt.length > 0) $('.alert').insertBefore(colSelAlt); + else if (colSelAltAlt.length > 0) $('.alert').insertBefore(colSelAltAlt); + else $('.alert').insertAfter(".rowhead:first"); + } + addInitHook("after_update_alert_list", (alertCount) => { console.log("misc.js"); console.log("alertCount:",alertCount); @@ -57,15 +68,7 @@ function noxMenuBind() { $(window).resize(() => noxMenuBind()); noxMenuBind(); - - // Move the alerts above the first header - let colSel = $(".colstack_right .colstack_head:first"); - let colSelAlt = $(".colstack_right .colstack_item:first"); - let colSelAltAlt = $(".colstack_right .coldyn_block:first"); - if(colSel.length > 0) $('.alert').insertBefore(colSel); - else if (colSelAlt.length > 0) $('.alert').insertBefore(colSelAlt); - else if (colSelAltAlt.length > 0) $('.alert').insertBefore(colSelAltAlt); - else $('.alert').insertAfter(".rowhead:first"); + moveAlerts(); $(".menu_hamburger").click(function() { event.stopPropagation(); @@ -78,4 +81,6 @@ function noxMenuBind() { $(document).click(() => $(".more_menu").removeClass("more_menu_selected")); }); + + addInitHook("after_notice", moveAlerts); })(); \ No newline at end of file diff --git a/themes/nox/public/panel.css b/themes/nox/public/panel.css index a7db5435..66001da4 100644 --- a/themes/nox/public/panel.css +++ b/themes/nox/public/panel.css @@ -79,6 +79,10 @@ .colstack_right .colstack_head h1 { font-size: 21px; } +.colstack_right .colstack_head h1 + h2.hguide { + margin-left: auto; + font-size: 17px; +} .colstack_right .colstack_item.the_form, .colstack_right .colstack_item:not(.colstack_head):not(.rowhead) .rowitem { background-color: #444444; } @@ -292,6 +296,35 @@ button, .formbutton, .panel_right_button:not(.has_inner_button), #panel_users .p margin-left: 4px; } +span.grip { + content: '....'; + width: 20px; + display: inline-block; + overflow: hidden; + line-height: 5px; + padding: 3px 4px; + cursor: move; + vertical-align: middle; + margin-top: -16px; + margin-right: 12px; + font-size: 12px; + font-family: sans-serif; + letter-spacing: -3px; + color: #888888; + text-shadow: 1px 0 1px black; + margin-left: -12px; + height: 100%; + font-size: 40px; + margin-bottom: -4px; + line-height: 8px; +} +span.grip::after { + content: '... ... ... ... ... ... ...'; +} +.forum_no_desc span.grip, .panel_menu_item span.grip { + height: 40px; +} + .panel_plugin_meta { display: flex; flex-direction: column; diff --git a/themes/shadow/public/panel.css b/themes/shadow/public/panel.css index 954b8959..fec4921b 100644 --- a/themes/shadow/public/panel.css +++ b/themes/shadow/public/panel.css @@ -11,6 +11,9 @@ .colstack_head .rowitem a h1 { margin-right: 0px; } +.rowitem h2.hguide { + font-size: 15px; +} .rowlist .tag-mini { font-size: 10px; diff --git a/themes/tempra_simple/public/panel.css b/themes/tempra_simple/public/panel.css index d269e958..4377372b 100644 --- a/themes/tempra_simple/public/panel.css +++ b/themes/tempra_simple/public/panel.css @@ -10,6 +10,12 @@ .submenu a { margin-left: 8px; } +/*.colstack_right .colstack_head .rowitem { + display: flex; +}*/ +.colstack_right .colstack_head h1 + h2.hguide { + margin-left: auto; +} .edit_button:before { content: "{{lang "panel_edit_button_text" . }}";