From 78c5c62eee2d3d6ccd36851ca2a1a5de1f05a9fa Mon Sep 17 00:00:00 2001 From: Azareal Date: Sun, 29 Sep 2019 14:56:39 +1000 Subject: [PATCH] Basic group promotions. Add the users_groups_promotions table. Optimise ConvoViewPage. Shortened some things. Convo CSS fixes. Make sure the user cache is flushed properly after the post stats change. You will need to run the patcher / updater for this commit. --- cmd/query_gen/tables.go | 16 + common/activity_stream.go | 6 +- common/alerts.go | 2 +- common/email.go | 2 +- common/email_store.go | 10 +- common/files.go | 6 +- common/forum_perms.go | 4 +- common/forum_store.go | 6 +- common/ip_search.go | 5 +- common/pages.go | 14 + common/password_reset.go | 2 +- common/pluginlangs.go | 3 +- common/promotions.go | 109 ++++ common/statistics.go | 6 +- common/subscription.go | 11 +- common/template_init.go | 104 ++-- common/thaw.go | 34 +- common/theme.go | 4 +- common/user.go | 11 +- common/ws_hub.go | 2 +- extend/guilds/lib/guilds.go | 15 +- gen_router.go | 490 +++++++++--------- gen_tables.go | 27 +- main.go | 4 + patcher/patches.go | 23 + router_gen/routes.go | 3 + routes/convos.go | 4 +- routes/panel/dashboard.go | 8 +- .../mssql/query_users_groups_promotions.sql | 9 + .../mysql/query_users_groups_promotions.sql | 9 + .../pgsql/query_users_groups_promotions.sql | 9 + templates/convos.html | 2 +- templates/panel_group_edit_promotions.html | 69 +++ templates/panel_group_menu.html | 2 +- templates/topic.html | 4 +- templates/topic_posts.html | 4 +- templates/topics_quick_topic.html | 2 +- themes/nox/overrides/panel_group_menu.html | 2 +- themes/nox/public/panel.css | 5 +- themes/shadow/public/convo.css | 16 + themes/tempra_simple/public/convo.css | 19 + 41 files changed, 714 insertions(+), 369 deletions(-) create mode 100644 common/promotions.go create mode 100644 schema/mssql/query_users_groups_promotions.sql create mode 100644 schema/mysql/query_users_groups_promotions.sql create mode 100644 schema/pgsql/query_users_groups_promotions.sql create mode 100644 templates/panel_group_edit_promotions.html diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index f6918ae0..c2eb48c1 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -69,6 +69,22 @@ func createTables(adapter qgen.Adapter) error { }, ) + qgen.Install.CreateTable("users_groups_promotions", mysqlPre, mysqlCol, + []tC{ + tC{"pid", "int", 0, false, true, ""}, + tC{"from_gid", "int", 0, false, false, ""}, + tC{"to_gid", "int", 0, false, false, ""}, + tC{"two_way", "boolean",0,false,false,"0"}, // If a user no longer meets the requirements for this promotion then they will be demoted if this flag is set + + // Requirements + tC{"level", "int", 0, false, false, ""}, + tC{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted + }, + []tblKey{ + tblKey{"pid", "primary","",false}, + }, + ) + qgen.Install.CreateTable("users_2fa_keys", mysqlPre, mysqlCol, []tC{ tC{"uid", "int", 0, false, false, ""}, diff --git a/common/activity_stream.go b/common/activity_stream.go index dcf30f2f..60aff6e9 100644 --- a/common/activity_stream.go +++ b/common/activity_stream.go @@ -25,8 +25,8 @@ func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, er }, acc.FirstError() } -func (s *DefaultActivityStream) Add(alert Alert) (int, error) { - res, err := s.add.Exec(alert.ActorID, alert.TargetUserID, alert.Event, alert.ElementType, alert.ElementID) +func (s *DefaultActivityStream) Add(a Alert) (int, error) { + res, err := s.add.Exec(a.ActorID, a.TargetUserID, a.Event, a.ElementType, a.ElementID) if err != nil { return 0, err } @@ -36,7 +36,7 @@ func (s *DefaultActivityStream) Add(alert Alert) (int, error) { } func (s *DefaultActivityStream) Get(id int) (Alert, error) { - var a = Alert{ASID: id} + a := Alert{ASID: id} err := s.get.QueryRow(id).Scan(&a.ActorID, &a.TargetUserID, &a.Event, &a.ElementType, &a.ElementID, &a.CreatedAt) return a, err } diff --git a/common/alerts.go b/common/alerts.go index ab40b82e..0a7332de 100644 --- a/common/alerts.go +++ b/common/alerts.go @@ -96,7 +96,7 @@ func BuildAlert(alert Alert, user User /* The current user */) (out string, err } var url, area string - var phraseName = "." + alert.ElementType + phraseName := "." + alert.ElementType switch alert.ElementType { case "topic": topic, err := Topics.Get(alert.ElementID) diff --git a/common/email.go b/common/email.go index 3269ebb4..6cbb3869 100644 --- a/common/email.go +++ b/common/email.go @@ -16,7 +16,7 @@ type Email struct { } func SendValidationEmail(username string, email string, token string) error { - var schema = "http" + schema := "http" if Site.EnableSsl { schema += "s" } diff --git a/common/email_store.go b/common/email_store.go index 4d67178b..c1bc3f51 100644 --- a/common/email_store.go +++ b/common/email_store.go @@ -28,7 +28,7 @@ func NewDefaultEmailStore(acc *qgen.Accumulator) (*DefaultEmailStore, error) { } func (s *DefaultEmailStore) GetEmailsByUser(user *User) (emails []Email, err error) { - email := Email{UserID: user.ID} + e := Email{UserID: user.ID} rows, err := s.getEmailsByUser.Query(user.ID) if err != nil { return emails, err @@ -36,15 +36,15 @@ func (s *DefaultEmailStore) GetEmailsByUser(user *User) (emails []Email, err err defer rows.Close() for rows.Next() { - err := rows.Scan(&email.Email, &email.Validated, &email.Token) + err := rows.Scan(&e.Email, &e.Validated, &e.Token) if err != nil { return emails, err } - if email.Email == user.Email { - email.Primary = true + if e.Email == user.Email { + e.Primary = true } - emails = append(emails, email) + emails = append(emails, e) } return emails, rows.Err() } diff --git a/common/files.go b/common/files.go index 809297f2..4774f8ba 100644 --- a/common/files.go +++ b/common/files.go @@ -212,7 +212,7 @@ func (list SFileList) JSTmplInit() error { fragset := tmpl.GetFrag(shortName) if fragset != nil { - var sfrags = []byte("let " + shortName + "_frags = [];\n") + sfrags := []byte("let " + shortName + "_frags = [];\n") for _, frags := range fragset { sfrags = append(sfrags, []byte(shortName+"_frags.push(`"+string(frags)+"`);\n")...) } @@ -229,7 +229,7 @@ func (list SFileList) JSTmplInit() error { path = tmplName + ".js" DebugLog("js path: ", path) - var ext = filepath.Ext("/tmpl_client/" + path) + ext := filepath.Ext("/tmpl_client/" + path) gzipData, err := CompressBytesGzip(data) if err != nil { return err @@ -306,7 +306,7 @@ func (list SFileList) Add(path string, prefix string) error { return err } - var ext = filepath.Ext(path) + ext := filepath.Ext(path) path = strings.TrimPrefix(path, prefix) gzipData, err := CompressBytesGzip(data) if err != nil { diff --git a/common/forum_perms.go b/common/forum_perms.go index a2e09270..30d08305 100644 --- a/common/forum_perms.go +++ b/common/forum_perms.go @@ -229,8 +229,8 @@ func ForumPermsToGroupForumPreset(fperms *ForumPerms) string { if !fperms.ViewTopic { return "no_access" } - var canPost = (fperms.LikeItem && fperms.CreateTopic && fperms.CreateReply) - var canModerate = (canPost && fperms.EditTopic && fperms.DeleteTopic && fperms.EditReply && fperms.DeleteReply && fperms.PinTopic && fperms.CloseTopic && fperms.MoveTopic) + canPost := (fperms.LikeItem && fperms.CreateTopic && fperms.CreateReply) + canModerate := (canPost && fperms.EditTopic && fperms.DeleteTopic && fperms.EditReply && fperms.DeleteReply && fperms.PinTopic && fperms.CloseTopic && fperms.MoveTopic) if canModerate { return "can_moderate" } diff --git a/common/forum_store.go b/common/forum_store.go index 1cd469a5..b83ef3cb 100644 --- a/common/forum_store.go +++ b/common/forum_store.go @@ -329,14 +329,14 @@ func (s *MemoryForumStore) UpdateLastTopic(tid int, uid int, fid int) error { return s.Reload(fid) } -func (s *MemoryForumStore) Create(forumName string, forumDesc string, active bool, preset string) (int, error) { - if forumName == "" { +func (s *MemoryForumStore) Create(name string, desc string, active bool, preset string) (int, error) { + if name == "" { return 0, ErrBlankName } forumCreateMutex.Lock() defer forumCreateMutex.Unlock() - res, err := s.create.Exec(forumName, forumDesc, active, preset) + res, err := s.create.Exec(name, desc, active, preset) if err != nil { return 0, err } diff --git a/common/ip_search.go b/common/ip_search.go index f411891d..4bb7d0ec 100644 --- a/common/ip_search.go +++ b/common/ip_search.go @@ -32,9 +32,8 @@ func NewDefaultIPSearcher() (*DefaultIPSearcher, error) { func (searcher *DefaultIPSearcher) Lookup(ip string) (uids []int, err error) { var uid int - var reqUserList = make(map[int]bool) - - var runQuery = func(stmt *sql.Stmt) error { + reqUserList := make(map[int]bool) + runQuery := func(stmt *sql.Stmt) error { rows, err := stmt.Query(ip) if err != nil { return err diff --git a/common/pages.go b/common/pages.go index 88cba86c..f5be8801 100644 --- a/common/pages.go +++ b/common/pages.go @@ -579,6 +579,20 @@ type PanelEditGroupPermsPage struct { GlobalPerms []NameLangToggle } +type GroupPromotionExtend struct { + *GroupPromotion + FromGroup *Group + ToGroup *Group +} + +type PanelEditGroupPromotionsPage struct { + *BasePanelPage + ID int + Name string + Promotions []*GroupPromotionExtend + Groups []*Group +} + type BackupItem struct { SQLURL string diff --git a/common/password_reset.go b/common/password_reset.go index fafb4121..7697cb01 100644 --- a/common/password_reset.go +++ b/common/password_reset.go @@ -42,7 +42,7 @@ func (r *DefaultPasswordResetter) ValidateToken(uid int, token string) error { } defer rows.Close() - var success = false + success := false for rows.Next() { var rtoken string err := rows.Scan(&rtoken) diff --git a/common/pluginlangs.go b/common/pluginlangs.go index f9915817..509d6eca 100644 --- a/common/pluginlangs.go +++ b/common/pluginlangs.go @@ -46,7 +46,6 @@ func InitPluginLangs() error { for _, pluginLang := range pluginLangs { pluginLang.Init() } - pluginList, err := GetPluginFiles() if err != nil { return err @@ -80,7 +79,7 @@ func InitPluginLangs() error { return errors.New("Couldn't find a main file for plugin '" + pluginItem + "'") } - var ext = filepath.Ext(plugin.Main) + ext := filepath.Ext(plugin.Main) pluginLang, err := ExtToPluginLang(ext) if err != nil { return err diff --git a/common/promotions.go b/common/promotions.go new file mode 100644 index 00000000..e91eb263 --- /dev/null +++ b/common/promotions.go @@ -0,0 +1,109 @@ +package common + +import ( + "database/sql" + + qgen "github.com/Azareal/Gosora/query_gen" +) + +var GroupPromotions *DefaultGroupPromotionStore + +type GroupPromotion struct { + ID int + From int + To int + TwoWay bool + + Level int + MinTime int +} + +type GroupPromotionStore interface { + GetByGroup() ([]*GroupPromotion, error) + Get(id int) (*GroupPromotion, error) + PromoteIfEligible(u *User, level int) error + Delete(id int) error + Create(from int, to int, twoWay bool, level int) (int, error) +} + +type DefaultGroupPromotionStore struct { + getByGroup *sql.Stmt + get *sql.Stmt + delete *sql.Stmt + create *sql.Stmt + + getByUser *sql.Stmt + updateUser *sql.Stmt +} + +func NewDefaultGroupPromotionStore(acc *qgen.Accumulator) (*DefaultGroupPromotionStore, error) { + ugp := "users_groups_promotions" + return &DefaultGroupPromotionStore{ + getByGroup: acc.Select(ugp).Columns("pid, from_gid, to_gid, two_way, level, minTime").Where("from_gid=? OR to_gid=?").Prepare(), + get: acc.Select(ugp).Columns("from_gid, to_gid, two_way, level, minTime").Where("pid = ?").Prepare(), + delete: acc.Delete(ugp).Where("pid = ?").Prepare(), + create: acc.Insert(ugp).Columns("from_gid, to_gid, two_way, level, minTime").Fields("?,?,?,?,?").Prepare(), + + getByUser: acc.Select(ugp).Columns("pid, to_gid, two_way, level, minTime").Where("from_gid=? AND level>=?").Orderby("level DESC").Limit("1").Prepare(), + updateUser: acc.Update("users").Set("group = ?").Where("level >= ?").Prepare(), + }, acc.FirstError() +} + +func (s *DefaultGroupPromotionStore) GetByGroup(gid int) (gps []*GroupPromotion, err error) { + rows, err := s.getByGroup.Query(gid, gid) + if err != nil { + return nil, err + } + defer rows.Close() + + for rows.Next() { + g := &GroupPromotion{} + err := rows.Scan(&g.ID, &g.From, &g.To, &g.TwoWay, &g.Level, &g.MinTime) + if err != nil { + return nil, err + } + gps = append(gps, g) + } + return gps, rows.Err() +} + +// TODO: Cache the group promotions to avoid hitting the database as much +func (s *DefaultGroupPromotionStore) Get(id int) (*GroupPromotion, error) { + /*g, err := s.cache.Get(id) + if err == nil { + return u, nil + }*/ + + g := &GroupPromotion{ID: id} + err := s.get.QueryRow(id).Scan(&g.From, &g.To, &g.TwoWay, &g.Level, &g.MinTime) + if err == nil { + //s.cache.Set(u) + } + return g, err +} + +func (s *DefaultGroupPromotionStore) PromoteIfEligible(u *User, level int) error { + g := &GroupPromotion{From: u.Group} + err := s.getByUser.QueryRow(u.Group, level).Scan(&g.ID, &g.To, &g.TwoWay, &g.Level, &g.MinTime) + if err == sql.ErrNoRows { + return nil + } else if err != nil { + return err + } + _, err = s.updateUser.Exec(g.To, g.Level) + return err +} + +func (s *DefaultGroupPromotionStore) Delete(id int) error { + _, err := s.delete.Exec(id) + return err +} + +func (s *DefaultGroupPromotionStore) Create(from int, to int, twoWay bool, level int) (int, error) { + res, err := s.create.Exec(from, to, twoWay, level, 0) + if err != nil { + return 0, err + } + lastID, err := res.LastInsertId() + return int(lastID), err +} diff --git a/common/statistics.go b/common/statistics.go index 241b4b10..807741b6 100644 --- a/common/statistics.go +++ b/common/statistics.go @@ -18,15 +18,15 @@ func NewDefaultStatStore() *DefaultStatStore { return &DefaultStatStore{} } -func (store *DefaultStatStore) LookupInt(name string, duration int, unit string) (int, error) { +func (s *DefaultStatStore) LookupInt(name string, duration int, unit string) (int, error) { switch name { case "postCount": - return store.countTable("replies", duration, unit) + return s.countTable("replies", duration, unit) } return 0, errors.New("The requested stat doesn't exist") } -func (store *DefaultStatStore) countTable(table string, duration int, unit string) (stat int, err error) { +func (s *DefaultStatStore) countTable(table string, duration int, unit string) (stat int, err error) { /*counter := qgen.NewAcc().Count("replies").DateCutoff("createdAt", 1, "day").Prepare() if acc.FirstError() != nil { return 0, acc.FirstError() diff --git a/common/subscription.go b/common/subscription.go index a15c03de..c48ebe31 100644 --- a/common/subscription.go +++ b/common/subscription.go @@ -1,7 +1,10 @@ package common -import "database/sql" -import "github.com/Azareal/Gosora/query_gen" +import ( + "database/sql" + + qgen "github.com/Azareal/Gosora/query_gen" +) var Subscriptions SubscriptionStore @@ -21,7 +24,7 @@ func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) { }, acc.FirstError() } -func (store *DefaultSubscriptionStore) Add(uid int, elementID int, elementType string) error { - _, err := store.add.Exec(uid, elementID, elementType) +func (s *DefaultSubscriptionStore) Add(uid int, elementID int, elementType string) error { + _, err := s.add.Exec(uid, elementID, elementType) return err } diff --git a/common/template_init.go b/common/template_init.go index a7fac951..694b3440 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -289,14 +289,14 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string header.Title = name return header } - tmpls := TItemHold(make(map[string]TItem)) - err = compileCommons(c, header, header2, forumList, tmpls) + t := TItemHold(make(map[string]TItem)) + err = compileCommons(c, header, header2, forumList, t) if err != nil { return err } ppage := ProfilePage{htitle("User 526"), replyList, user, 0, 0} // TODO: Use the score from user to generate the currentScore and nextScore - tmpls.Add("profile", "c.ProfilePage", ppage) + t.Add("profile", "c.ProfilePage", ppage) var topicsList []*TopicsRow topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 1, 0, "classname", 0, "", &user2, "", 0, &user3, "General", "/forum/general.2", nil}) @@ -331,38 +331,38 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string } if typ == "1" { - tmpls.Add(sp[0], sp[1], pi) + t.Add(sp[0], sp[1], pi) } else { - tmpls.AddStd(sp[0], sp[1], pi) + t.AddStd(sp[0], sp[1], pi) } } - tmpls.AddStd("login", "c.Page", Page{htitle("Login Page"), tList, nil}) - tmpls.AddStd("register", "c.Page", Page{htitle("Registration Page"), tList, "nananana"}) - tmpls.AddStd("error", "c.ErrorPage", ErrorPage{htitle("Error"), "A problem has occurred in the system."}) + t.AddStd("login", "c.Page", Page{htitle("Login Page"), tList, nil}) + t.AddStd("register", "c.Page", Page{htitle("Registration Page"), tList, "nananana"}) + t.AddStd("error", "c.ErrorPage", ErrorPage{htitle("Error"), "A problem has occurred in the system."}) ipSearchPage := IPSearchPage{htitle("IP Search"), map[int]*User{1: &user2}, "::1"} - tmpls.AddStd("ip_search", "c.IPSearchPage", ipSearchPage) + t.AddStd("ip_search", "c.IPSearchPage", ipSearchPage) var inter nobreak accountPage := Account{header, "dashboard", "account_own_edit", inter} - tmpls.AddStd("account", "c.Account", accountPage) + t.AddStd("account", "c.Account", accountPage) parti := []*User{&user} convo := &Conversation{1,user.ID,time.Now(),0,time.Now()} convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1,1,"hey","",user.ID}, &user, "", 4, true}} convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}} - tmpls.AddStd("convo", "c.ConvoViewPage", convoPage) + t.AddStd("convo", "c.ConvoViewPage", convoPage) convos := []*ConversationExtra{&ConversationExtra{&Conversation{},[]*User{&user}}} convoListPage := ConvoListPage{header, convos, Paginator{[]int{1}, 1, 1}} - tmpls.AddStd("convos", "c.ConvoListPage", convoListPage) + t.AddStd("convos", "c.ConvoListPage", convoListPage) basePage := &BasePanelPage{header, PanelStats{}, "dashboard", ReportForumID} - tmpls.AddStd("panel", "c.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter}) + t.AddStd("panel", "c.Panel", Panel{basePage, "panel_dashboard_right", "", "panel_dashboard", inter}) ges := []GridElement{GridElement{"","", "", 1, "grid_istat", "", "", ""}} - tmpls.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges,ges}) - //tmpls.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter}) + t.AddStd("panel_dashboard", "c.DashGrids", DashGrids{ges,ges}) + //t.AddStd("panel_analytics", "c.PanelAnalytics", Panel{basePage, "panel_dashboard_right","panel_dashboard", inter}) writeTemplate := func(name string, content interface{}) { log.Print("Writing template '" + name + "'") @@ -409,7 +409,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string } log.Print("Writing the templates") - for name, titem := range tmpls { + for name, titem := range t { log.Print("Writing " + name) varList := make(map[string]tmpl.VarItem) if titem.LoggedIn { @@ -444,15 +444,15 @@ func CompileJSTemplates() error { } log.Printf("overriden: %+v\n", overriden) - var config tmpl.CTemplateConfig - config.Minify = Config.MinifyTemplates - config.Debug = Dev.DebugMode - config.SuperDebug = Dev.TemplateDebug - config.SkipHandles = true - config.SkipTmplPtrMap = true - config.SkipInitBlock = false - config.PackageName = "tmpl" - + config := tmpl.CTemplateConfig{ + Minify: Config.MinifyTemplates, + Debug: Dev.DebugMode, + SuperDebug: Dev.TemplateDebug, + SkipHandles: true, + SkipTmplPtrMap: true, + SkipInitBlock: false, + PackageName: "tmpl", + } c := tmpl.NewCTemplateSet("js") c.SetConfig(config) c.SetBuildTags("!no_templategen") @@ -509,10 +509,10 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri // TODO: Fix the import loop so we don't have to use this hack anymore c.SetBuildTags("!no_templategen,tmplgentopic") - tmpls := TItemHold(make(map[string]TItem)) + t := TItemHold(make(map[string]TItem)) topicsRow := &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, now, now, user3.ID, 1, 1, "", "127.0.0.1", 1, 0, 1, 0, 1, "classname", 0, "", &user2, "", 0, &user3, "General", "/forum/general.2", nil} - tmpls.AddStd("topics_topic", "c.TopicsRow", topicsRow) + t.AddStd("topics_topic", "c.TopicsRow", topicsRow) poll := Poll{ID: 1, Type: 0, Options: map[int]string{0: "Nothing", 1: "Something"}, Results: map[int]int{0: 5, 1: 2}, QuickOptions: []PollOption{ PollOption{0, "Nothing"}, @@ -533,19 +533,25 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri header.Title = "Topic Name" tpage := TopicPage{header, replyList, topic, &Forum{ID: 1, Name: "Hahaha"}, poll, Paginator{[]int{1}, 1, 1}} tpage.Forum.Link = BuildForumURL(NameToSlug(tpage.Forum.Name), tpage.Forum.ID) - tmpls.AddStd("topic_posts", "c.TopicPage", tpage) - tmpls.AddStd("topic_alt_posts", "c.TopicPage", tpage) + t.AddStd("topic_posts", "c.TopicPage", tpage) + t.AddStd("topic_alt_posts", "c.TopicPage", tpage) itemsPerPage := 25 _, page, lastPage := PageOffset(20, 1, itemsPerPage) pageList := Paginate(page, lastPage, 5) - tmpls.AddStd("paginator", "c.Paginator", Paginator{pageList, page, lastPage}) + t.AddStd("paginator", "c.Paginator", Paginator{pageList, page, lastPage}) - tmpls.AddStd("topic_c_edit_post", "c.TopicCEditPost", TopicCEditPost{ID: 0, Source: "", Ref: ""}) - tmpls.AddStd("topic_c_attach_item", "c.TopicCAttachItem", TopicCAttachItem{ID: 1, ImgSrc: "", Path: "", FullPath: ""}) - tmpls.AddStd("topic_c_poll_input", "c.TopicCPollInput", TopicCPollInput{Index:0}) + t.AddStd("topic_c_edit_post", "c.TopicCEditPost", TopicCEditPost{ID: 0, Source: "", Ref: ""}) + t.AddStd("topic_c_attach_item", "c.TopicCAttachItem", TopicCAttachItem{ID: 1, ImgSrc: "", Path: "", FullPath: ""}) + t.AddStd("topic_c_poll_input", "c.TopicCPollInput", TopicCPollInput{Index:0}) - tmpls.AddStd("notice", "string", "nonono") + parti := []*User{&user} + convo := &Conversation{1,user.ID,time.Now(),0,time.Now()} + convoItems := []ConvoViewRow{ConvoViewRow{&ConversationPost{1,1,"hey","",user.ID}, &user, "", 4, true}} + convoPage := ConvoViewPage{header, convo, convoItems, parti, Paginator{[]int{1}, 1, 1}} + t.AddStd("convo", "c.ConvoViewPage", convoPage) + + t.AddStd("notice", "string", "nonono") dirPrefix := "./tmpl_client/" writeTemplate := func(name string, content string) { @@ -568,7 +574,7 @@ func compileJSTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName stri } log.Print("Writing the templates") - for name, titem := range tmpls { + for name, titem := range t { log.Print("Writing " + name) varList := make(map[string]tmpl.VarItem) tmpl, err := c.Compile(name+".html", "templates/", titem.Expects, titem.ExpectsInt, varList) @@ -802,22 +808,22 @@ func initDefaultTmplFuncMap() { DefaultTemplateFuncMap = fmap } -func loadTemplates(tmpls *template.Template, themeName string) error { - tmpls.Funcs(DefaultTemplateFuncMap) - templateFiles, err := filepath.Glob("templates/*.html") +func loadTemplates(t *template.Template, themeName string) error { + t.Funcs(DefaultTemplateFuncMap) + tFiles, err := filepath.Glob("templates/*.html") if err != nil { return err } - templateFileMap := make(map[string]int) - for index, path := range templateFiles { + tFileMap := make(map[string]int) + for index, path := range tFiles { path = strings.Replace(path, "\\", "/", -1) log.Print("templateFile: ", path) if skipCTmpl(path) { log.Print("skipping") continue } - templateFileMap[path] = index + tFileMap[path] = index } overrideFiles, err := filepath.Glob("templates/overrides/*.html") @@ -831,13 +837,13 @@ func loadTemplates(tmpls *template.Template, themeName string) error { log.Print("skipping") continue } - index, ok := templateFileMap["templates/"+strings.TrimPrefix(path, "templates/overrides/")] + index, ok := tFileMap["templates/"+strings.TrimPrefix(path, "templates/overrides/")] if !ok { log.Print("not ok: templates/" + strings.TrimPrefix(path, "templates/overrides/")) - templateFiles = append(templateFiles, path) + tFiles = append(tFiles, path) continue } - templateFiles[index] = path + tFiles[index] = path } if themeName != "" { @@ -852,18 +858,18 @@ func loadTemplates(tmpls *template.Template, themeName string) error { log.Print("skipping") continue } - index, ok := templateFileMap["templates/"+strings.TrimPrefix(path, "themes/"+themeName+"/overrides/")] + index, ok := tFileMap["templates/"+strings.TrimPrefix(path, "themes/"+themeName+"/overrides/")] if !ok { log.Print("not ok: templates/" + strings.TrimPrefix(path, "themes/"+themeName+"/overrides/")) - templateFiles = append(templateFiles, path) + tFiles = append(tFiles, path) continue } - templateFiles[index] = path + tFiles[index] = path } } - template.Must(tmpls.ParseFiles(templateFiles...)) - template.Must(tmpls.ParseGlob("pages/*")) + template.Must(t.ParseFiles(tFiles...)) + template.Must(t.ParseGlob("pages/*")) return nil } diff --git a/common/thaw.go b/common/thaw.go index 635c8d5e..503f80a0 100644 --- a/common/thaw.go +++ b/common/thaw.go @@ -18,23 +18,23 @@ type SingleServerThaw struct { } func NewSingleServerThaw() *SingleServerThaw { - thaw := &SingleServerThaw{} + t := &SingleServerThaw{} if Config.ServerCount == 1 { - AddScheduledSecondTask(thaw.Tick) + AddScheduledSecondTask(t.Tick) } - return thaw + return t } -func (thaw *SingleServerThaw) Thawed() bool { +func (t *SingleServerThaw) Thawed() bool { if Config.ServerCount == 1 { - return thaw.DefaultThaw.Thawed() + return t.DefaultThaw.Thawed() } return true } -func (thaw *SingleServerThaw) Thaw() { +func (t *SingleServerThaw) Thaw() { if Config.ServerCount == 1 { - thaw.DefaultThaw.Thaw() + t.DefaultThaw.Thaw() } } @@ -43,24 +43,24 @@ type DefaultThaw struct { } func NewDefaultThaw() *DefaultThaw { - thaw := &DefaultThaw{} - AddScheduledSecondTask(thaw.Tick) - return thaw + t := &DefaultThaw{} + AddScheduledSecondTask(t.Tick) + return t } // Decrement the thawed counter once a second until it goes cold -func (thaw *DefaultThaw) Tick() error { - prior := thaw.thawed +func (t *DefaultThaw) Tick() error { + prior := t.thawed if prior > 0 { - atomic.StoreInt64(&thaw.thawed, prior-1) + atomic.StoreInt64(&t.thawed, prior-1) } return nil } -func (thaw *DefaultThaw) Thawed() bool { - return thaw.thawed > 0 +func (t *DefaultThaw) Thawed() bool { + return t.thawed > 0 } -func (thaw *DefaultThaw) Thaw() { - atomic.StoreInt64(&thaw.thawed, 5) +func (t *DefaultThaw) Thaw() { + atomic.StoreInt64(&t.thawed, 5) } diff --git a/common/theme.go b/common/theme.go index a2c11b34..ccc0a4a0 100644 --- a/common/theme.go +++ b/common/theme.go @@ -145,8 +145,8 @@ func (theme *Theme) AddThemeStaticFiles() error { var ext = filepath.Ext(path) if ext == ".css" && len(data) != 0 { var b bytes.Buffer - var pieces = strings.Split(path, "/") - var filename = pieces[len(pieces)-1] + pieces := strings.Split(path, "/") + filename := pieces[len(pieces)-1] // TODO: Prepare resource templates for each loaded langpack? err = theme.ResourceTemplates.ExecuteTemplate(&b, filename, CSSData{Phrases: phraseMap}) if err != nil { diff --git a/common/user.go b/common/user.go index 3ab504e4..d5262e57 100644 --- a/common/user.go +++ b/common/user.go @@ -376,9 +376,15 @@ func (u *User) IncreasePostStats(wcount int, topic bool) (err error) { return err } //log.Print(u.Score + baseScore + mod) - //log.Print(getLevel(u.Score + baseScore + mod)) // TODO: Use a transaction to prevent level desyncs? - _, err = userStmts.updateLevel.Exec(GetLevel(u.Score+baseScore+mod), u.ID) + level := GetLevel(u.Score+baseScore+mod) + //log.Print(level) + _, err = userStmts.updateLevel.Exec(level, u.ID) + if err != nil { + return err + } + err = GroupPromotions.PromoteIfEligible(u,level) + u.CacheRemove() return err } @@ -413,6 +419,7 @@ func (u *User) DecreasePostStats(wcount int, topic bool) (err error) { } // TODO: Use a transaction to prevent level desyncs? _, err = userStmts.updateLevel.Exec(GetLevel(u.Score-baseScore-mod), u.ID) + u.CacheRemove() return err } diff --git a/common/ws_hub.go b/common/ws_hub.go index 6385867b..b403795e 100644 --- a/common/ws_hub.go +++ b/common/ws_hub.go @@ -47,7 +47,7 @@ func (hub *WsHubImpl) Start() { go func() { for { - var item = func(lock *sync.RWMutex, userMap map[int]*WSUser) { + item := func(lock *sync.RWMutex, userMap map[int]*WSUser) { lock.RLock() defer lock.RUnlock() // TODO: Copy to temporary slice for less contention? diff --git a/extend/guilds/lib/guilds.go b/extend/guilds/lib/guilds.go index 95747cec..770477aa 100644 --- a/extend/guilds/lib/guilds.go +++ b/extend/guilds/lib/guilds.go @@ -408,7 +408,7 @@ func TopicCreatePreLoop(args ...interface{}) interface{} { // TODO: Add privacy options // TODO: Add support for multiple boards and add per-board simplified permissions -// TODO: Take isJs into account for routes which expect JSON responses +// TODO: Take js into account for routes which expect JSON responses func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) { var r = args[1].(*http.Request) var fid = args[3].(*int) @@ -416,7 +416,7 @@ func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) { if forum.ParentType == "guild" { var err error - var w = args[0].(http.ResponseWriter) + w := args[0].(http.ResponseWriter) guildItem, ok := r.Context().Value("guilds_current_group").(*Guild) if !ok { guildItem, err = Gstore.Get(forum.ParentID) @@ -429,7 +429,7 @@ func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) { r = r.WithContext(context.WithValue(r.Context(), "guilds_current_group", guildItem)) } - var user = args[2].(*c.User) + user := args[2].(*c.User) var rank int var posts int var joinedAt string @@ -474,15 +474,14 @@ func ForumCheck(args ...interface{}) (skip bool, rerr c.RouteError) { // TODO: Override redirects? I don't think this is needed quite yet func Widgets(args ...interface{}) interface{} { - var zone = args[0].(string) - var header = args[2].(*c.Header) - var request = args[3].(*http.Request) - + zone := args[0].(string) + header := args[2].(*c.Header) + request := args[3].(*http.Request) if zone != "view_forum" { return false } - var forum = args[1].(*c.Forum) + forum := args[1].(*c.Forum) if forum.ParentType == "guild" { // This is why I hate using contexts, all the daisy chains and interface casts x.x guildItem, ok := request.Context().Value("guilds_current_group").(*Guild) diff --git a/gen_router.go b/gen_router.go index ce9d104f..bbc84904 100644 --- a/gen_router.go +++ b/gen_router.go @@ -102,6 +102,9 @@ var RouteMap = map[string]interface{}{ "panel.AnalyticsForums": panel.AnalyticsForums, "panel.Groups": panel.Groups, "panel.GroupsEdit": panel.GroupsEdit, + "panel.GroupsEditPromotions": panel.GroupsEditPromotions, + "panel.GroupsPromotionsCreateSubmit": panel.GroupsPromotionsCreateSubmit, + "panel.GroupsPromotionsDeleteSubmit": panel.GroupsPromotionsDeleteSubmit, "panel.GroupsEditPerms": panel.GroupsEditPerms, "panel.GroupsEditSubmit": panel.GroupsEditSubmit, "panel.GroupsEditPermsSubmit": panel.GroupsEditPermsSubmit, @@ -262,83 +265,86 @@ var routeMapEnum = map[string]int{ "panel.AnalyticsForums": 76, "panel.Groups": 77, "panel.GroupsEdit": 78, - "panel.GroupsEditPerms": 79, - "panel.GroupsEditSubmit": 80, - "panel.GroupsEditPermsSubmit": 81, - "panel.GroupsCreateSubmit": 82, - "panel.Backups": 83, - "panel.LogsRegs": 84, - "panel.LogsMod": 85, - "panel.Debug": 86, - "panel.Dashboard": 87, - "routes.AccountEdit": 88, - "routes.AccountEditPassword": 89, - "routes.AccountEditPasswordSubmit": 90, - "routes.AccountEditAvatarSubmit": 91, - "routes.AccountEditRevokeAvatarSubmit": 92, - "routes.AccountEditUsernameSubmit": 93, - "routes.AccountEditMFA": 94, - "routes.AccountEditMFASetup": 95, - "routes.AccountEditMFASetupSubmit": 96, - "routes.AccountEditMFADisableSubmit": 97, - "routes.AccountEditEmail": 98, - "routes.AccountEditEmailTokenSubmit": 99, - "routes.AccountLogins": 100, - "routes.LevelList": 101, - "routes.Convos": 102, - "routes.ConvosCreate": 103, - "routes.Convo": 104, - "routes.ConvosCreateSubmit": 105, - "routes.ConvosCreateReplySubmit": 106, - "routes.ConvosDeleteReplySubmit": 107, - "routes.ConvosEditReplySubmit": 108, - "routes.ViewProfile": 109, - "routes.BanUserSubmit": 110, - "routes.UnbanUser": 111, - "routes.ActivateUser": 112, - "routes.IPSearch": 113, - "routes.CreateTopicSubmit": 114, - "routes.EditTopicSubmit": 115, - "routes.DeleteTopicSubmit": 116, - "routes.StickTopicSubmit": 117, - "routes.UnstickTopicSubmit": 118, - "routes.LockTopicSubmit": 119, - "routes.UnlockTopicSubmit": 120, - "routes.MoveTopicSubmit": 121, - "routes.LikeTopicSubmit": 122, - "routes.AddAttachToTopicSubmit": 123, - "routes.RemoveAttachFromTopicSubmit": 124, - "routes.ViewTopic": 125, - "routes.CreateReplySubmit": 126, - "routes.ReplyEditSubmit": 127, - "routes.ReplyDeleteSubmit": 128, - "routes.ReplyLikeSubmit": 129, - "routes.AddAttachToReplySubmit": 130, - "routes.RemoveAttachFromReplySubmit": 131, - "routes.ProfileReplyCreateSubmit": 132, - "routes.ProfileReplyEditSubmit": 133, - "routes.ProfileReplyDeleteSubmit": 134, - "routes.PollVote": 135, - "routes.PollResults": 136, - "routes.AccountLogin": 137, - "routes.AccountRegister": 138, - "routes.AccountLogout": 139, - "routes.AccountLoginSubmit": 140, - "routes.AccountLoginMFAVerify": 141, - "routes.AccountLoginMFAVerifySubmit": 142, - "routes.AccountRegisterSubmit": 143, - "routes.AccountPasswordReset": 144, - "routes.AccountPasswordResetSubmit": 145, - "routes.AccountPasswordResetToken": 146, - "routes.AccountPasswordResetTokenSubmit": 147, - "routes.DynamicRoute": 148, - "routes.UploadedFile": 149, - "routes.StaticFile": 150, - "routes.RobotsTxt": 151, - "routes.SitemapXml": 152, - "routes.OpenSearchXml": 153, - "routes.BadRoute": 154, - "routes.HTTPSRedirect": 155, + "panel.GroupsEditPromotions": 79, + "panel.GroupsPromotionsCreateSubmit": 80, + "panel.GroupsPromotionsDeleteSubmit": 81, + "panel.GroupsEditPerms": 82, + "panel.GroupsEditSubmit": 83, + "panel.GroupsEditPermsSubmit": 84, + "panel.GroupsCreateSubmit": 85, + "panel.Backups": 86, + "panel.LogsRegs": 87, + "panel.LogsMod": 88, + "panel.Debug": 89, + "panel.Dashboard": 90, + "routes.AccountEdit": 91, + "routes.AccountEditPassword": 92, + "routes.AccountEditPasswordSubmit": 93, + "routes.AccountEditAvatarSubmit": 94, + "routes.AccountEditRevokeAvatarSubmit": 95, + "routes.AccountEditUsernameSubmit": 96, + "routes.AccountEditMFA": 97, + "routes.AccountEditMFASetup": 98, + "routes.AccountEditMFASetupSubmit": 99, + "routes.AccountEditMFADisableSubmit": 100, + "routes.AccountEditEmail": 101, + "routes.AccountEditEmailTokenSubmit": 102, + "routes.AccountLogins": 103, + "routes.LevelList": 104, + "routes.Convos": 105, + "routes.ConvosCreate": 106, + "routes.Convo": 107, + "routes.ConvosCreateSubmit": 108, + "routes.ConvosCreateReplySubmit": 109, + "routes.ConvosDeleteReplySubmit": 110, + "routes.ConvosEditReplySubmit": 111, + "routes.ViewProfile": 112, + "routes.BanUserSubmit": 113, + "routes.UnbanUser": 114, + "routes.ActivateUser": 115, + "routes.IPSearch": 116, + "routes.CreateTopicSubmit": 117, + "routes.EditTopicSubmit": 118, + "routes.DeleteTopicSubmit": 119, + "routes.StickTopicSubmit": 120, + "routes.UnstickTopicSubmit": 121, + "routes.LockTopicSubmit": 122, + "routes.UnlockTopicSubmit": 123, + "routes.MoveTopicSubmit": 124, + "routes.LikeTopicSubmit": 125, + "routes.AddAttachToTopicSubmit": 126, + "routes.RemoveAttachFromTopicSubmit": 127, + "routes.ViewTopic": 128, + "routes.CreateReplySubmit": 129, + "routes.ReplyEditSubmit": 130, + "routes.ReplyDeleteSubmit": 131, + "routes.ReplyLikeSubmit": 132, + "routes.AddAttachToReplySubmit": 133, + "routes.RemoveAttachFromReplySubmit": 134, + "routes.ProfileReplyCreateSubmit": 135, + "routes.ProfileReplyEditSubmit": 136, + "routes.ProfileReplyDeleteSubmit": 137, + "routes.PollVote": 138, + "routes.PollResults": 139, + "routes.AccountLogin": 140, + "routes.AccountRegister": 141, + "routes.AccountLogout": 142, + "routes.AccountLoginSubmit": 143, + "routes.AccountLoginMFAVerify": 144, + "routes.AccountLoginMFAVerifySubmit": 145, + "routes.AccountRegisterSubmit": 146, + "routes.AccountPasswordReset": 147, + "routes.AccountPasswordResetSubmit": 148, + "routes.AccountPasswordResetToken": 149, + "routes.AccountPasswordResetTokenSubmit": 150, + "routes.DynamicRoute": 151, + "routes.UploadedFile": 152, + "routes.StaticFile": 153, + "routes.RobotsTxt": 154, + "routes.SitemapXml": 155, + "routes.OpenSearchXml": 156, + "routes.BadRoute": 157, + "routes.HTTPSRedirect": 158, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -420,83 +426,86 @@ var reverseRouteMapEnum = map[int]string{ 76: "panel.AnalyticsForums", 77: "panel.Groups", 78: "panel.GroupsEdit", - 79: "panel.GroupsEditPerms", - 80: "panel.GroupsEditSubmit", - 81: "panel.GroupsEditPermsSubmit", - 82: "panel.GroupsCreateSubmit", - 83: "panel.Backups", - 84: "panel.LogsRegs", - 85: "panel.LogsMod", - 86: "panel.Debug", - 87: "panel.Dashboard", - 88: "routes.AccountEdit", - 89: "routes.AccountEditPassword", - 90: "routes.AccountEditPasswordSubmit", - 91: "routes.AccountEditAvatarSubmit", - 92: "routes.AccountEditRevokeAvatarSubmit", - 93: "routes.AccountEditUsernameSubmit", - 94: "routes.AccountEditMFA", - 95: "routes.AccountEditMFASetup", - 96: "routes.AccountEditMFASetupSubmit", - 97: "routes.AccountEditMFADisableSubmit", - 98: "routes.AccountEditEmail", - 99: "routes.AccountEditEmailTokenSubmit", - 100: "routes.AccountLogins", - 101: "routes.LevelList", - 102: "routes.Convos", - 103: "routes.ConvosCreate", - 104: "routes.Convo", - 105: "routes.ConvosCreateSubmit", - 106: "routes.ConvosCreateReplySubmit", - 107: "routes.ConvosDeleteReplySubmit", - 108: "routes.ConvosEditReplySubmit", - 109: "routes.ViewProfile", - 110: "routes.BanUserSubmit", - 111: "routes.UnbanUser", - 112: "routes.ActivateUser", - 113: "routes.IPSearch", - 114: "routes.CreateTopicSubmit", - 115: "routes.EditTopicSubmit", - 116: "routes.DeleteTopicSubmit", - 117: "routes.StickTopicSubmit", - 118: "routes.UnstickTopicSubmit", - 119: "routes.LockTopicSubmit", - 120: "routes.UnlockTopicSubmit", - 121: "routes.MoveTopicSubmit", - 122: "routes.LikeTopicSubmit", - 123: "routes.AddAttachToTopicSubmit", - 124: "routes.RemoveAttachFromTopicSubmit", - 125: "routes.ViewTopic", - 126: "routes.CreateReplySubmit", - 127: "routes.ReplyEditSubmit", - 128: "routes.ReplyDeleteSubmit", - 129: "routes.ReplyLikeSubmit", - 130: "routes.AddAttachToReplySubmit", - 131: "routes.RemoveAttachFromReplySubmit", - 132: "routes.ProfileReplyCreateSubmit", - 133: "routes.ProfileReplyEditSubmit", - 134: "routes.ProfileReplyDeleteSubmit", - 135: "routes.PollVote", - 136: "routes.PollResults", - 137: "routes.AccountLogin", - 138: "routes.AccountRegister", - 139: "routes.AccountLogout", - 140: "routes.AccountLoginSubmit", - 141: "routes.AccountLoginMFAVerify", - 142: "routes.AccountLoginMFAVerifySubmit", - 143: "routes.AccountRegisterSubmit", - 144: "routes.AccountPasswordReset", - 145: "routes.AccountPasswordResetSubmit", - 146: "routes.AccountPasswordResetToken", - 147: "routes.AccountPasswordResetTokenSubmit", - 148: "routes.DynamicRoute", - 149: "routes.UploadedFile", - 150: "routes.StaticFile", - 151: "routes.RobotsTxt", - 152: "routes.SitemapXml", - 153: "routes.OpenSearchXml", - 154: "routes.BadRoute", - 155: "routes.HTTPSRedirect", + 79: "panel.GroupsEditPromotions", + 80: "panel.GroupsPromotionsCreateSubmit", + 81: "panel.GroupsPromotionsDeleteSubmit", + 82: "panel.GroupsEditPerms", + 83: "panel.GroupsEditSubmit", + 84: "panel.GroupsEditPermsSubmit", + 85: "panel.GroupsCreateSubmit", + 86: "panel.Backups", + 87: "panel.LogsRegs", + 88: "panel.LogsMod", + 89: "panel.Debug", + 90: "panel.Dashboard", + 91: "routes.AccountEdit", + 92: "routes.AccountEditPassword", + 93: "routes.AccountEditPasswordSubmit", + 94: "routes.AccountEditAvatarSubmit", + 95: "routes.AccountEditRevokeAvatarSubmit", + 96: "routes.AccountEditUsernameSubmit", + 97: "routes.AccountEditMFA", + 98: "routes.AccountEditMFASetup", + 99: "routes.AccountEditMFASetupSubmit", + 100: "routes.AccountEditMFADisableSubmit", + 101: "routes.AccountEditEmail", + 102: "routes.AccountEditEmailTokenSubmit", + 103: "routes.AccountLogins", + 104: "routes.LevelList", + 105: "routes.Convos", + 106: "routes.ConvosCreate", + 107: "routes.Convo", + 108: "routes.ConvosCreateSubmit", + 109: "routes.ConvosCreateReplySubmit", + 110: "routes.ConvosDeleteReplySubmit", + 111: "routes.ConvosEditReplySubmit", + 112: "routes.ViewProfile", + 113: "routes.BanUserSubmit", + 114: "routes.UnbanUser", + 115: "routes.ActivateUser", + 116: "routes.IPSearch", + 117: "routes.CreateTopicSubmit", + 118: "routes.EditTopicSubmit", + 119: "routes.DeleteTopicSubmit", + 120: "routes.StickTopicSubmit", + 121: "routes.UnstickTopicSubmit", + 122: "routes.LockTopicSubmit", + 123: "routes.UnlockTopicSubmit", + 124: "routes.MoveTopicSubmit", + 125: "routes.LikeTopicSubmit", + 126: "routes.AddAttachToTopicSubmit", + 127: "routes.RemoveAttachFromTopicSubmit", + 128: "routes.ViewTopic", + 129: "routes.CreateReplySubmit", + 130: "routes.ReplyEditSubmit", + 131: "routes.ReplyDeleteSubmit", + 132: "routes.ReplyLikeSubmit", + 133: "routes.AddAttachToReplySubmit", + 134: "routes.RemoveAttachFromReplySubmit", + 135: "routes.ProfileReplyCreateSubmit", + 136: "routes.ProfileReplyEditSubmit", + 137: "routes.ProfileReplyDeleteSubmit", + 138: "routes.PollVote", + 139: "routes.PollResults", + 140: "routes.AccountLogin", + 141: "routes.AccountRegister", + 142: "routes.AccountLogout", + 143: "routes.AccountLoginSubmit", + 144: "routes.AccountLoginMFAVerify", + 145: "routes.AccountLoginMFAVerifySubmit", + 146: "routes.AccountRegisterSubmit", + 147: "routes.AccountPasswordReset", + 148: "routes.AccountPasswordResetSubmit", + 149: "routes.AccountPasswordResetToken", + 150: "routes.AccountPasswordResetTokenSubmit", + 151: "routes.DynamicRoute", + 152: "routes.UploadedFile", + 153: "routes.StaticFile", + 154: "routes.RobotsTxt", + 155: "routes.SitemapXml", + 156: "routes.OpenSearchXml", + 157: "routes.BadRoute", + 158: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -654,7 +663,7 @@ type HTTPSRedirect struct {} func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - counters.RouteViewCounter.Bump(155) + counters.RouteViewCounter.Bump(158) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -863,7 +872,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { counters.GlobalViewCounter.Bump() if prefix == "/s" { //old prefix: /static - counters.RouteViewCounter.Bump(150) + counters.RouteViewCounter.Bump(153) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -1611,8 +1620,27 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c case "/panel/groups/edit/": counters.RouteViewCounter.Bump(78) err = panel.GroupsEdit(w,req,user,extraData) - case "/panel/groups/edit/perms/": + case "/panel/groups/edit/promotions/": counters.RouteViewCounter.Bump(79) + err = panel.GroupsEditPromotions(w,req,user,extraData) + case "/panel/groups/promotions/create/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(80) + err = panel.GroupsPromotionsCreateSubmit(w,req,user,extraData) + case "/panel/groups/promotions/delete/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + counters.RouteViewCounter.Bump(81) + err = panel.GroupsPromotionsDeleteSubmit(w,req,user,extraData) + case "/panel/groups/edit/perms/": + counters.RouteViewCounter.Bump(82) err = panel.GroupsEditPerms(w,req,user,extraData) case "/panel/groups/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1620,7 +1648,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(80) + counters.RouteViewCounter.Bump(83) err = panel.GroupsEditSubmit(w,req,user,extraData) case "/panel/groups/edit/perms/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1628,7 +1656,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(81) + counters.RouteViewCounter.Bump(84) err = panel.GroupsEditPermsSubmit(w,req,user,extraData) case "/panel/groups/create/": err = c.NoSessionMismatch(w,req,user) @@ -1636,7 +1664,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(82) + counters.RouteViewCounter.Bump(85) err = panel.GroupsCreateSubmit(w,req,user) case "/panel/backups/": err = c.SuperAdminOnly(w,req,user) @@ -1650,13 +1678,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(83) + counters.RouteViewCounter.Bump(86) err = panel.Backups(w,req,user,extraData) case "/panel/logs/regs/": - counters.RouteViewCounter.Bump(84) + counters.RouteViewCounter.Bump(87) err = panel.LogsRegs(w,req,user) case "/panel/logs/mod/": - counters.RouteViewCounter.Bump(85) + counters.RouteViewCounter.Bump(88) err = panel.LogsMod(w,req,user) case "/panel/debug/": err = c.AdminOnly(w,req,user) @@ -1664,10 +1692,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(86) + counters.RouteViewCounter.Bump(89) err = panel.Debug(w,req,user) default: - counters.RouteViewCounter.Bump(87) + counters.RouteViewCounter.Bump(90) err = panel.Dashboard(w,req,user) } case "/user": @@ -1678,7 +1706,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(88) + counters.RouteViewCounter.Bump(91) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1690,7 +1718,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(89) + counters.RouteViewCounter.Bump(92) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1707,7 +1735,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(90) + counters.RouteViewCounter.Bump(93) err = routes.AccountEditPasswordSubmit(w,req,user) case "/user/edit/avatar/submit/": err = c.MemberOnly(w,req,user) @@ -1724,7 +1752,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(91) + counters.RouteViewCounter.Bump(94) err = routes.AccountEditAvatarSubmit(w,req,user) case "/user/edit/avatar/revoke/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1737,7 +1765,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(92) + counters.RouteViewCounter.Bump(95) err = routes.AccountEditRevokeAvatarSubmit(w,req,user) case "/user/edit/username/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1750,7 +1778,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(93) + counters.RouteViewCounter.Bump(96) err = routes.AccountEditUsernameSubmit(w,req,user) case "/user/edit/mfa/": err = c.MemberOnly(w,req,user) @@ -1758,7 +1786,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(94) + counters.RouteViewCounter.Bump(97) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1770,7 +1798,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(95) + counters.RouteViewCounter.Bump(98) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1787,7 +1815,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(96) + counters.RouteViewCounter.Bump(99) err = routes.AccountEditMFASetupSubmit(w,req,user) case "/user/edit/mfa/disable/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1800,7 +1828,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(97) + counters.RouteViewCounter.Bump(100) err = routes.AccountEditMFADisableSubmit(w,req,user) case "/user/edit/email/": err = c.MemberOnly(w,req,user) @@ -1808,14 +1836,14 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(98) + counters.RouteViewCounter.Bump(101) 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(99) + counters.RouteViewCounter.Bump(102) err = routes.AccountEditEmailTokenSubmit(w,req,user,extraData) case "/user/edit/logins/": err = c.MemberOnly(w,req,user) @@ -1823,7 +1851,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(100) + counters.RouteViewCounter.Bump(103) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1835,7 +1863,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(101) + counters.RouteViewCounter.Bump(104) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1847,7 +1875,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(102) + counters.RouteViewCounter.Bump(105) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1859,7 +1887,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(103) + counters.RouteViewCounter.Bump(106) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1871,7 +1899,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(104) + counters.RouteViewCounter.Bump(107) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1888,7 +1916,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(105) + counters.RouteViewCounter.Bump(108) err = routes.ConvosCreateSubmit(w,req,user) case "/user/convo/create/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1901,7 +1929,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(106) + counters.RouteViewCounter.Bump(109) err = routes.ConvosCreateReplySubmit(w,req,user,extraData) case "/user/convo/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1914,7 +1942,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(107) + counters.RouteViewCounter.Bump(110) err = routes.ConvosDeleteReplySubmit(w,req,user,extraData) case "/user/convo/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -1927,11 +1955,11 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(108) + counters.RouteViewCounter.Bump(111) err = routes.ConvosEditReplySubmit(w,req,user,extraData) default: req.URL.Path += extraData - counters.RouteViewCounter.Bump(109) + counters.RouteViewCounter.Bump(112) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -1951,7 +1979,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(110) + counters.RouteViewCounter.Bump(113) err = routes.BanUserSubmit(w,req,user,extraData) case "/users/unban/": err = c.NoSessionMismatch(w,req,user) @@ -1964,7 +1992,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(111) + counters.RouteViewCounter.Bump(114) err = routes.UnbanUser(w,req,user,extraData) case "/users/activate/": err = c.NoSessionMismatch(w,req,user) @@ -1977,7 +2005,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(112) + counters.RouteViewCounter.Bump(115) err = routes.ActivateUser(w,req,user,extraData) case "/users/ips/": err = c.MemberOnly(w,req,user) @@ -1985,7 +2013,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(113) + counters.RouteViewCounter.Bump(116) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2009,7 +2037,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(114) + counters.RouteViewCounter.Bump(117) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2022,7 +2050,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(115) + counters.RouteViewCounter.Bump(118) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2036,7 +2064,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(116) + counters.RouteViewCounter.Bump(119) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2049,7 +2077,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(117) + counters.RouteViewCounter.Bump(120) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2062,7 +2090,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(118) + counters.RouteViewCounter.Bump(121) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2076,7 +2104,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - counters.RouteViewCounter.Bump(119) + counters.RouteViewCounter.Bump(122) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2089,7 +2117,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(120) + counters.RouteViewCounter.Bump(123) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2102,7 +2130,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(121) + counters.RouteViewCounter.Bump(124) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2115,7 +2143,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(122) + counters.RouteViewCounter.Bump(125) err = routes.LikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2132,7 +2160,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(123) + counters.RouteViewCounter.Bump(126) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2145,10 +2173,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(124) + counters.RouteViewCounter.Bump(127) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - counters.RouteViewCounter.Bump(125) + counters.RouteViewCounter.Bump(128) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2172,7 +2200,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(126) + counters.RouteViewCounter.Bump(129) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2185,7 +2213,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(127) + counters.RouteViewCounter.Bump(130) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2198,7 +2226,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(128) + counters.RouteViewCounter.Bump(131) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2211,7 +2239,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(129) + counters.RouteViewCounter.Bump(132) err = routes.ReplyLikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2228,7 +2256,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(130) + counters.RouteViewCounter.Bump(133) err = routes.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2241,7 +2269,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(131) + counters.RouteViewCounter.Bump(134) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2257,7 +2285,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(132) + counters.RouteViewCounter.Bump(135) err = routes.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2270,7 +2298,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(133) + counters.RouteViewCounter.Bump(136) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2283,7 +2311,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(134) + counters.RouteViewCounter.Bump(137) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2299,23 +2327,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(135) + counters.RouteViewCounter.Bump(138) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - counters.RouteViewCounter.Bump(136) + counters.RouteViewCounter.Bump(139) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - counters.RouteViewCounter.Bump(137) + counters.RouteViewCounter.Bump(140) 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(138) + counters.RouteViewCounter.Bump(141) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2332,7 +2360,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(139) + counters.RouteViewCounter.Bump(142) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2340,10 +2368,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(140) + counters.RouteViewCounter.Bump(143) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - counters.RouteViewCounter.Bump(141) + counters.RouteViewCounter.Bump(144) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2355,7 +2383,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(142) + counters.RouteViewCounter.Bump(145) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2363,10 +2391,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(143) + counters.RouteViewCounter.Bump(146) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - counters.RouteViewCounter.Bump(144) + counters.RouteViewCounter.Bump(147) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2378,10 +2406,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(145) + counters.RouteViewCounter.Bump(148) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - counters.RouteViewCounter.Bump(146) + counters.RouteViewCounter.Bump(149) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2393,7 +2421,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - counters.RouteViewCounter.Bump(147) + counters.RouteViewCounter.Bump(150) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2410,7 +2438,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c h.Del("Content-Type") h.Del("Content-Encoding") } - counters.RouteViewCounter.Bump(149) + counters.RouteViewCounter.Bump(152) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2420,7 +2448,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(151) + counters.RouteViewCounter.Bump(154) return routes.RobotsTxt(w,req) case "favicon.ico": gzw, ok := w.(c.GzipResponseWriter) @@ -2434,10 +2462,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c routes.StaticFile(w,req) return nil case "opensearch.xml": - counters.RouteViewCounter.Bump(153) + counters.RouteViewCounter.Bump(156) return routes.OpenSearchXml(w,req) /*case "sitemap.xml": - counters.RouteViewCounter.Bump(152) + counters.RouteViewCounter.Bump(155) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2448,7 +2476,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - counters.RouteViewCounter.Bump(148) // TODO: Be more specific about *which* dynamic route it is + counters.RouteViewCounter.Bump(151) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2459,7 +2487,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - counters.RouteViewCounter.Bump(154) + counters.RouteViewCounter.Bump(157) return c.NotFound(w,req,nil) } return err diff --git a/gen_tables.go b/gen_tables.go index 44475f3c..e6ea0b27 100644 --- a/gen_tables.go +++ b/gen_tables.go @@ -2,24 +2,27 @@ package main var dbTablePrimaryKeys = map[string]string{ - "users_groups":"gid", - "users_avatar_queue":"uid", + "polls":"pollID", + "widgets":"wid", + "users_groups_promotions":"pid", + "topics":"tid", + "attachments":"attachID", "word_filters":"wfid", + "menu_items":"miid", + "users_groups":"gid", + "users_2fa_keys":"uid", + "activity_stream":"asid", + "conversations_posts":"pid", "menus":"mid", "login_logs":"lid", - "polls":"pollID", - "activity_stream":"asid", - "pages":"pid", "forums":"fid", - "topics":"tid", + "users_replies":"rid", + "conversations":"cid", "replies":"rid", - "attachments":"attachID", "revisions":"reviseID", - "users_2fa_keys":"uid", - "users_groups_scheduler":"uid", - "menu_items":"miid", + "pages":"pid", "registration_logs":"rlid", "users":"uid", - "users_replies":"rid", - "widgets":"wid", + "users_groups_scheduler":"uid", + "users_avatar_queue":"uid", } diff --git a/main.go b/main.go index 121a3aa6..4bb31425 100644 --- a/main.go +++ b/main.go @@ -138,6 +138,10 @@ func storeInit() (err error) { if err != nil { return errors.WithStack(err) } + c.GroupPromotions, err = c.NewDefaultGroupPromotionStore(acc) + if err != nil { + return errors.WithStack(err) + } err = phrases.InitPhrases(c.Site.Language) if err != nil { diff --git a/patcher/patches.go b/patcher/patches.go index 1c81b4df..e6b14758 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -37,6 +37,7 @@ func init() { addPatch(21, patch21) addPatch(22, patch22) addPatch(23, patch23) + addPatch(24, patch24) } func patch0(scanner *bufio.Scanner) (err error) { @@ -711,4 +712,26 @@ func patch23(scanner *bufio.Scanner) error { tC{"cid", "int", 0, false, false, ""}, }, nil, )) +} + +func patch24(scanner *bufio.Scanner) error { + err := execStmt(qgen.Builder.DropTable("users_groups_promotions")) + if err != nil { + return err + } + return execStmt(qgen.Builder.CreateTable("users_groups_promotions", "", "", + []tC{ + tC{"pid", "int", 0, false, true, ""}, + tC{"from_gid", "int", 0, false, false, ""}, + tC{"to_gid", "int", 0, false, false, ""}, + tC{"two_way", "boolean",0,false,false,"0"}, // If a user no longer meets the requirements for this promotion then they will be demoted if this flag is set + + // Requirements + tC{"level", "int", 0, false, false, ""}, + tC{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted + }, + []tblKey{ + tblKey{"pid", "primary","",false}, + }, + )) } \ No newline at end of file diff --git a/router_gen/routes.go b/router_gen/routes.go index 813608b7..5b2a79d0 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -248,6 +248,9 @@ func panelRoutes() *RouteGroup { View("panel.Groups", "/panel/groups/"), View("panel.GroupsEdit", "/panel/groups/edit/", "extraData"), + View("panel.GroupsEditPromotions", "/panel/groups/edit/promotions/", "extraData"), + Action("panel.GroupsPromotionsCreateSubmit", "/panel/groups/promotions/create/submit/","extraData"), + Action("panel.GroupsPromotionsDeleteSubmit", "/panel/groups/promotions/delete/submit/","extraData"), View("panel.GroupsEditPerms", "/panel/groups/edit/perms/", "extraData"), Action("panel.GroupsEditSubmit", "/panel/groups/edit/submit/", "extraData"), Action("panel.GroupsEditPermsSubmit", "/panel/groups/edit/perms/submit/", "extraData"), diff --git a/routes/convos.go b/routes/convos.go index 2f108b69..42428f8c 100644 --- a/routes/convos.go +++ b/routes/convos.go @@ -263,7 +263,7 @@ func ConvosEditReplySubmit(w http.ResponseWriter, r *http.Request, user c.User, if err != nil { return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) } - isJs := (r.PostFormValue("js") == "1") + js := (r.PostFormValue("js") == "1") post := &c.ConversationPost{ID: cpid} err = post.Fetch() @@ -296,7 +296,7 @@ func ConvosEditReplySubmit(w http.ResponseWriter, r *http.Request, user c.User, return c.InternalError(err, w, r) } - if !isJs { + if !js { http.Redirect(w, r, "/user/convo/"+strconv.Itoa(post.CID), http.StatusSeeOther) } else { w.Write(successJSONBytes) diff --git a/routes/panel/dashboard.go b/routes/panel/dashboard.go index fa5730d4..b7c4a141 100644 --- a/routes/panel/dashboard.go +++ b/routes/panel/dashboard.go @@ -29,8 +29,7 @@ type dashStmts struct { // TODO: Stop hard-coding these queries func dashMySQLStmts() (stmts dashStmts, err error) { db := qgen.Builder.GetConn() - - var prepareStmt = func(table string, ext string, dur string) *sql.Stmt { + prepareStmt := func(table string, ext string, dur string) *sql.Stmt { if err != nil { return nil } @@ -51,8 +50,7 @@ func dashMySQLStmts() (stmts dashStmts, err error) { // TODO: Stop hard-coding these queries func dashMSSQLStmts() (stmts dashStmts, err error) { db := qgen.Builder.GetConn() - - var prepareStmt = func(table string, ext string, dur string) *sql.Stmt { + prepareStmt := func(table string, ext string, dur string) *sql.Stmt { if err != nil { return nil } @@ -134,7 +132,7 @@ func Dashboard(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError // TODO: Add a stat store for this? var intErr error - var extractStat = func(stmt *sql.Stmt, args ...interface{}) (stat int) { + extractStat := func(stmt *sql.Stmt, args ...interface{}) (stat int) { err := stmt.QueryRow(args...).Scan(&stat) if err != nil && err != sql.ErrNoRows { intErr = err diff --git a/schema/mssql/query_users_groups_promotions.sql b/schema/mssql/query_users_groups_promotions.sql new file mode 100644 index 00000000..7cfc8f44 --- /dev/null +++ b/schema/mssql/query_users_groups_promotions.sql @@ -0,0 +1,9 @@ +CREATE TABLE [users_groups_promotions] ( + [pid] int not null IDENTITY, + [from_gid] int not null, + [to_gid] int not null, + [two_way] bit DEFAULT 0 not null, + [level] int not null, + [minTime] int not null, + primary key([pid]) +); \ No newline at end of file diff --git a/schema/mysql/query_users_groups_promotions.sql b/schema/mysql/query_users_groups_promotions.sql new file mode 100644 index 00000000..cf534c5a --- /dev/null +++ b/schema/mysql/query_users_groups_promotions.sql @@ -0,0 +1,9 @@ +CREATE TABLE `users_groups_promotions` ( + `pid` int not null AUTO_INCREMENT, + `from_gid` int not null, + `to_gid` int not null, + `two_way` boolean DEFAULT 0 not null, + `level` int not null, + `minTime` int not null, + primary key(`pid`) +) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; \ No newline at end of file diff --git a/schema/pgsql/query_users_groups_promotions.sql b/schema/pgsql/query_users_groups_promotions.sql new file mode 100644 index 00000000..5f1fc701 --- /dev/null +++ b/schema/pgsql/query_users_groups_promotions.sql @@ -0,0 +1,9 @@ +CREATE TABLE "users_groups_promotions" ( + `pid` serial not null, + `from_gid` int not null, + `to_gid` int not null, + `two_way` boolean DEFAULT 0 not null, + `level` int not null, + `minTime` int not null, + primary key(`pid`) +); \ No newline at end of file diff --git a/templates/convos.html b/templates/convos.html index 932e6f5b..7e140fa9 100644 --- a/templates/convos.html +++ b/templates/convos.html @@ -30,7 +30,7 @@ {{range .Users}}{{.Name}} {{end}} {{reltime .LastReplyAt}} +
{{end}} -
{{template "paginator.html" . }} \ No newline at end of file diff --git a/templates/panel_group_edit_promotions.html b/templates/panel_group_edit_promotions.html new file mode 100644 index 00000000..2c5ada0b --- /dev/null +++ b/templates/panel_group_edit_promotions.html @@ -0,0 +1,69 @@ +{{template "header.html" . }} +
+{{template "panel_group_menu.html" . }} +
+{{template "panel_before_head.html" . }} +
+

{{.Name}}{{lang "panel_group_head_suffix"}}

+
+
+
+ {{range .Promotions}} + {{end}} +
+ +
+
+
+ +{{if .CurrentUser.Perms.EditGroup}} +
+

Add Promotion

+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+
+
+{{end}} + +
+
+{{template "footer.html" . }} \ No newline at end of file diff --git a/templates/panel_group_menu.html b/templates/panel_group_menu.html index 6794ebd4..51f9503f 100644 --- a/templates/panel_group_menu.html +++ b/templates/panel_group_menu.html @@ -4,7 +4,7 @@
- +
{{template "panel_inner_menu.html" . }} diff --git a/templates/topic.html b/templates/topic.html index c8da2f69..fd5cdefd 100644 --- a/templates/topic.html +++ b/templates/topic.html @@ -31,7 +31,7 @@ {{end}}
-
+
{{.Topic.ContentHTML}}
{{if .CurrentUser.Loggedin}}{{end}} @@ -41,7 +41,7 @@ {{if .CurrentUser.Loggedin}} {{if .CurrentUser.Perms.LikeItem}} - {{end}} + {{end}} diff --git a/templates/topic_posts.html b/templates/topic_posts.html index 5dca6e21..5edf370b 100644 --- a/templates/topic_posts.html +++ b/templates/topic_posts.html @@ -1,11 +1,11 @@ -
{{range .ItemList}} +
{{range .ItemList}} {{if .ActionType}}
{{.ActionIcon}} {{.ActionType}}
{{else}} -
+
{{/** TODO: We might end up with
s in the inline editor, fix this **/}}
{{.ContentHtml}}
{{if $.CurrentUser.Loggedin}}
{{.Content}}
{{end}} diff --git a/templates/topics_quick_topic.html b/templates/topics_quick_topic.html index c6e86b47..7f9fb92a 100644 --- a/templates/topics_quick_topic.html +++ b/templates/topics_quick_topic.html @@ -1,4 +1,4 @@ - +
diff --git a/themes/nox/overrides/panel_group_menu.html b/themes/nox/overrides/panel_group_menu.html index 491bad4e..0341da19 100644 --- a/themes/nox/overrides/panel_group_menu.html +++ b/themes/nox/overrides/panel_group_menu.html @@ -7,7 +7,7 @@
{{template "panel_inner_menu.html" . }} diff --git a/themes/nox/public/panel.css b/themes/nox/public/panel.css index 43128432..669216c8 100644 --- a/themes/nox/public/panel.css +++ b/themes/nox/public/panel.css @@ -50,7 +50,7 @@ background-color: rgb(72, 72, 72); padding-top: 12px; padding-bottom: 12px; - padding-right: 20px; + padding-right: 22px; padding-left: 20px; } .above_right img { @@ -275,6 +275,9 @@ select + .timeRangeSelector { margin-top: 8px; margin-right: auto; } +.panel_group_promotions .formitem { + display: flex; +} .perm_preset_no_access:before { content: "{{lang "panel_perms_no_access" . }}"; diff --git a/themes/shadow/public/convo.css b/themes/shadow/public/convo.css index a50090b4..0e615767 100644 --- a/themes/shadow/public/convo.css +++ b/themes/shadow/public/convo.css @@ -1,3 +1,19 @@ .convos_item_user:not(:last-child):after { content: ","; +} + +.parti { + margin-bottom: 8px; +} +.parti .rowitem { + display: flex; +} +.parti_user:not(:last-child):after { + content: ","; +} + +.convo_row_box .rowitem { + background-repeat: no-repeat, repeat-y; + background-size: 128px; + padding-left: 136px; } \ No newline at end of file diff --git a/themes/tempra_simple/public/convo.css b/themes/tempra_simple/public/convo.css index a50090b4..25f13e86 100644 --- a/themes/tempra_simple/public/convo.css +++ b/themes/tempra_simple/public/convo.css @@ -1,3 +1,22 @@ .convos_item_user:not(:last-child):after { content: ","; +} +.to_left:after { + clear: both; +} + +/*.parti { + margin-bottom: 8px; +}*/ +.parti .rowitem { + display: flex; +} +.parti_user:not(:last-child):after { + content: ","; +} + +.convo_row_box .rowitem { + background-repeat: no-repeat, repeat-y; + background-size: 128px; + padding-left: 136px; } \ No newline at end of file