From 2e28ae39f3c30d995eec5d8f5f93da9db6252d0d Mon Sep 17 00:00:00 2001 From: Azareal Date: Mon, 7 Oct 2019 08:20:37 +1000 Subject: [PATCH] Add posts as a criteria for group promotions. Load the posts column when loading users. Make DefaultUserStore.Reload a wrapper around DefaultUserStore.BypassGet. Use the OO style query builder more in common/user.go GroupPromotions is now bound to an interface rather than the concrete type. Fix some issues in the GroupPromotionStore interface. Improve error handling for table creation and reduce boilerplate. Shorten some more things. Add panel_group_promotions_posts phrase. You will need to run the patcher / updater for this commit. --- cmd/query_gen/tables.go | 113 ++++++++++-------- common/promotions.go | 33 ++--- common/reply.go | 2 +- common/template_init.go | 6 +- common/topic.go | 2 +- common/user.go | 95 +++++++-------- common/user_store.go | 26 ++-- general_test.go | 3 +- langs/english.json | 1 + patcher/patches.go | 5 + query_gen/install.go | 60 +++++----- routes/panel/forums.go | 6 +- routes/panel/groups.go | 76 ++++++------ .../mssql/query_users_groups_promotions.sql | 1 + .../mysql/query_users_groups_promotions.sql | 1 + .../pgsql/query_users_groups_promotions.sql | 1 + templates/panel_group_edit_promotions.html | 4 + 17 files changed, 230 insertions(+), 205 deletions(-) diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index c2eb48c1..5189dfd4 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -9,8 +9,14 @@ type tblColumn = qgen.DBTableColumn type tC = tblColumn type tblKey = qgen.DBTableKey -func createTables(adapter qgen.Adapter) error { - qgen.Install.CreateTable("users", mysqlPre, mysqlCol, +func createTables(adapter qgen.Adapter) (err error) { + createTable := func(table string, charset string, collation string, columns []qgen.DBTableColumn, keys []qgen.DBTableKey) { + if err != nil { + return + } + err = qgen.Install.CreateTable(table, charset, collation, columns, keys) + } + createTable("users", mysqlPre, mysqlCol, []tC{ tC{"uid", "int", 0, false, true, ""}, tC{"name", "varchar", 100, false, false, ""}, @@ -51,7 +57,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("users_groups", mysqlPre, mysqlCol, + createTable("users_groups", mysqlPre, mysqlCol, []tC{ tC{"gid", "int", 0, false, true, ""}, tC{"name", "varchar", 100, false, false, ""}, @@ -69,7 +75,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("users_groups_promotions", mysqlPre, mysqlCol, + createTable("users_groups_promotions", mysqlPre, mysqlCol, []tC{ tC{"pid", "int", 0, false, true, ""}, tC{"from_gid", "int", 0, false, false, ""}, @@ -78,6 +84,7 @@ func createTables(adapter qgen.Adapter) error { // Requirements tC{"level", "int", 0, false, false, ""}, + tC{"posts", "int", 0, false, false, "0"}, tC{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted }, []tblKey{ @@ -85,7 +92,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("users_2fa_keys", mysqlPre, mysqlCol, + createTable("users_2fa_keys", mysqlPre, mysqlCol, []tC{ tC{"uid", "int", 0, false, false, ""}, tC{"secret", "varchar", 100, false, false, ""}, @@ -110,7 +117,7 @@ func createTables(adapter qgen.Adapter) error { // TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag // TODO: Add a penalty type where a user is stopped from creating plugin_guilds social groups // TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly. - /*qgen.Install.CreateTable("users_penalties","","", + /*createTable("users_penalties","","", []tC{ tC{"uid","int",0,false,false,""}, tC{"element_id","int",0,false,false,""}, @@ -134,7 +141,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, )*/ - qgen.Install.CreateTable("users_groups_scheduler", "", "", + createTable("users_groups_scheduler", "", "", []tC{ tC{"uid", "int", 0, false, false, ""}, tC{"set_group", "int", 0, false, false, ""}, @@ -150,7 +157,7 @@ func createTables(adapter qgen.Adapter) error { ) // TODO: Can we use a piece of software dedicated to persistent queues for this rather than relying on the database for it? - qgen.Install.CreateTable("users_avatar_queue", "", "", + createTable("users_avatar_queue", "", "", []tC{ tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key }, @@ -160,7 +167,7 @@ func createTables(adapter qgen.Adapter) error { ) // TODO: Should we add a users prefix to this table to fit the "unofficial convention"? - qgen.Install.CreateTable("emails", "", "", + createTable("emails", "", "", []tC{ tC{"email", "varchar", 200, false, false, ""}, tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -171,7 +178,7 @@ func createTables(adapter qgen.Adapter) error { // TODO: Allow for patterns in domains, if the bots try to shake things up there? /* - qgen.Install.CreateTable("email_domain_blacklist", "", "", + createTable("email_domain_blacklist", "", "", []tC{ tC{"domain", "varchar", 200, false, false, ""}, tC{"gtld", "boolean", 0, false, false, "0"}, @@ -183,7 +190,7 @@ func createTables(adapter qgen.Adapter) error { */ // TODO: Implement password resets - qgen.Install.CreateTable("password_resets", "", "", + createTable("password_resets", "", "", []tC{ tC{"email", "varchar", 200, false, false, ""}, tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -193,7 +200,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("forums", mysqlPre, mysqlCol, + createTable("forums", mysqlPre, mysqlCol, []tC{ tC{"fid", "int", 0, false, true, ""}, tC{"name", "varchar", 100, false, false, ""}, @@ -213,7 +220,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("forums_permissions", "", "", + createTable("forums_permissions", "", "", []tC{ tC{"fid", "int", 0, false, false, ""}, tC{"gid", "int", 0, false, false, ""}, @@ -226,7 +233,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("topics", mysqlPre, mysqlCol, + createTable("topics", mysqlPre, mysqlCol, []tC{ tC{"tid", "int", 0, false, true, ""}, tC{"title", "varchar", 100, false, false, ""}, // TODO: Increase the max length to 200? @@ -263,7 +270,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("replies", mysqlPre, mysqlCol, + createTable("replies", mysqlPre, mysqlCol, []tC{ tC{"rid", "int", 0, false, true, ""}, // TODO: Rename to replyID? tC{"tid", "int", 0, false, false, ""}, // TODO: Rename to topicID? @@ -287,7 +294,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("attachments", mysqlPre, mysqlCol, + createTable("attachments", mysqlPre, mysqlCol, []tC{ tC{"attachID", "int", 0, false, true, ""}, tC{"sectionID", "int", 0, false, false, "0"}, @@ -303,7 +310,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("revisions", mysqlPre, mysqlCol, + createTable("revisions", mysqlPre, mysqlCol, []tC{ tC{"reviseID", "int", 0, false, true, ""}, tC{"content", "text", 0, false, false, ""}, @@ -317,7 +324,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("polls", mysqlPre, mysqlCol, + createTable("polls", mysqlPre, mysqlCol, []tC{ tC{"pollID", "int", 0, false, true, ""}, tC{"parentID", "int", 0, false, false, "0"}, @@ -331,7 +338,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("polls_options", "", "", + createTable("polls_options", "", "", []tC{ tC{"pollID", "int", 0, false, false, ""}, tC{"option", "int", 0, false, false, "0"}, @@ -339,7 +346,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("polls_votes", mysqlPre, mysqlCol, + createTable("polls_votes", mysqlPre, mysqlCol, []tC{ tC{"pollID", "int", 0, false, false, ""}, tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -349,7 +356,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("users_replies", mysqlPre, mysqlCol, + createTable("users_replies", mysqlPre, mysqlCol, []tC{ tC{"rid", "int", 0, false, true, ""}, tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -366,7 +373,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("likes", "", "", + createTable("likes", "", "", []tC{ tC{"weight", "tinyint", 0, false, false, "1"}, tC{"targetItem", "int", 0, false, false, ""}, @@ -378,7 +385,7 @@ func createTables(adapter qgen.Adapter) error { ) //columns("participants, createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?") - qgen.Install.CreateTable("conversations", "", "", + createTable("conversations", "", "", []tC{ tC{"cid", "int", 0, false, true, ""}, tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -391,7 +398,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("conversations_posts", "", "", + createTable("conversations_posts", "", "", []tC{ tC{"pid", "int", 0, false, true, ""}, tC{"cid", "int", 0, false, false, ""}, @@ -404,14 +411,14 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("conversations_participants", "", "", + createTable("conversations_participants", "", "", []tC{ tC{"uid", "int", 0, false, false, ""}, tC{"cid", "int", 0, false, false, ""}, }, nil, ) - qgen.Install.CreateTable("activity_stream_matches", "", "", + createTable("activity_stream_matches", "", "", []tC{ tC{"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -421,7 +428,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("activity_stream", "", "", + createTable("activity_stream", "", "", []tC{ tC{"asid", "int", 0, false, true, ""}, tC{"actor", "int", 0, false, false, ""}, /* the one doing the act */ // TODO: Make this a foreign key @@ -436,7 +443,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("activity_subscriptions", "", "", + createTable("activity_subscriptions", "", "", []tC{ tC{"user", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"targetID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ @@ -446,7 +453,7 @@ func createTables(adapter qgen.Adapter) error { ) /* Due to MySQL's design, we have to drop the unique keys for table settings, plugins, and themes down from 200 to 180 or it will error */ - qgen.Install.CreateTable("settings", "", "", + createTable("settings", "", "", []tC{ tC{"name", "varchar", 180, false, false, ""}, tC{"content", "varchar", 250, false, false, ""}, @@ -458,7 +465,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("word_filters", "", "", + createTable("word_filters", "", "", []tC{ tC{"wfid", "int", 0, false, true, ""}, tC{"find", "varchar", 200, false, false, ""}, @@ -469,7 +476,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("plugins", "", "", + createTable("plugins", "", "", []tC{ tC{"uname", "varchar", 180, false, false, ""}, tC{"active", "boolean", 0, false, false, "0"}, @@ -480,7 +487,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("themes", "", "", + createTable("themes", "", "", []tC{ tC{"uname", "varchar", 180, false, false, ""}, tC{"default", "boolean", 0, false, false, "0"}, @@ -491,7 +498,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("widgets", "", "", + createTable("widgets", "", "", []tC{ tC{"wid", "int", 0, false, true, ""}, tC{"position", "int", 0, false, false, ""}, @@ -506,7 +513,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("menus", "", "", + createTable("menus", "", "", []tC{ tC{"mid", "int", 0, false, true, ""}, }, @@ -515,7 +522,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("menu_items", "", "", + createTable("menu_items", "", "", []tC{ tC{"miid", "int", 0, false, true, ""}, tC{"mid", "int", 0, false, false, ""}, @@ -539,7 +546,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("pages", mysqlPre, mysqlCol, + createTable("pages", mysqlPre, mysqlCol, []tC{ tC{"pid", "int", 0, false, true, ""}, //tC{"path", "varchar", 200, false, false, ""}, @@ -555,7 +562,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("registration_logs", "", "", + createTable("registration_logs", "", "", []tC{ tC{"rlid", "int", 0, false, true, ""}, tC{"username", "varchar", 100, false, false, ""}, @@ -570,7 +577,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("login_logs", "", "", + createTable("login_logs", "", "", []tC{ tC{"lid", "int", 0, false, true, ""}, tC{"uid", "int", 0, false, false, ""}, @@ -583,7 +590,7 @@ func createTables(adapter qgen.Adapter) error { }, ) - qgen.Install.CreateTable("moderation_logs", "", "", + createTable("moderation_logs", "", "", []tC{ tC{"action", "varchar", 100, false, false, ""}, tC{"elementID", "int", 0, false, false, ""}, @@ -594,7 +601,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("administration_logs", "", "", + createTable("administration_logs", "", "", []tC{ tC{"action", "varchar", 100, false, false, ""}, tC{"elementID", "int", 0, false, false, ""}, @@ -605,7 +612,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("viewchunks", "", "", + createTable("viewchunks", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -613,7 +620,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("viewchunks_agents", "", "", + createTable("viewchunks_agents", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -622,7 +629,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("viewchunks_systems", "", "", + createTable("viewchunks_systems", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -630,7 +637,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("viewchunks_langs", "", "", + createTable("viewchunks_langs", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -638,7 +645,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("viewchunks_referrers", "", "", + createTable("viewchunks_referrers", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -646,7 +653,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("viewchunks_forums", "", "", + createTable("viewchunks_forums", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -654,7 +661,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("topicchunks", "", "", + createTable("topicchunks", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -662,7 +669,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("postchunks", "", "", + createTable("postchunks", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, @@ -670,7 +677,7 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("memchunks", "", "", + createTable("memchunks", "", "", []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"stack", "int", 0, false, false, "0"}, @@ -679,24 +686,24 @@ func createTables(adapter qgen.Adapter) error { }, nil, ) - qgen.Install.CreateTable("sync", "", "", + createTable("sync", "", "", []tC{ tC{"last_update", "datetime", 0, false, false, ""}, }, nil, ) - qgen.Install.CreateTable("updates", "", "", + createTable("updates", "", "", []tC{ tC{"dbVersion", "int", 0, false, false, "0"}, }, nil, ) - qgen.Install.CreateTable("meta", "", "", + createTable("meta", "", "", []tC{ tC{"name", "varchar", 200, false, false, ""}, tC{"value", "varchar", 200, false, false, ""}, }, nil, ) - return nil + return err } diff --git a/common/promotions.go b/common/promotions.go index e91eb263..38d29b83 100644 --- a/common/promotions.go +++ b/common/promotions.go @@ -6,7 +6,7 @@ import ( qgen "github.com/Azareal/Gosora/query_gen" ) -var GroupPromotions *DefaultGroupPromotionStore +var GroupPromotions GroupPromotionStore type GroupPromotion struct { ID int @@ -15,15 +15,16 @@ type GroupPromotion struct { TwoWay bool Level int + Posts int MinTime int } type GroupPromotionStore interface { - GetByGroup() ([]*GroupPromotion, error) + GetByGroup(gid int) (gps []*GroupPromotion, err error) Get(id int) (*GroupPromotion, error) - PromoteIfEligible(u *User, level int) error + PromoteIfEligible(u *User, level int, posts int) error Delete(id int) error - Create(from int, to int, twoWay bool, level int) (int, error) + Create(from int, to int, twoWay bool, level int, posts int) (int, error) } type DefaultGroupPromotionStore struct { @@ -39,13 +40,13 @@ type DefaultGroupPromotionStore struct { 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(), + getByGroup: acc.Select(ugp).Columns("pid, from_gid, to_gid, two_way, level, posts, minTime").Where("from_gid=? OR to_gid=?").Prepare(), + get: acc.Select(ugp).Columns("from_gid, to_gid, two_way, level, posts, 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(), + create: acc.Insert(ugp).Columns("from_gid, to_gid, two_way, level, posts, 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(), + getByUser: acc.Select(ugp).Columns("pid, to_gid, two_way, level, posts, minTime").Where("from_gid=? AND level>=? AND posts>=?").Orderby("level DESC").Limit("1").Prepare(), + updateUser: acc.Update("users").Set("group = ?").Where("level >= ? AND posts >= ?").Prepare(), }, acc.FirstError() } @@ -58,7 +59,7 @@ func (s *DefaultGroupPromotionStore) GetByGroup(gid int) (gps []*GroupPromotion, for rows.Next() { g := &GroupPromotion{} - err := rows.Scan(&g.ID, &g.From, &g.To, &g.TwoWay, &g.Level, &g.MinTime) + err := rows.Scan(&g.ID, &g.From, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime) if err != nil { return nil, err } @@ -75,22 +76,22 @@ func (s *DefaultGroupPromotionStore) Get(id int) (*GroupPromotion, error) { }*/ g := &GroupPromotion{ID: id} - err := s.get.QueryRow(id).Scan(&g.From, &g.To, &g.TwoWay, &g.Level, &g.MinTime) + err := s.get.QueryRow(id).Scan(&g.From, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime) if err == nil { //s.cache.Set(u) } return g, err } -func (s *DefaultGroupPromotionStore) PromoteIfEligible(u *User, level int) error { +func (s *DefaultGroupPromotionStore) PromoteIfEligible(u *User, level int, posts 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) + err := s.getByUser.QueryRow(u.Group, level, posts).Scan(&g.ID, &g.To, &g.TwoWay, &g.Level, &g.Posts, &g.MinTime) if err == sql.ErrNoRows { return nil } else if err != nil { return err } - _, err = s.updateUser.Exec(g.To, g.Level) + _, err = s.updateUser.Exec(g.To, g.Level, g.Posts) return err } @@ -99,8 +100,8 @@ func (s *DefaultGroupPromotionStore) Delete(id int) error { 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) +func (s *DefaultGroupPromotionStore) Create(from int, to int, twoWay bool, level int, posts int) (int, error) { + res, err := s.create.Exec(from, to, twoWay, level, posts, 0) if err != nil { return 0, err } diff --git a/common/reply.go b/common/reply.go index fe2f0ca1..aa3c5e2f 100644 --- a/common/reply.go +++ b/common/reply.go @@ -113,7 +113,7 @@ func (r *Reply) Like(uid int) (err error) { if err != nil { return err } - _, err = userStmts.incrementLiked.Exec(1, uid) + _, err = userStmts.incLiked.Exec(1, uid) _ = Rstore.GetCache().Remove(r.ID) return err } diff --git a/common/template_init.go b/common/template_init.go index 0cef94a0..888c67b0 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -91,14 +91,14 @@ var Template_account_handle = genIntTmpl("account") func tmplInitUsers() (User, User, User) { avatar, microAvatar := BuildAvatar(62, "") - user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", avatar, microAvatar, "", "", "", "", 0, 0, 0, "0.0.0.0.0", "", 0} + user := User{62, BuildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", avatar, microAvatar, "", "", "", "", 0, 0, 0, 0,"0.0.0.0.0", "", 0} // TODO: Do a more accurate level calculation for this? avatar, microAvatar = BuildAvatar(1, "") - user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 58, 1000, 0, "127.0.0.1", "", 0} + user2 := User{1, BuildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 58, 1000, 0, 1000, "127.0.0.1", "", 0} avatar, microAvatar = BuildAvatar(2, "") - user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 42, 900, 0, "::1", "", 0} + user3 := User{2, BuildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", avatar, microAvatar, "", "", "", "", 42, 900, 0, 900, "::1", "", 0} return user, user2, user3 } diff --git a/common/topic.go b/common/topic.go index c8c84b1c..d4234eaa 100644 --- a/common/topic.go +++ b/common/topic.go @@ -318,7 +318,7 @@ func (t *Topic) Like(score int, uid int) (err error) { if err != nil { return err } - _, err = userStmts.incrementLiked.Exec(1, uid) + _, err = userStmts.incLiked.Exec(1, uid) t.cacheRemove() return err } diff --git a/common/user.go b/common/user.go index d5262e57..443111b8 100644 --- a/common/user.go +++ b/common/user.go @@ -13,7 +13,7 @@ import ( "strings" "time" - "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" "github.com/go-sql-driver/mysql" ) @@ -51,6 +51,7 @@ type User struct { Tag string Level int Score int + Posts int Liked int LastIP string // ! This part of the UserCache data might fall out of date LastAgent string // ! Temporary hack, don't use @@ -104,7 +105,7 @@ type MeUser struct { //Perms Perms //PluginPerms map[string]bool - S string // Session + S string // Session Avatar string MicroAvatar string Tag string @@ -114,24 +115,24 @@ type MeUser struct { } type UserStmts struct { - activate *sql.Stmt - changeGroup *sql.Stmt - delete *sql.Stmt - setAvatar *sql.Stmt - setUsername *sql.Stmt - incrementTopics *sql.Stmt - updateLevel *sql.Stmt - update *sql.Stmt + activate *sql.Stmt + changeGroup *sql.Stmt + delete *sql.Stmt + setAvatar *sql.Stmt + setUsername *sql.Stmt + incTopics *sql.Stmt + updateLevel *sql.Stmt + update *sql.Stmt // TODO: Split these into a sub-struct - incrementScore *sql.Stmt - incrementPosts *sql.Stmt - incrementBigposts *sql.Stmt - incrementMegaposts *sql.Stmt - incrementLiked *sql.Stmt + incScore *sql.Stmt + incPosts *sql.Stmt + incBigposts *sql.Stmt + incMegaposts *sql.Stmt + incLiked *sql.Stmt - decrementLiked *sql.Stmt - updateLastIP *sql.Stmt + decLiked *sql.Stmt + updateLastIP *sql.Stmt setPassword *sql.Stmt @@ -142,23 +143,23 @@ var userStmts UserStmts func init() { DbInits.Add(func(acc *qgen.Accumulator) error { - var w = "uid = ?" + w := "uid = ?" userStmts = UserStmts{ - activate: acc.SimpleUpdate("users", "active = 1", w), - changeGroup: acc.SimpleUpdate("users", "group = ?", w), // TODO: Implement user_count for users_groups here - delete: acc.SimpleDelete("users", w), - setAvatar: acc.Update("users").Set("avatar = ?").Where(w).Prepare(), - setUsername: acc.Update("users").Set("name = ?").Where(w).Prepare(), - incrementTopics: acc.SimpleUpdate("users", "topics = topics + ?", w), - updateLevel: acc.SimpleUpdate("users", "level = ?", w), - update: acc.Update("users").Set("name = ?, email = ?, group = ?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this + activate: acc.SimpleUpdate("users", "active = 1", w), + changeGroup: acc.SimpleUpdate("users", "group = ?", w), // TODO: Implement user_count for users_groups here + delete: acc.SimpleDelete("users", w), + setAvatar: acc.Update("users").Set("avatar = ?").Where(w).Prepare(), + setUsername: acc.Update("users").Set("name = ?").Where(w).Prepare(), + incTopics: acc.SimpleUpdate("users", "topics = topics + ?", w), + updateLevel: acc.SimpleUpdate("users", "level = ?", w), + update: acc.Update("users").Set("name = ?, email = ?, group = ?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this - incrementScore: acc.SimpleUpdate("users", "score = score + ?", w), - incrementPosts: acc.SimpleUpdate("users", "posts = posts + ?", w), - incrementBigposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?", w), - incrementMegaposts: acc.SimpleUpdate("users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", w), - incrementLiked: acc.SimpleUpdate("users", "liked = liked + ?, lastLiked = UTC_TIMESTAMP()", w), - decrementLiked: acc.SimpleUpdate("users", "liked = liked - ?", w), + incScore: acc.Update("users").Set("score = score + ?").Where(w).Prepare(), + incPosts: acc.Update("users").Set("posts = posts + ?").Where(w).Prepare(), + incBigposts: acc.Update("users").Set("posts = posts + ?, bigposts = bigposts + ?").Where(w).Prepare(), + incMegaposts: acc.Update("users").Set("posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?").Where(w).Prepare(), + incLiked: acc.Update("users").Set("liked = liked + ?, lastLiked = UTC_TIMESTAMP()").Where(w).Prepare(), + decLiked: acc.Update("users").Set("liked = liked - ?").Where(w).Prepare(), //recalcLastLiked: acc... updateLastIP: acc.SimpleUpdate("users", "last_ip = ?", w), @@ -342,15 +343,15 @@ func (u *User) UpdateIP(host string) error { return err } -func (u *User) Update(newname string, newemail string, newgroup int) (err error) { - return u.bindStmt(userStmts.update, newname, newemail, newgroup) +func (u *User) Update(name string, email string, group int) (err error) { + return u.bindStmt(userStmts.update, name, email, group) } func (u *User) IncreasePostStats(wcount int, topic bool) (err error) { var mod int baseScore := 1 if topic { - _, err = userStmts.incrementTopics.Exec(1, u.ID) + _, err = userStmts.incTopics.Exec(1, u.ID) if err != nil { return err } @@ -359,31 +360,31 @@ func (u *User) IncreasePostStats(wcount int, topic bool) (err error) { settings := SettingBox.Load().(SettingMap) if wcount >= settings["megapost_min_words"].(int) { - _, err = userStmts.incrementMegaposts.Exec(1, 1, 1, u.ID) + _, err = userStmts.incMegaposts.Exec(1, 1, 1, u.ID) mod = 4 } else if wcount >= settings["bigpost_min_words"].(int) { - _, err = userStmts.incrementBigposts.Exec(1, 1, u.ID) + _, err = userStmts.incBigposts.Exec(1, 1, u.ID) mod = 1 } else { - _, err = userStmts.incrementPosts.Exec(1, u.ID) + _, err = userStmts.incPosts.Exec(1, u.ID) } if err != nil { return err } - _, err = userStmts.incrementScore.Exec(baseScore+mod, u.ID) + _, err = userStmts.incScore.Exec(baseScore+mod, u.ID) if err != nil { return err } //log.Print(u.Score + baseScore + mod) // TODO: Use a transaction to prevent level desyncs? - level := GetLevel(u.Score+baseScore+mod) + 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) + err = GroupPromotions.PromoteIfEligible(u, level, u.Posts+1) u.CacheRemove() return err } @@ -392,7 +393,7 @@ func (u *User) DecreasePostStats(wcount int, topic bool) (err error) { var mod int baseScore := -1 if topic { - _, err = userStmts.incrementTopics.Exec(-1, u.ID) + _, err = userStmts.incTopics.Exec(-1, u.ID) if err != nil { return err } @@ -401,19 +402,19 @@ func (u *User) DecreasePostStats(wcount int, topic bool) (err error) { settings := SettingBox.Load().(SettingMap) if wcount >= settings["megapost_min_words"].(int) { - _, err = userStmts.incrementMegaposts.Exec(-1, -1, -1, u.ID) + _, err = userStmts.incMegaposts.Exec(-1, -1, -1, u.ID) mod = 4 } else if wcount >= settings["bigpost_min_words"].(int) { - _, err = userStmts.incrementBigposts.Exec(-1, -1, u.ID) + _, err = userStmts.incBigposts.Exec(-1, -1, u.ID) mod = 1 } else { - _, err = userStmts.incrementPosts.Exec(-1, u.ID) + _, err = userStmts.incPosts.Exec(-1, u.ID) } if err != nil { return err } - _, err = userStmts.incrementScore.Exec(baseScore-mod, u.ID) + _, err = userStmts.incScore.Exec(baseScore-mod, u.ID) if err != nil { return err } @@ -479,7 +480,7 @@ func buildNoavatar(uid int, width int) string { } } if !Config.DisableDefaultNoavatar && uid < 5 { - return "/s/n"+strconv.Itoa(uid)+"-"+strconv.Itoa(width)+".png?i=0" + return "/s/n" + strconv.Itoa(uid) + "-" + strconv.Itoa(width) + ".png?i=0" } return strings.Replace(strings.Replace(Config.Noavatar, "{id}", strconv.Itoa(uid), 1), "{width}", strconv.Itoa(width), 1) } diff --git a/common/user_store.go b/common/user_store.go index b0b9b714..89ccfe60 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -53,9 +53,9 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) { // TODO: Add an admin version of registerStmt with more flexibility? return &DefaultUserStore{ cache: cache, - get: acc.SimpleSelect("users", "name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group", "uid = ?", "", ""), - getByName: acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group").Where("name = ?").Prepare(), - getOffset: acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, liked, last_ip, temp_group").Orderby("uid ASC").Limit("?,?").Prepare(), + get: acc.SimpleSelect("users", "name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, posts, liked, last_ip, temp_group", "uid = ?", "", ""), + getByName: acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, posts, liked, last_ip, temp_group").Where("name = ?").Prepare(), + getOffset: acc.Select("users").Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, posts, liked, last_ip, temp_group").Orderby("uid ASC").Limit("?,?").Prepare(), exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""), register: acc.Insert("users").Columns("name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""), @@ -86,7 +86,7 @@ func (s *DefaultUserStore) Get(id int) (*User, error) { //log.Print("uncached user") u = &User{ID: id, Loggedin: true} - err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score,&u.Liked, &u.LastIP, &u.TempGroup) + err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts,&u.Liked, &u.LastIP, &u.TempGroup) if err == nil { u.Init() s.cache.Set(u) @@ -98,7 +98,7 @@ func (s *DefaultUserStore) Get(id int) (*User, error) { // ! This bypasses the cache, use frugally func (s *DefaultUserStore) GetByName(name string) (*User, error) { u := &User{Loggedin: true} - err := s.getByName.QueryRow(name).Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup) + err := s.getByName.QueryRow(name).Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts,&u.Liked, &u.LastIP, &u.TempGroup) if err == nil { u.Init() s.cache.Set(u) @@ -117,7 +117,7 @@ func (s *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User, er for rows.Next() { u := &User{Loggedin: true} - err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup) + err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup) if err != nil { return nil, err } @@ -171,7 +171,7 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) } q = q[0 : len(q)-1] - rows, err := qgen.NewAcc().Select("users").Columns("uid,name,group,active,is_super_admin,session,email,avatar,message,url_prefix,url_name,level,score,liked,last_ip,temp_group").Where("uid IN(" + q + ")").Query(idList...) + rows, err := qgen.NewAcc().Select("users").Columns("uid,name,group,active,is_super_admin,session,email,avatar,message,url_prefix,url_name,level,score,posts,liked,last_ip,temp_group").Where("uid IN(" + q + ")").Query(idList...) if err != nil { return list, err } @@ -179,7 +179,7 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) for rows.Next() { u := &User{Loggedin: true} - err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup) + err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup) if err != nil { return list, err } @@ -212,19 +212,19 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) func (s *DefaultUserStore) BypassGet(id int) (*User, error) { u := &User{ID: id, Loggedin: true} - err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup) - u.Init() + err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup) + if err == nil { + u.Init() + } return u, err } func (s *DefaultUserStore) Reload(id int) error { - u := &User{ID: id, Loggedin: true} - err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.URLPrefix, &u.URLName, &u.Level, &u.Score, &u.Liked, &u.LastIP, &u.TempGroup) + u, err := s.BypassGet(id) if err != nil { s.cache.Remove(id) return err } - u.Init() _ = s.cache.Set(u) TopicListThaw.Thaw() return nil diff --git a/general_test.go b/general_test.go index 6d767185..62659ac2 100644 --- a/general_test.go +++ b/general_test.go @@ -106,8 +106,7 @@ func gloinit() (err error) { } func init() { - err := gloinit() - if err != nil { + if err := gloinit(); err != nil { log.Print("Something bad happened") //debug.PrintStack() log.Fatalf("%+v\n", err) diff --git a/langs/english.json b/langs/english.json index 65484bd3..539da022 100644 --- a/langs/english.json +++ b/langs/english.json @@ -889,6 +889,7 @@ "panel_group_promotions_to":"To", "panel_group_promotions_two_way":"Two Way", "panel_group_promotions_level":"Level", + "panel_group_promotions_posts":"Posts", "panel_group_promotions_create_button":"Add Promotion", "panel_word_filters_head":"Word Filters", diff --git a/patcher/patches.go b/patcher/patches.go index e6b14758..9c0366d0 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -38,6 +38,7 @@ func init() { addPatch(22, patch22) addPatch(23, patch23) addPatch(24, patch24) + addPatch(25, patch25) } func patch0(scanner *bufio.Scanner) (err error) { @@ -734,4 +735,8 @@ func patch24(scanner *bufio.Scanner) error { tblKey{"pid", "primary","",false}, }, )) +} + +func patch25(scanner *bufio.Scanner) error { + return execStmt(qgen.Builder.AddColumn("users_groups_promotions", tC{"posts", "int", 0, false, false, "0"}, nil)) } \ No newline at end of file diff --git a/query_gen/install.go b/query_gen/install.go index 3e8a743d..1bce70ef 100644 --- a/query_gen/install.go +++ b/query_gen/install.go @@ -30,81 +30,81 @@ type installer struct { plugins []QueryPlugin } -func (install *installer) SetAdapter(name string) error { +func (ins *installer) SetAdapter(name string) error { adap, err := GetAdapter(name) if err != nil { return err } - install.SetAdapterInstance(adap) + ins.SetAdapterInstance(adap) return nil } -func (install *installer) SetAdapterInstance(adapter Adapter) { - install.adapter = adapter - install.instructions = []DBInstallInstruction{} +func (ins *installer) SetAdapterInstance(adapter Adapter) { + ins.adapter = adapter + ins.instructions = []DBInstallInstruction{} } -func (install *installer) AddPlugins(plugins ...QueryPlugin) { - install.plugins = append(install.plugins, plugins...) +func (ins *installer) AddPlugins(plugins ...QueryPlugin) { + ins.plugins = append(ins.plugins, plugins...) } -func (install *installer) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) error { +func (ins *installer) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) error { tableStruct := &DBInstallTable{table, charset, collation, columns, keys} - err := install.RunHook("CreateTableStart", tableStruct) + err := ins.RunHook("CreateTableStart", tableStruct) if err != nil { return err } - res, err := install.adapter.CreateTable("", table, charset, collation, columns, keys) + res, err := ins.adapter.CreateTable("", table, charset, collation, columns, keys) if err != nil { return err } - err = install.RunHook("CreateTableAfter", tableStruct) + err = ins.RunHook("CreateTableAfter", tableStruct) if err != nil { return err } - install.instructions = append(install.instructions, DBInstallInstruction{table, res, "create-table"}) - install.tables = append(install.tables, tableStruct) + ins.instructions = append(ins.instructions, DBInstallInstruction{table, res, "create-table"}) + ins.tables = append(ins.tables, tableStruct) return nil } // TODO: Let plugins manipulate the parameters like in CreateTable -func (install *installer) AddIndex(table string, iname string, colname string) error { - err := install.RunHook("AddIndexStart", table, iname, colname) +func (ins *installer) AddIndex(table string, iname string, colname string) error { + err := ins.RunHook("AddIndexStart", table, iname, colname) if err != nil { return err } - res, err := install.adapter.AddIndex("", table, iname, colname) + res, err := ins.adapter.AddIndex("", table, iname, colname) if err != nil { return err } - err = install.RunHook("AddIndexAfter", table, iname, colname) + err = ins.RunHook("AddIndexAfter", table, iname, colname) if err != nil { return err } - install.instructions = append(install.instructions, DBInstallInstruction{table, res, "index"}) + ins.instructions = append(ins.instructions, DBInstallInstruction{table, res, "index"}) return nil } // TODO: Let plugins manipulate the parameters like in CreateTable -func (install *installer) SimpleInsert(table string, columns string, fields string) error { - err := install.RunHook("SimpleInsertStart", table, columns, fields) +func (ins *installer) SimpleInsert(table string, columns string, fields string) error { + err := ins.RunHook("SimpleInsertStart", table, columns, fields) if err != nil { return err } - res, err := install.adapter.SimpleInsert("", table, columns, fields) + res, err := ins.adapter.SimpleInsert("", table, columns, fields) if err != nil { return err } - err = install.RunHook("SimpleInsertAfter", table, columns, fields, res) + err = ins.RunHook("SimpleInsertAfter", table, columns, fields, res) if err != nil { return err } - install.instructions = append(install.instructions, DBInstallInstruction{table, res, "insert"}) + ins.instructions = append(ins.instructions, DBInstallInstruction{table, res, "insert"}) return nil } -func (install *installer) RunHook(name string, args ...interface{}) error { - for _, plugin := range install.plugins { +func (ins *installer) RunHook(name string, args ...interface{}) error { + for _, plugin := range ins.plugins { err := plugin.Hook(name, args...) if err != nil { return err @@ -113,12 +113,12 @@ func (install *installer) RunHook(name string, args ...interface{}) error { return nil } -func (install *installer) Write() error { +func (ins *installer) Write() error { var inserts string // We can't escape backticks, so we have to dump it out a file at a time - for _, instr := range install.instructions { + for _, instr := range ins.instructions { if instr.Type == "create-table" { - err := writeFile("./schema/"+install.adapter.GetName()+"/query_"+instr.Table+".sql", instr.Contents) + err := writeFile("./schema/"+ins.adapter.GetName()+"/query_"+instr.Table+".sql", instr.Contents) if err != nil { return err } @@ -127,12 +127,12 @@ func (install *installer) Write() error { } } - err := writeFile("./schema/"+install.adapter.GetName()+"/inserts.sql", inserts) + err := writeFile("./schema/"+ins.adapter.GetName()+"/inserts.sql", inserts) if err != nil { return err } - for _, plugin := range install.plugins { + for _, plugin := range ins.plugins { err := plugin.Write() if err != nil { return err diff --git a/routes/panel/forums.go b/routes/panel/forums.go index db8a4c75..aa0cf9fe 100644 --- a/routes/panel/forums.go +++ b/routes/panel/forums.go @@ -137,7 +137,7 @@ func ForumsOrderSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro if ferr != nil { return ferr } - js := (r.PostFormValue("js") == "1") + js := r.PostFormValue("js") == "1" if !user.Perms.ManageForums { return c.NoPermissionsJSQ(w, r, user, js) } @@ -218,7 +218,7 @@ func ForumsEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, sfid if !user.Perms.ManageForums { return c.NoPermissions(w, r, user) } - js := (r.PostFormValue("js") == "1") + js := r.PostFormValue("js") == "1" fid, err := strconv.Atoi(sfid) if err != nil { @@ -260,7 +260,7 @@ func ForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, user c.User, if !user.Perms.ManageForums { return c.NoPermissions(w, r, user) } - js := (r.PostFormValue("js") == "1") + js := r.PostFormValue("js") == "1" fid, err := strconv.Atoi(sfid) if err != nil { diff --git a/routes/panel/groups.go b/routes/panel/groups.go index 6172fb8c..75898536 100644 --- a/routes/panel/groups.go +++ b/routes/panel/groups.go @@ -208,6 +208,10 @@ func GroupsPromotionsCreateSubmit(w http.ResponseWriter, r *http.Request, user c if err != nil { return c.LocalError("level must be integer", w, r, user) } + posts, err := strconv.Atoi(r.FormValue("posts")) + if err != nil { + return c.LocalError("posts must be integer", w, r, user) + } g, err := c.Groups.Get(from) ferr := groupCheck(w, r, user, g, err) @@ -219,7 +223,7 @@ func GroupsPromotionsCreateSubmit(w http.ResponseWriter, r *http.Request, user c if err != nil { return ferr } - _, err = c.GroupPromotions.Create(from, to, twoWay, level) + _, err = c.GroupPromotions.Create(from, to, twoWay, level, posts) if err != nil { return c.InternalError(err, w, r) } @@ -300,50 +304,50 @@ func GroupsEditPerms(w http.ResponseWriter, r *http.Request, user c.User, sgid s // TODO: Load the phrases in bulk for efficiency? var localPerms []c.NameLangToggle - addLocalPerm := func(permStr string, perm bool) { + addPerm := func(permStr string, perm bool) { localPerms = append(localPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm}) } - addLocalPerm("ViewTopic", g.Perms.ViewTopic) - addLocalPerm("LikeItem", g.Perms.LikeItem) - addLocalPerm("CreateTopic", g.Perms.CreateTopic) + addPerm("ViewTopic", g.Perms.ViewTopic) + addPerm("LikeItem", g.Perms.LikeItem) + addPerm("CreateTopic", g.Perms.CreateTopic) //<-- - addLocalPerm("EditTopic", g.Perms.EditTopic) - addLocalPerm("DeleteTopic", g.Perms.DeleteTopic) - addLocalPerm("CreateReply", g.Perms.CreateReply) - addLocalPerm("EditReply", g.Perms.EditReply) - addLocalPerm("DeleteReply", g.Perms.DeleteReply) - addLocalPerm("PinTopic", g.Perms.PinTopic) - addLocalPerm("CloseTopic", g.Perms.CloseTopic) - addLocalPerm("MoveTopic", g.Perms.MoveTopic) + addPerm("EditTopic", g.Perms.EditTopic) + addPerm("DeleteTopic", g.Perms.DeleteTopic) + addPerm("CreateReply", g.Perms.CreateReply) + addPerm("EditReply", g.Perms.EditReply) + addPerm("DeleteReply", g.Perms.DeleteReply) + addPerm("PinTopic", g.Perms.PinTopic) + addPerm("CloseTopic", g.Perms.CloseTopic) + addPerm("MoveTopic", g.Perms.MoveTopic) var globalPerms []c.NameLangToggle - addGlobalPerm := func(permStr string, perm bool) { + addPerm = func(permStr string, perm bool) { globalPerms = append(globalPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm}) } - addGlobalPerm("BanUsers", g.Perms.BanUsers) - addGlobalPerm("ActivateUsers", g.Perms.ActivateUsers) - addGlobalPerm("EditUser", g.Perms.EditUser) - addGlobalPerm("EditUserEmail", g.Perms.EditUserEmail) - addGlobalPerm("EditUserPassword", g.Perms.EditUserPassword) - addGlobalPerm("EditUserGroup", g.Perms.EditUserGroup) - addGlobalPerm("EditUserGroupSuperMod", g.Perms.EditUserGroupSuperMod) - addGlobalPerm("EditUserGroupAdmin", g.Perms.EditUserGroupAdmin) - addGlobalPerm("EditGroup", g.Perms.EditGroup) - addGlobalPerm("EditGroupLocalPerms", g.Perms.EditGroupLocalPerms) - addGlobalPerm("EditGroupGlobalPerms", g.Perms.EditGroupGlobalPerms) - addGlobalPerm("EditGroupSuperMod", g.Perms.EditGroupSuperMod) - addGlobalPerm("EditGroupAdmin", g.Perms.EditGroupAdmin) - addGlobalPerm("ManageForums", g.Perms.ManageForums) - addGlobalPerm("EditSettings", g.Perms.EditSettings) - addGlobalPerm("ManageThemes", g.Perms.ManageThemes) - addGlobalPerm("ManagePlugins", g.Perms.ManagePlugins) - addGlobalPerm("ViewAdminLogs", g.Perms.ViewAdminLogs) - addGlobalPerm("ViewIPs", g.Perms.ViewIPs) - addGlobalPerm("UploadFiles", g.Perms.UploadFiles) - addGlobalPerm("UploadAvatars", g.Perms.UploadAvatars) - addGlobalPerm("UseConvos", g.Perms.UseConvos) + addPerm("BanUsers", g.Perms.BanUsers) + addPerm("ActivateUsers", g.Perms.ActivateUsers) + addPerm("EditUser", g.Perms.EditUser) + addPerm("EditUserEmail", g.Perms.EditUserEmail) + addPerm("EditUserPassword", g.Perms.EditUserPassword) + addPerm("EditUserGroup", g.Perms.EditUserGroup) + addPerm("EditUserGroupSuperMod", g.Perms.EditUserGroupSuperMod) + addPerm("EditUserGroupAdmin", g.Perms.EditUserGroupAdmin) + addPerm("EditGroup", g.Perms.EditGroup) + addPerm("EditGroupLocalPerms", g.Perms.EditGroupLocalPerms) + addPerm("EditGroupGlobalPerms", g.Perms.EditGroupGlobalPerms) + addPerm("EditGroupSuperMod", g.Perms.EditGroupSuperMod) + addPerm("EditGroupAdmin", g.Perms.EditGroupAdmin) + addPerm("ManageForums", g.Perms.ManageForums) + addPerm("EditSettings", g.Perms.EditSettings) + addPerm("ManageThemes", g.Perms.ManageThemes) + addPerm("ManagePlugins", g.Perms.ManagePlugins) + addPerm("ViewAdminLogs", g.Perms.ViewAdminLogs) + addPerm("ViewIPs", g.Perms.ViewIPs) + addPerm("UploadFiles", g.Perms.UploadFiles) + addPerm("UploadAvatars", g.Perms.UploadAvatars) + addPerm("UseConvos", g.Perms.UseConvos) pi := c.PanelEditGroupPermsPage{basePage, g.ID, g.Name, localPerms, globalPerms} return renderTemplate("panel_group_edit_perms", w, r, basePage.Header, pi) diff --git a/schema/mssql/query_users_groups_promotions.sql b/schema/mssql/query_users_groups_promotions.sql index 7cfc8f44..3e722449 100644 --- a/schema/mssql/query_users_groups_promotions.sql +++ b/schema/mssql/query_users_groups_promotions.sql @@ -4,6 +4,7 @@ CREATE TABLE [users_groups_promotions] ( [to_gid] int not null, [two_way] bit DEFAULT 0 not null, [level] int not null, + [posts] int DEFAULT 0 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 index cf534c5a..8c1dec3e 100644 --- a/schema/mysql/query_users_groups_promotions.sql +++ b/schema/mysql/query_users_groups_promotions.sql @@ -4,6 +4,7 @@ CREATE TABLE `users_groups_promotions` ( `to_gid` int not null, `two_way` boolean DEFAULT 0 not null, `level` int not null, + `posts` int DEFAULT 0 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 index 5f1fc701..5bec0a3e 100644 --- a/schema/pgsql/query_users_groups_promotions.sql +++ b/schema/pgsql/query_users_groups_promotions.sql @@ -4,6 +4,7 @@ CREATE TABLE "users_groups_promotions" ( `to_gid` int not null, `two_way` boolean DEFAULT 0 not null, `level` int not null, + `posts` int DEFAULT 0 not null, `minTime` int not null, primary key(`pid`) ); \ No newline at end of file diff --git a/templates/panel_group_edit_promotions.html b/templates/panel_group_edit_promotions.html index 92c475a1..8c25103a 100644 --- a/templates/panel_group_edit_promotions.html +++ b/templates/panel_group_edit_promotions.html @@ -57,6 +57,10 @@
{{lang "panel_group_promotions_level"}}
+
+ +
+