From b1af963916374cfd29e803b67ab12601638de3cf Mon Sep 17 00:00:00 2001 From: Azareal Date: Wed, 15 Jul 2020 07:50:29 +1000 Subject: [PATCH] Add per-user profile comment visibility settings. Honor user blocks in ProfileReplyEditSubmit. Reduce boilerplate. Update account_privacy_profile_comments phrase. Add account_privacy_profile_comments_public phrase. Add account_privacy_profile_comments_registered phrase. Add account_privacy_profile_comments_self phrase. Add account_privacy_enable_embeds phrase. Add profile_comments column to users table. Add who_can_convo column to users table. You will need to run the updater / patcher for this commit. --- cmd/query_gen/tables.go | 414 ++++++++++-------- common/auth.go | 6 +- common/pages.go | 7 +- common/template_init.go | 10 +- common/user.go | 47 +- common/user_store.go | 43 +- langs/english.json | 6 +- patcher/patches.go | 348 ++++++++------- query_gen/querygen.go | 2 +- routes/account.go | 27 +- routes/convos.go | 40 +- routes/panel/pages.go | 26 +- routes/panel/plugins.go | 116 ++--- routes/panel/themes.go | 154 +++---- routes/profile.go | 42 +- routes/profile_reply.go | 51 ++- routes/user.go | 94 ++-- schema/mysql/query_login_logs.sql | 2 +- schema/mysql/query_registration_logs.sql | 2 +- schema/mysql/query_users.sql | 2 + schema/pgsql/query_forums_permissions.sql | 2 +- schema/pgsql/query_login_logs.sql | 2 +- schema/pgsql/query_registration_logs.sql | 2 +- templates/account_own_edit_privacy.html | 15 +- templates/forum_gallery.html | 6 +- templates/panel_analytics_active_memory.html | 18 +- templates/panel_analytics_memory.html | 8 +- templates/panel_analytics_performance.html | 18 +- templates/panel_analytics_posts.html | 8 +- templates/panel_analytics_referrer_views.html | 4 +- templates/panel_analytics_referrers.html | 14 +- templates/panel_analytics_route_views.html | 6 +- templates/panel_analytics_routes.html | 8 +- templates/panel_analytics_routes_perf.html | 8 +- templates/panel_analytics_script.html | 2 +- templates/panel_analytics_script_memory.html | 4 +- templates/panel_analytics_script_perf.html | 4 +- templates/panel_analytics_system_views.html | 4 +- templates/panel_analytics_systems.html | 8 +- .../panel_analytics_time_range_month.html | 16 +- templates/panel_analytics_topics.html | 8 +- templates/panel_analytics_views.html | 8 +- templates/panel_backups.html | 4 +- templates/panel_forum_edit.html | 20 +- templates/panel_forum_edit_perms.html | 4 +- templates/panel_modlogs.html | 2 +- templates/panel_pages_edit.html | 10 +- templates/panel_plugins.html | 12 +- templates/panel_themes_menus_items.html | 16 +- templates/profile.html | 6 +- 50 files changed, 919 insertions(+), 767 deletions(-) diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go index 198ff370..5d512e3b 100644 --- a/cmd/query_gen/tables.go +++ b/cmd/query_gen/tables.go @@ -5,12 +5,16 @@ import qgen "github.com/Azareal/Gosora/query_gen" var mysqlPre = "utf8mb4" var mysqlCol = "utf8mb4_general_ci" +var tables []string + type tblColumn = qgen.DBTableColumn type tC = tblColumn type tblKey = qgen.DBTableKey func createTables(a qgen.Adapter) error { + tables = nil f := func(table, charset, collation string, cols []tC, keys []tblKey) error { + tables = append(tables, table) return qgen.Install.CreateTable(table, charset, collation, cols, keys) } return createTables2(a, f) @@ -23,30 +27,65 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu } err = f(table, charset, collation, cols, keys) } + bcol := func(col string, val bool) qgen.DBTableColumn { + if val { + return tC{col, "boolean", 0, false, false, "1"} + } + return tC{col, "boolean", 0, false, false, "0"} + } + ccol := func(col string, size int, sdefault string) qgen.DBTableColumn { + return tC{col, "varchar", size, false, false, sdefault} + } + text := func(params ...string) qgen.DBTableColumn { + if len(params) == 0 { + return tC{"", "text", 0, false, false, ""} + } + col, sdefault := params[0], "" + if len(params) > 1 { + sdefault = params[1] + if sdefault == "" { + sdefault = "''" + } + } + return tC{col, "text", 0, false, false, sdefault} + } + createdAt := func(coll ...string) qgen.DBTableColumn { + var col string + if len(coll) > 0 { + col = coll[0] + } + if col == "" { + col = "createdAt" + } + return tC{col, "createdAt", 0, false, false, ""} + } + createTable("users", mysqlPre, mysqlCol, []tC{ tC{"uid", "int", 0, false, true, ""}, - tC{"name", "varchar", 100, false, false, ""}, - tC{"password", "varchar", 100, false, false, ""}, + ccol("name", 100, ""), + ccol("password", 100, ""), - tC{"salt", "varchar", 80, false, false, "''"}, + ccol("salt", 80, "''"), tC{"group", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"active", "boolean", 0, false, false, "0"}, - tC{"is_super_admin", "boolean", 0, false, false, "0"}, - tC{"createdAt", "createdAt", 0, false, false, ""}, + bcol("active", false), + bcol("is_super_admin", false), + createdAt(), tC{"lastActiveAt", "datetime", 0, false, false, ""}, - tC{"session", "varchar", 200, false, false, "''"}, - //tC{"authToken", "varchar", 200, false, false, "''"}, - tC{"last_ip", "varchar", 200, false, false, "''"}, + ccol("session", 200, "''"), + //ccol("authToken", 200, "''"), + ccol("last_ip", 200, "''"), + tC{"profile_comments", "int", 0, false, false, "0"}, + tC{"who_can_convo", "int", 0, false, false, "0"}, tC{"enable_embeds", "int", 0, false, false, "-1"}, - tC{"email", "varchar", 200, false, false, "''"}, - tC{"avatar", "varchar", 100, false, false, "''"}, - tC{"message", "text", 0, false, false, "''"}, + ccol("email", 200, "''"), + ccol("avatar", 100, "''"), + text("message"), // TODO: Drop these columns? - tC{"url_prefix", "varchar", 20, false, false, "''"}, - tC{"url_name", "varchar", 100, false, false, "''"}, - //tC{"pub_key", "text", 0, false, false, "''"}, + ccol("url_prefix", 20, "''"), + ccol("url_name", 100, "''"), + //text("pub_key"), tC{"level", "smallint", 0, false, false, "0"}, tC{"score", "int", 0, false, false, "0"}, @@ -63,27 +102,27 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu //tC{"penalty_count","int",0,false,false,"0"}, tC{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect }, - []tblKey{ - tblKey{"uid", "primary", "", false}, - tblKey{"name", "unique", "", false}, + []tK{ + tK{"uid", "primary", "", false}, + tK{"name", "unique", "", false}, }, ) createTable("users_groups", mysqlPre, mysqlCol, []tC{ tC{"gid", "int", 0, false, true, ""}, - tC{"name", "varchar", 100, false, false, ""}, - tC{"permissions", "text", 0, false, false, ""}, - tC{"plugin_perms", "text", 0, false, false, ""}, - tC{"is_mod", "boolean", 0, false, false, "0"}, - tC{"is_admin", "boolean", 0, false, false, "0"}, - tC{"is_banned", "boolean", 0, false, false, "0"}, + ccol("name", 100, ""), + text("permissions"), + text("plugin_perms"), + bcol("is_mod", false), + bcol("is_admin", false), + bcol("is_banned", false), tC{"user_count", "int", 0, false, false, "0"}, // TODO: Implement this - tC{"tag", "varchar", 50, false, false, "''"}, + ccol("tag", 50, "''"), }, - []tblKey{ - tblKey{"gid", "primary", "", false}, + []tK{ + tK{"gid", "primary", "", false}, }, ) @@ -92,7 +131,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu 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 + bcol("two_way", false), // 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, ""}, @@ -100,8 +139,8 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu tC{"minTime", "int", 0, false, false, ""}, // How long someone needs to have been in their current group before being promoted tC{"registeredFor", "int", 0, false, false, "0"}, // minutes }, - []tblKey{ - tblKey{"pid", "primary", "", false}, + []tK{ + tK{"pid", "primary", "", false}, }, ) @@ -122,15 +161,15 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("users_2fa_keys", mysqlPre, mysqlCol, []tC{ tC{"uid", "int", 0, false, false, ""}, - tC{"secret", "varchar", 100, false, false, ""}, - tC{"scratch1", "varchar", 50, false, false, ""}, - tC{"scratch2", "varchar", 50, false, false, ""}, - tC{"scratch3", "varchar", 50, false, false, ""}, - tC{"scratch4", "varchar", 50, false, false, ""}, - tC{"scratch5", "varchar", 50, false, false, ""}, - tC{"scratch6", "varchar", 50, false, false, ""}, - tC{"scratch7", "varchar", 50, false, false, ""}, - tC{"scratch8", "varchar", 50, false, false, ""}, + ccol("secret", 100, ""), + ccol("scratch1", 50, ""), + ccol("scratch2", 50, ""), + ccol("scratch3", 50, ""), + ccol("scratch4", 50, ""), + ccol("scratch5", 50, ""), + ccol("scratch6", 50, ""), + ccol("scratch7", 50, ""), + ccol("scratch8", 50, ""), tC{"createdAt", "createdAt", 0, false, false, ""}, }, []tblKey{ @@ -148,12 +187,12 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"uid","int",0,false,false,""}, tC{"element_id","int",0,false,false,""}, - tC{"element_type","varchar",50,false,false,""}, //forum, profile?, and social_group. Leave blank for global. - tC{"overrides","text",0,false,false,"{}"}, + ccol("element_type",50,""), //forum, profile?, and social_group. Leave blank for global. + text("overrides","{}"), - tC{"mod_queue","boolean",0,false,false,"0"}, - tC{"shadow_ban","boolean",0,false,false,"0"}, - tC{"no_avatar","boolean",0,false,false,"0"}, // Coming Soon. Should this be a perm override instead? + bcol("mod_queue",false), + bcol("shadow_ban",false), + bcol("no_avatar",false), // Coming Soon. Should this be a perm override instead? // Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something? //tC{"posts_per_hour","int",0,false,false,"0"}, @@ -163,7 +202,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu //tC{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters. tC{"issued_by","int",0,false,false,""}, - tC{"issued_at","createdAt",0,false,false,""}, + createdAt("issued_at"), tC{"expires_at","datetime",0,false,false,""}, }, nil, )*/ @@ -174,7 +213,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu tC{"set_group", "int", 0, false, false, ""}, tC{"issued_by", "int", 0, false, false, ""}, - tC{"issued_at", "createdAt", 0, false, false, ""}, + createdAt("issued_at"), tC{"revert_at", "datetime", 0, false, false, ""}, tC{"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future }, @@ -197,10 +236,10 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu // TODO: Add an autoincrement key? createTable("emails", "", "", []tC{ - tC{"email", "varchar", 200, false, false, ""}, + ccol("email", 200, ""), tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"validated", "boolean", 0, false, false, "0"}, - tC{"token", "varchar", 200, false, false, "''"}, + bcol("validated", false), + ccol("token", 200, "''"), }, nil, ) @@ -208,11 +247,11 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu /* createTable("email_domain_blacklist", "", "", []tC{ - tC{"domain", "varchar", 200, false, false, ""}, - tC{"gtld", "boolean", 0, false, false, "0"}, + ccol("domain", 200, ""), + bcol("gtld", false), }, - []tblKey{ - tblKey{"domain", "primary"}, + []tK{ + tK{"domain", "primary"}, }, ) */ @@ -220,26 +259,26 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu // TODO: Implement password resets createTable("password_resets", "", "", []tC{ - tC{"email", "varchar", 200, false, false, ""}, - tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"validated", "varchar", 200, false, false, ""}, // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token - tC{"token", "varchar", 200, false, false, ""}, - tC{"createdAt", "createdAt", 0, false, false, ""}, + ccol("email", 200, ""), + tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token + ccol("token", 200, ""), + createdAt(), }, nil, ) createTable("forums", mysqlPre, mysqlCol, []tC{ tC{"fid", "int", 0, false, true, ""}, - tC{"name", "varchar", 100, false, false, ""}, - tC{"desc", "varchar", 200, false, false, ""}, - tC{"tmpl", "varchar", 200, false, false, "''"}, - tC{"active", "boolean", 0, false, false, "1"}, + ccol("name", 100, ""), + ccol("desc", 200, ""), + ccol("tmpl", 200, "''"), + bcol("active", true), tC{"order", "int", 0, false, false, "0"}, tC{"topicCount", "int", 0, false, false, "0"}, - tC{"preset", "varchar", 100, false, false, "''"}, + ccol("preset", 100, "''"), tC{"parentID", "int", 0, false, false, "0"}, - tC{"parentType", "varchar", 50, false, false, "''"}, + ccol("parentType", 50, "''"), tC{"lastTopicID", "int", 0, false, false, "0"}, tC{"lastReplyerID", "int", 0, false, false, "0"}, }, @@ -252,8 +291,8 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"fid", "int", 0, false, false, ""}, tC{"gid", "int", 0, false, false, ""}, - tC{"preset", "varchar", 100, false, false, "''"}, - tC{"permissions", "text", 0, false, false, ""}, + ccol("preset", 100, "''"), + text("permissions", "{}"), }, []tblKey{ // TODO: Test to see that the compound primary key works @@ -264,19 +303,19 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("topics", mysqlPre, mysqlCol, []tC{ tC{"tid", "int", 0, false, true, ""}, - tC{"title", "varchar", 100, false, false, ""}, // TODO: Increase the max length to 200? - tC{"content", "text", 0, false, false, ""}, - tC{"parsed_content", "text", 0, false, false, ""}, - tC{"createdAt", "createdAt", 0, false, false, ""}, + ccol("title", 100, ""), // TODO: Increase the max length to 200? + text("content"), + text("parsed_content"), + createdAt(), tC{"lastReplyAt", "datetime", 0, false, false, ""}, tC{"lastReplyBy", "int", 0, false, false, ""}, tC{"lastReplyID", "int", 0, false, false, "0"}, tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"is_closed", "boolean", 0, false, false, "0"}, - tC{"sticky", "boolean", 0, false, false, "0"}, + bcol("is_closed", false), + bcol("sticky", false), // TODO: Add an index for this tC{"parentID", "int", 0, false, false, "2"}, - tC{"ip", "varchar", 200, false, false, "''"}, + ccol("ip", 200, "''"), tC{"postCount", "int", 0, false, false, "1"}, tC{"likeCount", "int", 0, false, false, "0"}, tC{"attachCount", "int", 0, false, false, "0"}, @@ -288,14 +327,14 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu // ? - A little hacky, maybe we could do something less likely to bite us with huge numbers of topics? // TODO: Add an index for this? //tC{"lastMonth", "datetime", 0, false, false, ""}, - tC{"css_class", "varchar", 100, false, false, "''"}, + ccol("css_class", 100, "''"), tC{"poll", "int", 0, false, false, "0"}, - tC{"data", "varchar", 200, false, false, "''"}, + ccol("data", 200, "''"), }, - []tblKey{ - tblKey{"tid", "primary", "", false}, - tblKey{"title", "fulltext", "", false}, - tblKey{"content", "fulltext", "", false}, + []tK{ + tK{"tid", "primary", "", false}, + tK{"title", "fulltext", "", false}, + tK{"content", "fulltext", "", false}, }, ) @@ -303,23 +342,23 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"rid", "int", 0, false, true, ""}, // TODO: Rename to replyID? tC{"tid", "int", 0, false, false, ""}, // TODO: Rename to topicID? - tC{"content", "text", 0, false, false, ""}, - tC{"parsed_content", "text", 0, false, false, ""}, - tC{"createdAt", "createdAt", 0, false, false, ""}, + text("content"), + text("parsed_content"), + createdAt(), tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"lastEdit", "int", 0, false, false, "0"}, tC{"lastEditBy", "int", 0, false, false, "0"}, tC{"lastUpdated", "datetime", 0, false, false, ""}, - tC{"ip", "varchar", 200, false, false, "''"}, + ccol("ip", 200, "''"), tC{"likeCount", "int", 0, false, false, "0"}, tC{"attachCount", "int", 0, false, false, "0"}, tC{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why? - tC{"actionType", "varchar", 20, false, false, "''"}, + ccol("actionType", 20, "''"), tC{"poll", "int", 0, false, false, "0"}, }, - []tblKey{ - tblKey{"rid", "primary", "", false}, - tblKey{"content", "fulltext", "", false}, + []tK{ + tK{"rid", "primary", "", false}, + tK{"content", "fulltext", "", false}, }, ) @@ -327,12 +366,12 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"attachID", "int", 0, false, true, ""}, tC{"sectionID", "int", 0, false, false, "0"}, - tC{"sectionTable", "varchar", 200, false, false, "forums"}, + ccol("sectionTable", 200, "forums"), tC{"originID", "int", 0, false, false, ""}, - tC{"originTable", "varchar", 200, false, false, "replies"}, + ccol("originTable", 200, "replies"), tC{"uploadedBy", "int", 0, false, false, ""}, // TODO; Make this a foreign key - tC{"path", "varchar", 200, false, false, ""}, - tC{"extra", "varchar", 200, false, false, ""}, + ccol("path", 200, ""), + ccol("extra", 200, ""), }, []tblKey{ tblKey{"attachID", "primary", "", false}, @@ -342,10 +381,10 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("revisions", mysqlPre, mysqlCol, []tC{ tC{"reviseID", "int", 0, false, true, ""}, - tC{"content", "text", 0, false, false, ""}, + text("content"), tC{"contentID", "int", 0, false, false, ""}, - tC{"contentType", "varchar", 100, false, false, "replies"}, - tC{"createdAt", "createdAt", 0, false, false, ""}, + ccol("contentType", 100, "replies"), + createdAt(), // TODO: Add a createdBy column? }, []tblKey{ @@ -357,7 +396,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"pollID", "int", 0, false, true, ""}, tC{"parentID", "int", 0, false, false, "0"}, - tC{"parentTable", "varchar", 100, false, false, "topics"}, // topics, replies + ccol("parentTable", 100, "topics"), // topics, replies tC{"type", "int", 0, false, false, "0"}, tC{"options", "json", 0, false, false, ""}, tC{"votes", "int", 0, false, false, "0"}, @@ -380,8 +419,8 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu tC{"pollID", "int", 0, false, false, ""}, tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"option", "int", 0, false, false, "0"}, - tC{"castAt", "createdAt", 0, false, false, ""}, - tC{"ip", "varchar", 200, false, false, "''"}, + createdAt("castAt"), + ccol("ip", 200, "''"), }, nil, ) @@ -389,13 +428,13 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"rid", "int", 0, false, true, ""}, tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"content", "text", 0, false, false, ""}, - tC{"parsed_content", "text", 0, false, false, ""}, - tC{"createdAt", "createdAt", 0, false, false, ""}, + text("content"), + text("parsed_content"), + createdAt(), tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"lastEdit", "int", 0, false, false, "0"}, tC{"lastEditBy", "int", 0, false, false, "0"}, - tC{"ip", "varchar", 200, false, false, "''"}, + ccol("ip", 200, "''"), }, []tblKey{ tblKey{"rid", "primary", "", false}, @@ -406,9 +445,9 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"weight", "tinyint", 0, false, false, "1"}, tC{"targetItem", "int", 0, false, false, ""}, - tC{"targetType", "varchar", 50, false, false, "replies"}, + ccol("targetType", 50, "replies"), tC{"sentBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"createdAt", "createdAt", 0, false, false, ""}, + createdAt(), tC{"recalc", "tinyint", 0, false, false, "0"}, }, nil, ) @@ -418,7 +457,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"cid", "int", 0, false, true, ""}, tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"createdAt", "createdAt", 0, false, false, ""}, + createdAt(), tC{"lastReplyAt", "datetime", 0, false, false, ""}, tC{"lastReplyBy", "int", 0, false, false, ""}, }, @@ -432,8 +471,8 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu tC{"pid", "int", 0, false, true, ""}, tC{"cid", "int", 0, false, false, ""}, tC{"createdBy", "int", 0, false, false, ""}, - tC{"body", "varchar", 50, false, false, ""}, - tC{"post", "varchar", 50, false, false, "''"}, + ccol("body", 50, ""), + ccol("post", 50, "''"), }, []tblKey{ tblKey{"pid", "primary", "", false}, @@ -482,35 +521,39 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu 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 - tC{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */ - tC{"event", "varchar", 50, false, false, ""}, /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */ - tC{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ - tC{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ - tC{"createdAt", "createdAt", 0, false, false, ""}, - tC{"extra", "varchar", 200, false, false, "''"}, + tC{"actor", "int", 0, false, false, ""}, /* the one doing the act */ // TODO: Make this a foreign key + tC{"targetUser", "int", 0, false, false, ""}, /* the user who created the item the actor is acting on, some items like forums may lack a targetUser field */ + ccol("event", 50, ""), /* mention, like, reply (as in the act of replying to an item, not the reply item type, you can "reply" to a forum by making a topic in it), friend_invite */ + ccol("elementType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ + + // replacement for elementType + tC{"elementTable", "int", 0, false, false, "0"}, + + tC{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */ + createdAt(), + ccol("extra", 200, "''"), }, - []tblKey{ - tblKey{"asid", "primary", "", false}, + []tK{ + tK{"asid", "primary", "", false}, }, ) 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 */ - tC{"targetType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ - tC{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/ + 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 */ + ccol("targetType", 50, ""), /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ + tC{"level", "int", 0, false, false, "0"}, /* 0: Mentions (aka the global default for any post), 1: Replies To You, 2: All Replies*/ }, nil, ) /* 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 */ createTable("settings", "", "", []tC{ - tC{"name", "varchar", 180, false, false, ""}, - tC{"content", "varchar", 250, false, false, ""}, - tC{"type", "varchar", 50, false, false, ""}, - tC{"constraints", "varchar", 200, false, false, "''"}, + ccol("name", 180, ""), + ccol("content", 250, ""), + ccol("type", 50, ""), + ccol("constraints", 200, "''"), }, []tblKey{ tblKey{"name", "unique", "", false}, @@ -520,8 +563,8 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("word_filters", "", "", []tC{ tC{"wfid", "int", 0, false, true, ""}, - tC{"find", "varchar", 200, false, false, ""}, - tC{"replacement", "varchar", 200, false, false, ""}, + ccol("find", 200, ""), + ccol("replacement", 200, ""), }, []tblKey{ tblKey{"wfid", "primary", "", false}, @@ -530,9 +573,9 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("plugins", "", "", []tC{ - tC{"uname", "varchar", 180, false, false, ""}, - tC{"active", "boolean", 0, false, false, "0"}, - tC{"installed", "boolean", 0, false, false, "0"}, + ccol("uname", 180, ""), + bcol("active", false), + bcol("installed", false), }, []tblKey{ tblKey{"uname", "unique", "", false}, @@ -541,9 +584,9 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("themes", "", "", []tC{ - tC{"uname", "varchar", 180, false, false, ""}, - tC{"default", "boolean", 0, false, false, "0"}, - //tC{"profileUserVars", "text", 0, false, false, "''"}, + ccol("uname", 180, ""), + bcol("default", false), + //text("profileUserVars"), }, []tblKey{ tblKey{"uname", "unique", "", false}, @@ -554,11 +597,11 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"wid", "int", 0, false, true, ""}, tC{"position", "int", 0, false, false, ""}, - tC{"side", "varchar", 100, false, false, ""}, - tC{"type", "varchar", 100, false, false, ""}, - tC{"active", "boolean", 0, false, false, "0"}, - tC{"location", "varchar", 100, false, false, ""}, - tC{"data", "text", 0, false, false, "''"}, + ccol("side", 100, ""), + ccol("type", 100, ""), + bcol("active", false), + ccol("location", 100, ""), + text("data"), }, []tblKey{ tblKey{"wid", "primary", "", false}, @@ -578,51 +621,51 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"miid", "int", 0, false, true, ""}, tC{"mid", "int", 0, false, false, ""}, - tC{"name", "varchar", 200, false, false, "''"}, - tC{"htmlID", "varchar", 200, false, false, "''"}, - tC{"cssClass", "varchar", 200, false, false, "''"}, - tC{"position", "varchar", 100, false, false, ""}, - tC{"path", "varchar", 200, false, false, "''"}, - tC{"aria", "varchar", 200, false, false, "''"}, - tC{"tooltip", "varchar", 200, false, false, "''"}, - tC{"tmplName", "varchar", 200, false, false, "''"}, + ccol("name", 200, "''"), + ccol("htmlID", 200, "''"), + ccol("cssClass", 200, "''"), + ccol("position", 100, ""), + ccol("path", 200, "''"), + ccol("aria", 200, "''"), + ccol("tooltip", 200, "''"), + ccol("tmplName", 200, "''"), tC{"order", "int", 0, false, false, "0"}, - tC{"guestOnly", "boolean", 0, false, false, "0"}, - tC{"memberOnly", "boolean", 0, false, false, "0"}, - tC{"staffOnly", "boolean", 0, false, false, "0"}, - tC{"adminOnly", "boolean", 0, false, false, "0"}, + bcol("guestOnly", false), + bcol("memberOnly", false), + bcol("staffOnly", false), + bcol("adminOnly", false), }, - []tblKey{ - tblKey{"miid", "primary", "", false}, + []tK{ + tK{"miid", "primary", "", false}, }, ) createTable("pages", mysqlPre, mysqlCol, []tC{ tC{"pid", "int", 0, false, true, ""}, - //tC{"path", "varchar", 200, false, false, ""}, - tC{"name", "varchar", 200, false, false, ""}, - tC{"title", "varchar", 200, false, false, ""}, - tC{"body", "text", 0, false, false, ""}, + //ccol("path", 200, ""), + ccol("name", 200, ""), + ccol("title", 200, ""), + text("body"), // TODO: Make this a table? - tC{"allowedGroups", "text", 0, false, false, ""}, + text("allowedGroups"), tC{"menuID", "int", 0, false, false, "-1"}, // simple sidebar menu }, - []tblKey{ - tblKey{"pid", "primary", "", false}, + []tK{ + tK{"pid", "primary", "", false}, }, ) createTable("registration_logs", "", "", []tC{ tC{"rlid", "int", 0, false, true, ""}, - tC{"username", "varchar", 100, false, false, ""}, + ccol("username", 100, ""), tC{"email", "varchar", 100, false, false, ""}, - tC{"failureReason", "varchar", 100, false, false, ""}, - tC{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? - tC{"ipaddress", "varchar", 200, false, false, ""}, - tC{"doneAt", "createdAt", 0, false, false, ""}, + ccol("failureReason", 100, ""), + bcol("success", false), // Did this attempt succeed? + ccol("ipaddress", 200, ""), + createdAt("doneAt"), }, []tblKey{ tblKey{"rlid", "primary", "", false}, @@ -633,9 +676,9 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"lid", "int", 0, false, true, ""}, tC{"uid", "int", 0, false, false, ""}, - tC{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? - tC{"ipaddress", "varchar", 200, false, false, ""}, - tC{"doneAt", "createdAt", 0, false, false, ""}, + bcol("success", false), // Did this attempt succeed? + ccol("ipaddress", 200, ""), + createdAt("doneAt"), }, []tblKey{ tblKey{"lid", "primary", "", false}, @@ -644,25 +687,25 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("moderation_logs", "", "", []tC{ - tC{"action", "varchar", 100, false, false, ""}, + ccol("action", 100, ""), tC{"elementID", "int", 0, false, false, ""}, - tC{"elementType", "varchar", 100, false, false, ""}, - tC{"ipaddress", "varchar", 200, false, false, ""}, + ccol("elementType", 100, ""), + ccol("ipaddress", 200, ""), tC{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"doneAt", "datetime", 0, false, false, ""}, - tC{"extra", "text", 0, false, false, ""}, + text("extra"), }, nil, ) createTable("administration_logs", "", "", []tC{ - tC{"action", "varchar", 100, false, false, ""}, + ccol("action", 100, ""), tC{"elementID", "int", 0, false, false, ""}, - tC{"elementType", "varchar", 100, false, false, ""}, - tC{"ipaddress", "varchar", 200, false, false, ""}, + ccol("elementType", 100, ""), + ccol("ipaddress", 200, ""), tC{"actorID", "int", 0, false, false, ""}, // TODO: Make this a foreign key tC{"doneAt", "datetime", 0, false, false, ""}, - tC{"extra", "text", 0, false, false, ""}, + text("extra"), }, nil, ) @@ -671,7 +714,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu tC{"count", "int", 0, false, false, "0"}, tC{"avg", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, - tC{"route", "varchar", 200, false, false, ""}, // todo: set a default empty here + ccol("route", 200, ""), // TODO: set a default empty here }, nil, ) @@ -679,8 +722,8 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, - tC{"browser", "varchar", 200, false, false, ""}, // googlebot, firefox, opera, etc. - //tC{"version","varchar",0,false,false,""}, // the version of the browser or bot + ccol("browser", 200, ""), // googlebot, firefox, opera, etc. + //ccol("version",0,""), // the version of the browser or bot }, nil, ) @@ -688,7 +731,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, - tC{"system", "varchar", 200, false, false, ""}, // windows, android, unknown, etc. + ccol("system", 200, ""), // windows, android, unknown, etc. }, nil, ) @@ -696,7 +739,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, - tC{"lang", "varchar", 200, false, false, ""}, // en, ru, etc. + ccol("lang", 200, ""), // en, ru, etc. }, nil, ) @@ -704,7 +747,7 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu []tC{ tC{"count", "int", 0, false, false, "0"}, tC{"createdAt", "datetime", 0, false, false, ""}, - tC{"domain", "varchar", 200, false, false, ""}, + ccol("domain", 200, ""), }, nil, ) @@ -764,18 +807,19 @@ func createTables2(a qgen.Adapter, f func(table, charset, collation string, colu createTable("meta", "", "", []tC{ - tC{"name", "varchar", 200, false, false, ""}, - tC{"value", "varchar", 200, false, false, ""}, + ccol("name", 200, ""), + ccol("value", 200, ""), }, nil, ) - /*createTable("tables", mysqlPre, mysqlCol, + /*createTable("tables", "", "", []tC{ tC{"id", "int", 0, false, true, ""}, - tC{"name", "varchar", 200, false, false, ""}, + ccol("name", 200, ""), }, - []tblKey{ - tblKey{"id", "primary", "", false}, + []tK{ + tK{"id", "primary", "", false}, + tK{"name", "unique", "", false}, }, )*/ diff --git a/common/auth.go b/common/auth.go index fcec795c..922ad809 100644 --- a/common/auth.go +++ b/common/auth.go @@ -98,7 +98,7 @@ func NewDefaultAuth() (*DefaultAuth, error) { // Authenticate checks if a specific username and password is valid and returns the UID for the corresponding user, if so. Otherwise, a user safe error. // IF MFA is enabled, then pass it back a flag telling the caller that authentication isn't complete yet // TODO: Find a better way of handling errors we don't want to reach the user -func (auth *DefaultAuth) Authenticate(name string, password string) (uid int, err error, requiresExtraAuth bool) { +func (auth *DefaultAuth) Authenticate(name, password string) (uid int, err error, requiresExtraAuth bool) { var realPassword, salt string err = auth.login.QueryRow(name).Scan(&uid, &realPassword, &salt) if err == ErrNoRows { @@ -211,7 +211,7 @@ func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session stri // TODO: Set the cookie domain // SetProvisionalCookies sets the two cookies required for guests to be recognised as having passed the initial login but not having passed the additional checks (e.g. multi-factor authentication) -func (auth *DefaultAuth) SetProvisionalCookies(w http.ResponseWriter, uid int, provSession string, signedSession string) { +func (auth *DefaultAuth) SetProvisionalCookies(w http.ResponseWriter, uid int, provSession, signedSession string) { cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: int(Year)} setCookie(w, &cookie, "lax") cookie = http.Cookie{Name: "provSession", Value: provSession, Path: "/", MaxAge: int(Year)} @@ -282,7 +282,7 @@ func (auth *DefaultAuth) CreateSession(uid int) (session string, err error) { return session, nil } -func (auth *DefaultAuth) CreateProvisionalSession(uid int) (provSession string, signedSession string, err error) { +func (auth *DefaultAuth) CreateProvisionalSession(uid int) (provSession, signedSession string, err error) { provSession, err = GenerateSafeString(SessionLength) if err != nil { return "", "", err diff --git a/common/pages.go b/common/pages.go index ef9c24ff..aeb0db41 100644 --- a/common/pages.go +++ b/common/pages.go @@ -232,6 +232,7 @@ type ProfilePage struct { Blocked bool CanMessage bool CanComment bool + ShowComments bool } type CreateTopicPage struct { @@ -249,7 +250,7 @@ type IPSearchPage struct { type RegisterPage struct { *Header RequireEmail bool - Token string + Token string } type Account struct { @@ -278,8 +279,8 @@ type AccountBlocksPage struct { type AccountPrivacyPage struct { *Header - ProfileComments bool - ReceiveConvos bool + ProfileComments int + ReceiveConvos int EnableEmbeds bool } diff --git a/common/template_init.go b/common/template_init.go index e3b3981f..28e00654 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -95,14 +95,14 @@ var Template_account_handle = genIntTmpl("account") func tmplInitUsers() (*User, *User, *User) { avatar, microAvatar := BuildAvatar(62, "") - u := 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, StartTime, "0.0.0.0.0", 0, 0, nil} + u := 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, StartTime, "0.0.0.0.0", 0, 0, nil, UserPrivacy{}} // TODO: Do a more accurate level calculation for this? avatar, microAvatar = BuildAvatar(1, "") - u2 := 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, StartTime, "127.0.0.1", 0, 0, nil} + u2 := 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, StartTime, "127.0.0.1", 0, 0, nil, UserPrivacy{}} avatar, microAvatar = BuildAvatar(2, "") - u3 := 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, StartTime, "::1", 0, 0, nil} + u3 := 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, StartTime, "::1", 0, 0, nil, UserPrivacy{}} return &u, &u2, &u3 } @@ -306,7 +306,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string return err } - ppage := ProfilePage{htitle("User 526"), replyList, *user, 0, 0, false, false, false} // TODO: Use the score from user to generate the currentScore and nextScore + ppage := ProfilePage{htitle("User 526"), replyList, *user, 0, 0, false, false, false, false} // TODO: Use the score from user to generate the currentScore and nextScore t.Add("profile", "c.ProfilePage", ppage) var topicsList []*TopicsRow @@ -349,7 +349,7 @@ func compileTemplates(wg *sync.WaitGroup, c *tmpl.CTemplateSet, themeName string } t.AddStd("login", "c.Page", Page{htitle("Login Page"), tList, nil}) - t.AddStd("register", "c.RegisterPage", RegisterPage{htitle("Registration Page"), false,""}) + t.AddStd("register", "c.RegisterPage", RegisterPage{htitle("Registration Page"), false, ""}) 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"} diff --git a/common/user.go b/common/user.go index cd1c4462..37d935b6 100644 --- a/common/user.go +++ b/common/user.go @@ -62,6 +62,12 @@ type User struct { TempGroup int ParseSettings *ParseSettings + Privacy UserPrivacy +} + +type UserPrivacy struct { + ShowComments int // 0 = default, 1 = public, 2 = registered, 3 = friends, 4 = mods / self + AllowMessage int // 0 = default, 1 = registered, 2 = friends, 3 = mods / self } func (u *User) WebSockets() *WsJSONUser { @@ -191,7 +197,7 @@ func init() { decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(), //recalcLastLiked: acc... updateLastIP: acc.SimpleUpdate(u, "last_ip=?", w), - updatePrivacy: acc.Update(u).Set("enable_embeds=?").Where(w).Prepare(), + updatePrivacy: acc.Update(u).Set("profile_comments=?,enable_embeds=?").Where(w).Prepare(), setPassword: acc.Update(u).Set("password=?,salt=?").Where(w).Prepare(), @@ -211,6 +217,10 @@ func init() { } func (u *User) Init() { + // TODO: Let admins configure the minimum default? + if u.Privacy.ShowComments < 1 { + u.Privacy.ShowComments = 1 + } u.Avatar, u.MicroAvatar = BuildAvatar(u.ID, u.RawAvatar) u.Link = BuildProfileURL(NameToSlug(u.Name), u.ID) u.Tag = Groups.DirtyGet(u.Group).Tag @@ -554,12 +564,27 @@ func (u *User) UpdateIP(ip string) error { return err } -func (u *User) UpdatePrivacy(enableEmbeds int) error { - _, err := userStmts.updatePrivacy.Exec(enableEmbeds, u.ID) +//var ErrMalformedInteger = errors.New("malformed integer") +var ErrProfileCommentsOutOfBounds = errors.New("profile_comments must be an integer between -1 and 4") +var ErrEnableEmbedsOutOfBounds = errors.New("enable_embeds must be -1, 0 or 1") + +/*func (u *User) UpdatePrivacyS(sProfileComments, sEnableEmbeds string) error { + + return u.UpdatePrivacy(profileComments, enableEmbeds) +}*/ + +func (u *User) UpdatePrivacy(profileComments, enableEmbeds int) error { + if profileComments < -1 || profileComments > 4 { + return ErrProfileCommentsOutOfBounds + } + if enableEmbeds < -1 || enableEmbeds > 1 { + return ErrEnableEmbedsOutOfBounds + } + _, e := userStmts.updatePrivacy.Exec(profileComments, enableEmbeds, u.ID) if uc := Users.GetCache(); uc != nil { uc.Remove(u.ID) } - return err + return e } func (u *User) Update(name, email string, group int) (err error) { @@ -790,3 +815,17 @@ func BuildProfileURL(slug string, uid int) string { } return "/user/" + slug + "." + strconv.Itoa(uid) } + +func BuildProfileURLSb(sb *strings.Builder, slug string, uid int) { + if slug == "" || !Config.BuildSlugs { + sb.Grow(6 + 1) + sb.WriteString("/user/") + sb.WriteString(strconv.Itoa(uid)) + return + } + sb.Grow(7 + 1 + len(slug)) + sb.WriteString("/user/") + sb.WriteString(slug) + sb.WriteRune('.') + sb.WriteString(strconv.Itoa(uid)) +} diff --git a/common/user_store.go b/common/user_store.go index fa6ea18e..74f9f586 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -54,16 +54,16 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) { cache = NewNullUserCache() } u := "users" - allCols := "uid,name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds" + allCols := "uid,name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds,profile_comments,who_can_convo" // TODO: Add an admin version of registerStmt with more flexibility? return &DefaultUserStore{ cache: cache, - get: acc.Select(u).Columns("name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, createdAt, enable_embeds").Where("uid=?").Prepare(), + get: acc.Select(u).Columns("name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds,profile_comments,who_can_convo").Where("uid=?").Prepare(), getByName: acc.Select(u).Columns(allCols).Where("name=?").Prepare(), getOffset: acc.Select(u).Columns(allCols).Orderby("uid ASC").Limit("?,?").Prepare(), getAll: acc.Select(u).Columns(allCols).Prepare(), exists: acc.Exists(u, "uid").Prepare(), - register: acc.Insert(u).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 + register: acc.Insert(u).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 nameExists: acc.Exists(u, "name").Prepare(), count: acc.Count(u).Prepare(), }, acc.FirstError() @@ -93,16 +93,7 @@ func (s *DefaultUserStore) Get(id int) (*User, error) { u = &User{ID: id, Loggedin: true} var embeds int - err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds) - /*if err != nil { - return nil, err - } - if embeds != -1 { - u.ParseSettings = DefaultParseSettings.CopyPtr() - u.ParseSettings.NoEmbed = embeds == 0 - } - u.Init() - s.cache.Set(u)*/ + err = s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage) if err == nil { if embeds != -1 { u.ParseSettings = DefaultParseSettings.CopyPtr() @@ -122,7 +113,7 @@ func (s *DefaultUserStore) Getn(id int) *User { u = &User{ID: id, Loggedin: true} var embeds int - err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds) + err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage) if err != nil { return nil } @@ -140,7 +131,7 @@ func (s *DefaultUserStore) Getn(id int) *User { func (s *DefaultUserStore) GetByName(name string) (*User, error) { u := &User{Loggedin: true} var embeds int - 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.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds) + 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.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage) if err != nil { return nil, err } @@ -165,7 +156,7 @@ func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*User, err er var embeds int 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.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds) + err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage) if err != nil { return nil, err } @@ -180,24 +171,24 @@ func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*User, err er return users, rows.Err() } func (s *DefaultUserStore) Each(f func(*User) error) error { - rows, err := s.getAll.Query() - if err != nil { - return err + rows, e := s.getAll.Query() + if e != nil { + return e } defer rows.Close() var embeds int for rows.Next() { u := new(User) - if err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds); err != nil { - return err + if e := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage); e != nil { + return e } if embeds != -1 { u.ParseSettings = DefaultParseSettings.CopyPtr() u.ParseSettings.NoEmbed = embeds == 0 } u.Init() - if err := f(u); err != nil { - return err + if e := f(u); e != nil { + return e } } return rows.Err() @@ -246,7 +237,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,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds").Where("uid IN(" + q + ")").Query(idList...) + rows, err := qgen.NewAcc().Select("users").Columns("uid,name,group,active,is_super_admin,session,email,avatar,message,level,score,posts,liked,last_ip,temp_group,createdAt,enable_embeds,profile_comments,who_can_convo").Where("uid IN(" + q + ")").Query(idList...) if err != nil { return list, err } @@ -255,7 +246,7 @@ func (s *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) var embeds int 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.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds) + err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage) if err != nil { return list, err } @@ -292,7 +283,7 @@ 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} var embeds int - err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds) + err := s.get.QueryRow(id).Scan(&u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &u.CreatedAt, &embeds, &u.Privacy.ShowComments, &u.Privacy.AllowMessage) if err == nil { if embeds != -1 { u.ParseSettings = DefaultParseSettings.CopyPtr() diff --git a/langs/english.json b/langs/english.json index 29bfcfa8..4559db67 100644 --- a/langs/english.json +++ b/langs/english.json @@ -577,7 +577,11 @@ "account_password_update_button":"Update", "account_privacy_head":"Privacy", - "account_privacy_profile_comments":"Enable Profile Comments", + "account_privacy_profile_comments":"Profile Comment Visibility", + "account_privacy_profile_comments_public":"Anyone", + "account_privacy_profile_comments_registered":"Registered Users", + "account_privacy_profile_comments_self":"Only Me", + "account_privacy_enable_embeds":"Enable Embeds", "account_privacy_button":"Update", "account_mfa_head":"Manage 2FA", diff --git a/patcher/patches.go b/patcher/patches.go index bcbeafaa..14becc05 100644 --- a/patcher/patches.go +++ b/patcher/patches.go @@ -51,18 +51,21 @@ func init() { addPatch(31, patch31) addPatch(32, patch32) addPatch(33, patch33) + addPatch(34, patch34) + //addPatch(35, patch35) +} + +func bcol(col string, val bool) qgen.DBTableColumn { + if val { + return tC{col, "boolean", 0, false, false, "1"} + } + return tC{col, "boolean", 0, false, false, "0"} +} +func ccol(col string, size int, sdefault string) qgen.DBTableColumn { + return tC{col, "varchar", size, false, false, sdefault} } func patch0(scanner *bufio.Scanner) (err error) { - err = execStmt(qgen.Builder.DropTable("menus")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.DropTable("menu_items")) - if err != nil { - return err - } - err = createTable("menus", "", "", []tC{ tC{"mid", "int", 0, false, true, ""}, @@ -79,20 +82,20 @@ func patch0(scanner *bufio.Scanner) (err error) { []tC{ tC{"miid", "int", 0, false, true, ""}, tC{"mid", "int", 0, false, false, ""}, - tC{"name", "varchar", 200, false, false, ""}, - tC{"htmlID", "varchar", 200, false, false, "''"}, - tC{"cssClass", "varchar", 200, false, false, "''"}, - tC{"position", "varchar", 100, false, false, ""}, - tC{"path", "varchar", 200, false, false, "''"}, - tC{"aria", "varchar", 200, false, false, "''"}, - tC{"tooltip", "varchar", 200, false, false, "''"}, - tC{"tmplName", "varchar", 200, false, false, "''"}, + ccol("name", 200, ""), + ccol("htmlID", 200, "''"), + ccol("cssClass", 200, "''"), + ccol("position", 100, ""), + ccol("path", 200, "''"), + ccol("aria", 200, "''"), + ccol("tooltip", 200, "''"), + ccol("tmplName", 200, "''"), tC{"order", "int", 0, false, false, "0"}, - tC{"guestOnly", "boolean", 0, false, false, "0"}, - tC{"memberOnly", "boolean", 0, false, false, "0"}, - tC{"staffOnly", "boolean", 0, false, false, "0"}, - tC{"adminOnly", "boolean", 0, false, false, "0"}, + bcol("guestOnly", false), + bcol("memberOnly", false), + bcol("staffOnly", false), + bcol("adminOnly", false), }, []tK{ tK{"miid", "primary", "", false}, @@ -194,11 +197,11 @@ func patch3(scanner *bufio.Scanner) error { return createTable("registration_logs", "", "", []tC{ tC{"rlid", "int", 0, false, true, ""}, - tC{"username", "varchar", 100, false, false, ""}, - tC{"email", "varchar", 100, false, false, ""}, - tC{"failureReason", "varchar", 100, false, false, ""}, - tC{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? - tC{"ipaddress", "varchar", 200, false, false, ""}, + ccol("username", 100, ""), + ccol("email", 100, ""), + ccol("failureReason", 100, ""), + bcol("success", false), // Did this attempt succeed? + ccol("ipaddress", 200, ""), tC{"doneAt", "createdAt", 0, false, false, ""}, }, []tK{ @@ -258,8 +261,8 @@ func patch4(scanner *bufio.Scanner) error { err = createTable("pages", "utf8mb4", "utf8mb4_general_ci", []tC{ tC{"pid", "int", 0, false, true, ""}, - tC{"name", "varchar", 200, false, false, ""}, - tC{"title", "varchar", 200, false, false, ""}, + ccol("name", 200, ""), + ccol("title", 200, ""), tC{"body", "text", 0, false, false, ""}, tC{"allowedGroups", "text", 0, false, false, ""}, tC{"menuID", "int", 0, false, false, "-1"}, @@ -293,29 +296,24 @@ func patch5(scanner *bufio.Scanner) error { return err } - err = createTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci", + return createTable("users_2fa_keys", "utf8mb4", "utf8mb4_general_ci", []tC{ tC{"uid", "int", 0, false, false, ""}, - tC{"secret", "varchar", 100, false, false, ""}, - tC{"scratch1", "varchar", 50, false, false, ""}, - tC{"scratch2", "varchar", 50, false, false, ""}, - tC{"scratch3", "varchar", 50, false, false, ""}, - tC{"scratch4", "varchar", 50, false, false, ""}, - tC{"scratch5", "varchar", 50, false, false, ""}, - tC{"scratch6", "varchar", 50, false, false, ""}, - tC{"scratch7", "varchar", 50, false, false, ""}, - tC{"scratch8", "varchar", 50, false, false, ""}, + ccol("secret", 100, ""), + ccol("scratch1", 50, ""), + ccol("scratch2", 50, ""), + ccol("scratch3", 50, ""), + ccol("scratch4", 50, ""), + ccol("scratch5", 50, ""), + ccol("scratch6", 50, ""), + ccol("scratch7", 50, ""), + ccol("scratch8", 50, ""), tC{"createdAt", "createdAt", 0, false, false, ""}, }, []tK{ tK{"uid", "primary", "", false}, }, ) - if err != nil { - return err - } - - return nil } func patch6(scanner *bufio.Scanner) error { @@ -382,10 +380,6 @@ func patch8(scanner *bufio.Scanner) error { return err } - err = execStmt(qgen.Builder.DropTable("updates")) - if err != nil { - return err - } return createTable("updates", "", "", []tC{ tC{"dbVersion", "int", 0, false, false, "0"}, @@ -404,8 +398,8 @@ func patch9(scanner *bufio.Scanner) error { []tC{ tC{"lid", "int", 0, false, true, ""}, tC{"uid", "int", 0, false, false, ""}, - tC{"success", "bool", 0, false, false, "0"}, // Did this attempt succeed? - tC{"ipaddress", "varchar", 200, false, false, ""}, + bcol("success", false), // Did this attempt succeed? + ccol("ipaddress", 200, ""), tC{"doneAt", "createdAt", 0, false, false, ""}, }, []tK{ @@ -463,7 +457,7 @@ func patch11(scanner *bufio.Scanner) error { } // Attachments for replies got the topicID rather than the replyID for a while in error, so we want to separate these out - _, err = acc().Update("attachments").Set("originTable = 'freplies'").Where("originTable = 'replies'").Exec() + _, err = acc().Update("attachments").Set("originTable='freplies'").Where("originTable='replies'").Exec() if err != nil { return err } @@ -491,39 +485,26 @@ func patch11(scanner *bufio.Scanner) error { } func patch12(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddIndex("topics", "parentID", "parentID")) - if err != nil { - return err + var e error + addIndex := func(tbl, iname, colname string) { + if e != nil { + return + } + /*e = execStmt(qgen.Builder.RemoveIndex(tbl, iname)) + if e != nil { + return + }*/ + e = execStmt(qgen.Builder.AddIndex(tbl, iname, colname)) } - err = execStmt(qgen.Builder.AddIndex("replies", "tid", "tid")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddIndex("polls", "parentID", "parentID")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddIndex("likes", "targetItem", "targetItem")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddIndex("emails", "uid", "uid")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddIndex("attachments", "originID", "originID")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddIndex("attachments", "path", "path")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddIndex("activity_stream_matches", "watcher", "watcher")) - if err != nil { - return err - } - return nil + addIndex("topics", "parentID", "parentID") + addIndex("replies", "tid", "tid") + addIndex("polls", "parentID", "parentID") + addIndex("likes", "targetItem", "targetItem") + addIndex("emails", "uid", "uid") + addIndex("attachments", "originID", "originID") + addIndex("attachments", "path", "path") + addIndex("activity_stream_matches", "watcher", "watcher") + return e } func patch13(scanner *bufio.Scanner) error { @@ -554,17 +535,17 @@ func patch15(scanner *bufio.Scanner) error { func patch16(scanner *bufio.Scanner) error { return createTable("password_resets", "", "", []tC{ - tC{"email", "varchar", 200, false, false, ""}, - tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key - tC{"validated", "varchar", 200, false, false, ""}, // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token - tC{"token", "varchar", 200, false, false, ""}, + ccol("email", 200, ""), + tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key + ccol("validated", 200, ""), // Token given once the one-use token is consumed, used to prevent multiple people consuming the same one-use token + ccol("token", 200, ""), tC{"createdAt", "createdAt", 0, false, false, ""}, }, nil, ) } func patch17(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.AddColumn("attachments", tC{"extra", "varchar", 200, false, false, ""}, nil)) + err := execStmt(qgen.Builder.AddColumn("attachments", ccol("extra", 200, ""), nil)) if err != nil { return err } @@ -575,7 +556,7 @@ func patch17(scanner *bufio.Scanner) error { if err != nil { return err } - _, err = acc().Update("attachments").Set("sectionID=?").Where("originTable = 'topics' AND originID = ?").Exec(parentID, tid) + _, err = acc().Update("attachments").Set("sectionID=?").Where("originTable='topics' AND originID=?").Exec(parentID, tid) return err }) if err != nil { @@ -592,7 +573,7 @@ func patch17(scanner *bufio.Scanner) error { if err != nil { return err } - _, err = acc().Update("attachments").Set("sectionID=?, extra=?").Where("originTable = 'replies' AND originID = ?").Exec(sectionID, tid, rid) + _, err = acc().Update("attachments").Set("sectionID=?, extra=?").Where("originTable='replies' AND originID=?").Exec(sectionID, tid, rid) return err }) } @@ -613,16 +594,15 @@ func patch19(scanner *bufio.Scanner) error { func patch20(scanner *bufio.Scanner) error { err := acc().Select("activity_stream_matches").Cols("asid").Each(func(rows *sql.Rows) error { var asid int - err := rows.Scan(&asid) - if err != nil { - return err + if e := rows.Scan(&asid); e != nil { + return e } - err = acc().Select("activity_stream").Cols("asid").Where("asid = ?").QueryRow(asid).Scan(&asid) - if err != sql.ErrNoRows { - return err + e := acc().Select("activity_stream").Cols("asid").Where("asid=?").QueryRow(asid).Scan(&asid) + if e != sql.ErrNoRows { + return e } - _, err = acc().Delete("activity_stream_matches").Where("asid = ?").Run(asid) - return err + _, e = acc().Delete("activity_stream_matches").Where("asid=?").Run(asid) + return e }) if err != nil { return err @@ -644,8 +624,8 @@ func patch21(scanner *bufio.Scanner) error { err = createTable("meta", "", "", []tC{ - tC{"name", "varchar", 200, false, false, ""}, - tC{"value", "varchar", 200, false, false, ""}, + ccol("name", 200, ""), + ccol("value", 200, ""), }, nil, ) if err != nil { @@ -656,15 +636,11 @@ func patch21(scanner *bufio.Scanner) error { } func patch22(scanner *bufio.Scanner) error { - return execStmt(qgen.Builder.AddColumn("forums", tC{"tmpl", "varchar", 200, false, false, "''"}, nil)) + return execStmt(qgen.Builder.AddColumn("forums", ccol("tmpl", 200, "''"), nil)) } func patch23(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.DropTable("conversations")) - if err != nil { - return err - } - err = createTable("conversations", "", "", + err := createTable("conversations", "", "", []tC{ tC{"cid", "int", 0, false, true, ""}, tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key @@ -680,17 +656,13 @@ func patch23(scanner *bufio.Scanner) error { return err } - err = execStmt(qgen.Builder.DropTable("conversations_posts")) - if err != nil { - return err - } err = createTable("conversations_posts", "", "", []tC{ tC{"pid", "int", 0, false, true, ""}, tC{"cid", "int", 0, false, false, ""}, tC{"createdBy", "int", 0, false, false, ""}, - tC{"body", "varchar", 50, false, false, ""}, - tC{"post", "varchar", 50, false, false, "''"}, + ccol("body", 50, ""), + ccol("post", 50, "''"), }, []tK{ tK{"pid", "primary", "", false}, @@ -700,10 +672,6 @@ func patch23(scanner *bufio.Scanner) error { return err } - err = execStmt(qgen.Builder.DropTable("conversations_participants")) - if err != nil { - return err - } return createTable("conversations_participants", "", "", []tC{ tC{"uid", "int", 0, false, false, ""}, @@ -713,16 +681,12 @@ func patch23(scanner *bufio.Scanner) error { } func patch24(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.DropTable("users_groups_promotions")) - if err != nil { - return err - } return 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 + bcol("two_way", false), // 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, ""}, @@ -812,28 +776,21 @@ func patch29(scanner *bufio.Scanner) error { return err } - fixCol := func(tbl string) error { - //err := execStmt(qgen.Builder.RenameColumn(tbl, "ipaddress","ip")) - err := execStmt(qgen.Builder.ChangeColumn(tbl, "ipaddress", tC{"ip", "varchar", 200, false, false, "''"})) - if err != nil { - return err + fixCols := func(tbls ...string) error { + for _, tbl := range tbls { + //err := execStmt(qgen.Builder.RenameColumn(tbl, "ipaddress","ip")) + err := execStmt(qgen.Builder.ChangeColumn(tbl, "ipaddress", ccol("ip", 200, "''"))) + if err != nil { + return err + } + err = execStmt(qgen.Builder.SetDefaultColumn(tbl, "ip", "varchar", "")) + if err != nil { + return err + } } - return execStmt(qgen.Builder.SetDefaultColumn(tbl, "ip", "varchar", "")) + return nil } - - err = fixCol("topics") - if err != nil { - return err - } - err = fixCol("replies") - if err != nil { - return err - } - err = fixCol("polls_votes") - if err != nil { - return err - } - err = fixCol("users_replies") + err = fixCols("topics", "replies", "polls_votes", "users_replies") if err != nil { return err } @@ -867,36 +824,31 @@ func patch30(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", "")) } -func patch31(scanner *bufio.Scanner) error { - err := execStmt(qgen.Builder.RemoveIndex("topics", "title")) +func patch31(scanner *bufio.Scanner) (e error) { + addKey := func(tbl, col string, tk qgen.DBTableKey) error { + /*err := execStmt(qgen.Builder.RemoveIndex(tbl, col)) + if err != nil { + return err + }*/ + return execStmt(qgen.Builder.AddKey(tbl, col, tk)) + } + err := addKey("topics", "title", tK{"title", "fulltext", "", false}) if err != nil { return err } - err = execStmt(qgen.Builder.RemoveIndex("topics", "content")) + err = addKey("topics", "content", tK{"content", "fulltext", "", false}) if err != nil { return err } - err = execStmt(qgen.Builder.RemoveIndex("replies", "content")) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddKey("topics", "title", tK{"title", "fulltext", "", false})) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddKey("topics", "content", tK{"content", "fulltext", "", false})) - if err != nil { - return err - } - err = execStmt(qgen.Builder.AddKey("replies", "content", tK{"content", "fulltext", "", false})) - if err != nil { - return err - } - return nil + return addKey("replies", "content", tK{"content", "fulltext", "", false}) } -func createTable(table, charset, collation string, cols []tC, keys []tK) error { - return execStmt(qgen.Builder.CreateTable(table, charset, collation, cols, keys)) +func createTable(tbl, charset, collation string, cols []tC, keys []tK) error { + err := execStmt(qgen.Builder.DropTable(tbl)) + if err != nil { + return err + } + return execStmt(qgen.Builder.CreateTable(tbl, charset, collation, cols, keys)) } func patch32(scanner *bufio.Scanner) error { @@ -913,3 +865,75 @@ func patch32(scanner *bufio.Scanner) error { func patch33(scanner *bufio.Scanner) error { return execStmt(qgen.Builder.AddColumn("viewchunks", tC{"avg", "int", 0, false, false, "0"}, nil)) } + +func patch34(scanner *bufio.Scanner) error { + /*err := createTable("tables", "", "", + []tC{ + tC{"id", "int", 0, false, true, ""}, + ccol("name", 200, ""), + }, + []tK{ + tK{"id", "primary", "", false}, + tK{"name", "unique", "", false}, + }, + ) + if err != nil { + return err + } + insert := func(tbl, cols, fields string) { + if err != nil { + return + } + err = execStmt(qgen.Builder.SimpleInsert(tbl, cols, fields)) + } + insert("tables", "name", "forums") + insert("tables", "name", "topics") + insert("tables", "name", "replies") + // ! Hold onto freplies for a while longer + insert("tables", "name", "freplies")*/ + /*err := execStmt(qgen.Builder.AddColumn("topics", tC{"attachCount", "int", 0, false, false, "0"}, nil)) + if err != nil { + return err + }*/ + overwriteColumn := func(tbl, col string, tc qgen.DBTableColumn) error { + /*e := execStmt(qgen.Builder.DropColumn(tbl, col)) + if e != nil { + return e + }*/ + return execStmt(qgen.Builder.AddColumn(tbl, tc, nil)) + } + err := overwriteColumn("users", "profile_comments", tC{"profile_comments", "int", 0, false, false, "0"}) + if err != nil { + return err + } + err = overwriteColumn("users", "who_can_convo", tC{"who_can_convo", "int", 0, false, false, "0"}) + if err != nil { + return err + } + + setDefault := func(tbl, col, typ, val string) { + if err != nil { + return + } + err = execStmt(qgen.Builder.SetDefaultColumn(tbl, col, typ, val)) + } + setDefault("users_groups", "permissions", "text", "{}") + setDefault("users_groups", "plugin_perms", "text", "{}") + setDefault("forums_permissions", "permissions", "text", "{}") + setDefault("topics", "content", "text", "") + setDefault("topics", "parsed_content", "text", "") + setDefault("replies", "content", "text", "") + setDefault("replies", "parsed_content", "text", "") + //setDefault("revisions", "content", "text", "") + setDefault("users_replies", "content", "text", "") + setDefault("users_replies", "parsed_content", "text", "") + setDefault("pages", "body", "text", "") + setDefault("pages", "allowedGroups", "text", "") + setDefault("moderation_logs", "extra", "text", "") + setDefault("administration_logs", "extra", "text", "") + if err != nil { + return err + } + + return nil +} diff --git a/query_gen/querygen.go b/query_gen/querygen.go index 99eaa049..8b06ee53 100644 --- a/query_gen/querygen.go +++ b/query_gen/querygen.go @@ -126,7 +126,7 @@ type DBStmt struct { Type string // create-table, insert, update, delete } -// TODO: Add the DropTable, TableExists, AddColumn, ColumnExists, and RemoveColumn methods +// TODO: Add the TableExists and ColumnExists methods type Adapter interface { GetName() string BuildConn(config map[string]string) (*sql.DB, error) diff --git a/routes/account.go b/routes/account.go index bd1571eb..54ca9cf3 100644 --- a/routes/account.go +++ b/routes/account.go @@ -230,7 +230,7 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user *c.User) if r.PostFormValue("tos") != "0" { regError(p.GetErrorPhrase("register_might_be_machine"), "trap-question") } - + { h := sha256.New() h.Write([]byte(c.JSTokenBox.Load().(string))) @@ -614,8 +614,8 @@ func AccountEditMFADisableSubmit(w http.ResponseWriter, r *http.Request, u *c.Us func AccountEditPrivacy(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header) c.RouteError { accountEditHead("account_privacy", w, r, u, h) - profileComments := false - receiveConvos := false + profileComments := u.Privacy.ShowComments + receiveConvos := u.Privacy.AllowMessage enableEmbeds := !c.DefaultParseSettings.NoEmbed if u.ParseSettings != nil { enableEmbeds = !u.ParseSettings.NoEmbed @@ -626,14 +626,21 @@ func AccountEditPrivacy(w http.ResponseWriter, r *http.Request, u *c.User, h *c. func AccountEditPrivacySubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { //headerLite, _ := c.SimpleUserCheck(w, r, u) - + sProfileComments := r.FormValue("profile_comments") sEnableEmbeds := r.FormValue("enable_embeds") - enableEmbeds, e := strconv.Atoi(sEnableEmbeds) - if e != nil { - return c.LocalError("enable_embeds must be 0 or 1", w, r, u) - } - if sEnableEmbeds != r.FormValue("o_enable_embeds") { - if e = u.UpdatePrivacy(enableEmbeds); e != nil { + oProfileComments := r.FormValue("o_profile_comments") + oEnableEmbeds := r.FormValue("o_enable_embeds") + + if sProfileComments != oProfileComments || sEnableEmbeds != oEnableEmbeds { + profileComments, e := strconv.Atoi(sProfileComments) + enableEmbeds, e2 := strconv.Atoi(sEnableEmbeds) + if e != nil || e2 != nil { + return c.LocalError("malformed integer", w, r, u) + } + e = u.UpdatePrivacy(profileComments, enableEmbeds) + if e == c.ErrProfileCommentsOutOfBounds || e == c.ErrEnableEmbedsOutOfBounds { + return c.LocalError(e.Error(), w, r, u) + } else if e != nil { return c.InternalError(e, w, r) } } diff --git a/routes/convos.go b/routes/convos.go index 0c580a07..cd8b74c1 100644 --- a/routes/convos.go +++ b/routes/convos.go @@ -399,24 +399,18 @@ func ConvosEditReplySubmit(w http.ResponseWriter, r *http.Request, user *c.User, if err != nil { return c.InternalError(err, w, r) } - - if !js { - http.Redirect(w, r, "/user/convo/"+strconv.Itoa(post.CID), http.StatusSeeOther) - } else { - w.Write(successJSONBytes) - } - return nil + return actionSuccess(w, r, "/user/convo/"+strconv.Itoa(post.CID), js) } -func RelationsBlockCreate(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header, spid string) c.RouteError { +func RelationsBlockCreate(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, spid string) c.RouteError { h.Title = p.GetTitlePhrase("create_block") pid, err := strconv.Atoi(spid) if err != nil { - return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u) } puser, err := c.Users.Get(pid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to block doesn't exist.", w, r, user) + return c.LocalError("The user you're trying to block doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -425,22 +419,22 @@ func RelationsBlockCreate(w http.ResponseWriter, r *http.Request, user *c.User, return renderTemplate("are_you_sure", w, r, h, pi) } -func RelationsBlockCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User, spid string) c.RouteError { +func RelationsBlockCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User, spid string) c.RouteError { pid, err := strconv.Atoi(spid) if err != nil { - return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u) } puser, err := c.Users.Get(pid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to block doesn't exist.", w, r, user) + return c.LocalError("The user you're trying to block doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - if user.ID == puser.ID { - return c.LocalError("You can't block yourself.", w, r, user) + if u.ID == puser.ID { + return c.LocalError("You can't block yourself.", w, r, u) } - err = c.UserBlocks.Add(user.ID, puser.ID) + err = c.UserBlocks.Add(u.ID, puser.ID) if err != nil { return c.InternalError(err, w, r) } @@ -449,15 +443,15 @@ func RelationsBlockCreateSubmit(w http.ResponseWriter, r *http.Request, user *c. return nil } -func RelationsBlockRemove(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Header, spid string) c.RouteError { +func RelationsBlockRemove(w http.ResponseWriter, r *http.Request, u *c.User, h *c.Header, spid string) c.RouteError { h.Title = p.GetTitlePhrase("remove_block") pid, err := strconv.Atoi(spid) if err != nil { - return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u) } puser, err := c.Users.Get(pid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to block doesn't exist.", w, r, user) + return c.LocalError("The user you're trying to block doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -466,19 +460,19 @@ func RelationsBlockRemove(w http.ResponseWriter, r *http.Request, user *c.User, return renderTemplate("are_you_sure", w, r, h, pi) } -func RelationsBlockRemoveSubmit(w http.ResponseWriter, r *http.Request, user *c.User, spid string) c.RouteError { +func RelationsBlockRemoveSubmit(w http.ResponseWriter, r *http.Request, u *c.User, spid string) c.RouteError { pid, err := strconv.Atoi(spid) if err != nil { - return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("id_must_be_integer"), w, r, u) } puser, err := c.Users.Get(pid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to unblock doesn't exist.", w, r, user) + return c.LocalError("The user you're trying to unblock doesn't exist.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - err = c.UserBlocks.Remove(user.ID, puser.ID) + err = c.UserBlocks.Remove(u.ID, puser.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/pages.go b/routes/panel/pages.go index 63957852..716d4254 100644 --- a/routes/panel/pages.go +++ b/routes/panel/pages.go @@ -35,23 +35,23 @@ func Pages(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_page_list", "", "panel_pages", &pi}) } -func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } name := c.SanitiseSingleLine(r.PostFormValue("name")) if name == "" { - return c.LocalError("No name was provided for this page", w, r, user) + return c.LocalError("No name was provided for this page", w, r, u) } title := c.SanitiseSingleLine(r.PostFormValue("title")) if title == "" { - return c.LocalError("No title was provided for this page", w, r, user) + return c.LocalError("No title was provided for this page", w, r, u) } body := r.PostFormValue("body") if body == "" { - return c.LocalError("No body was provided for this page", w, r, user) + return c.LocalError("No body was provided for this page", w, r, u) } page := c.BlankCustomPage() @@ -62,7 +62,7 @@ func PagesCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.R if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("create", pid, "page", user.GetIP(), user.ID) + err = c.AdminLogs.Create("create", pid, "page", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -95,27 +95,27 @@ func PagesEdit(w http.ResponseWriter, r *http.Request, u *c.User, spid string) c return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_page_edit", "", "panel_pages_edit", &pi}) } -func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, spid string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func PagesEditSubmit(w http.ResponseWriter, r *http.Request, u *c.User, spid string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } pid, err := strconv.Atoi(spid) if err != nil { - return c.LocalError("Page ID needs to be an integer", w, r, user) + return c.LocalError("Page ID needs to be an integer", w, r, u) } name := c.SanitiseSingleLine(r.PostFormValue("name")) if name == "" { - return c.LocalError("No name was provided for this page", w, r, user) + return c.LocalError("No name was provided for this page", w, r, u) } title := c.SanitiseSingleLine(r.PostFormValue("title")) if title == "" { - return c.LocalError("No title was provided for this page", w, r, user) + return c.LocalError("No title was provided for this page", w, r, u) } body := r.PostFormValue("body") if body == "" { - return c.LocalError("No body was provided for this page", w, r, user) + return c.LocalError("No body was provided for this page", w, r, u) } page, err := c.Pages.Get(pid) @@ -129,7 +129,7 @@ func PagesEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, spid if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("edit", pid, "page", user.GetIP(), user.ID) + err = c.AdminLogs.Create("edit", pid, "page", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/plugins.go b/routes/panel/plugins.go index bc23286a..09f27125 100644 --- a/routes/panel/plugins.go +++ b/routes/panel/plugins.go @@ -26,54 +26,54 @@ func Plugins(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { } // TODO: Abstract more of the plugin activation / installation / deactivation logic, so we can test all that more reliably and easily -func PluginsActivate(w http.ResponseWriter, r *http.Request, user *c.User, uname string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func PluginsActivate(w http.ResponseWriter, r *http.Request, u *c.User, uname string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManagePlugins { - return c.NoPermissions(w, r, user) + if !u.Perms.ManagePlugins { + return c.NoPermissions(w, r, u) } - plugin, ok := c.Plugins[uname] + pl, ok := c.Plugins[uname] if !ok { - return c.LocalError("The plugin isn't registered in the system", w, r, user) + return c.LocalError("The plugin isn't registered in the system", w, r, u) } - if plugin.Installable && !plugin.Installed { - return c.LocalError("You can't activate this plugin without installing it first", w, r, user) + if pl.Installable && !pl.Installed { + return c.LocalError("You can't activate this plugin without installing it first", w, r, u) } - active, err := plugin.BypassActive() - hasPlugin, err2 := plugin.InDatabase() + active, err := pl.BypassActive() + hasPlugin, err2 := pl.InDatabase() if err != nil || err2 != nil { return c.InternalError(err, w, r) } - if plugin.Activate != nil { - err = plugin.Activate(plugin) + if pl.Activate != nil { + err = pl.Activate(pl) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } } if hasPlugin { if active { - return c.LocalError("The plugin is already active", w, r, user) + return c.LocalError("The plugin is already active", w, r, u) } - err = plugin.SetActive(true) + err = pl.SetActive(true) } else { - err = plugin.AddToDatabase(true, false) + err = pl.AddToDatabase(true, false) } if err != nil { return c.InternalError(err, w, r) } - log.Printf("Activating plugin '%s'", plugin.Name) - err = plugin.Init(plugin) + log.Printf("Activating plugin '%s'", pl.Name) + err = pl.Init(pl) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } - err = c.AdminLogs.CreateExtra("activate", 0, "plugin", user.GetIP(), user.ID, c.SanitiseSingleLine(plugin.Name)) + err = c.AdminLogs.CreateExtra("activate", 0, "plugin", u.GetIP(), u.ID, c.SanitiseSingleLine(pl.Name)) if err != nil { return c.InternalError(err, w, r) } @@ -82,36 +82,36 @@ func PluginsActivate(w http.ResponseWriter, r *http.Request, user *c.User, uname return nil } -func PluginsDeactivate(w http.ResponseWriter, r *http.Request, user *c.User, uname string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func PluginsDeactivate(w http.ResponseWriter, r *http.Request, u *c.User, uname string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManagePlugins { - return c.NoPermissions(w, r, user) + if !u.Perms.ManagePlugins { + return c.NoPermissions(w, r, u) } - plugin, ok := c.Plugins[uname] + pl, ok := c.Plugins[uname] if !ok { - return c.LocalError("The plugin isn't registered in the system", w, r, user) + return c.LocalError("The plugin isn't registered in the system", w, r, u) } - log.Printf("plugin: %+v\n", plugin) + log.Printf("plugin: %+v\n", pl) - active, err := plugin.BypassActive() + active, err := pl.BypassActive() if err != nil { return c.InternalError(err, w, r) } else if !active { - return c.LocalError("The plugin you're trying to deactivate isn't active", w, r, user) + return c.LocalError("The plugin you're trying to deactivate isn't active", w, r, u) } - err = plugin.SetActive(false) + err = pl.SetActive(false) if err != nil { return c.InternalError(err, w, r) } - if plugin.Deactivate != nil { - plugin.Deactivate(plugin) + if pl.Deactivate != nil { + pl.Deactivate(pl) } - err = c.AdminLogs.CreateExtra("deactivate", 0, "plugin", user.GetIP(), user.ID, c.SanitiseSingleLine(plugin.Name)) + err = c.AdminLogs.CreateExtra("deactivate", 0, "plugin", u.GetIP(), u.ID, c.SanitiseSingleLine(pl.Name)) if err != nil { return c.InternalError(err, w, r) } @@ -120,28 +120,28 @@ func PluginsDeactivate(w http.ResponseWriter, r *http.Request, user *c.User, una return nil } -func PluginsInstall(w http.ResponseWriter, r *http.Request, user *c.User, uname string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func PluginsInstall(w http.ResponseWriter, r *http.Request, u *c.User, uname string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManagePlugins { - return c.NoPermissions(w, r, user) + if !u.Perms.ManagePlugins { + return c.NoPermissions(w, r, u) } - plugin, ok := c.Plugins[uname] + pl, ok := c.Plugins[uname] if !ok { - return c.LocalError("The plugin isn't registered in the system", w, r, user) + return c.LocalError("The plugin isn't registered in the system", w, r, u) } - if !plugin.Installable { - return c.LocalError("This plugin is not installable", w, r, user) + if !pl.Installable { + return c.LocalError("This plugin is not installable", w, r, u) } - if plugin.Installed { - return c.LocalError("This plugin has already been installed", w, r, user) + if pl.Installed { + return c.LocalError("This plugin has already been installed", w, r, u) } - active, err := plugin.BypassActive() - hasPlugin, err2 := plugin.InDatabase() + active, err := pl.BypassActive() + hasPlugin, err2 := pl.InDatabase() if err != nil || err2 != nil { return c.InternalError(err, w, r) } @@ -149,39 +149,39 @@ func PluginsInstall(w http.ResponseWriter, r *http.Request, user *c.User, uname return c.InternalError(errors.New("An uninstalled plugin is still active"), w, r) } - if plugin.Install != nil { - err = plugin.Install(plugin) + if pl.Install != nil { + err = pl.Install(pl) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } } - if plugin.Activate != nil { - err = plugin.Activate(plugin) + if pl.Activate != nil { + err = pl.Activate(pl) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } } if hasPlugin { - err = plugin.SetInstalled(true) + err = pl.SetInstalled(true) if err != nil { return c.InternalError(err, w, r) } - err = plugin.SetActive(true) + err = pl.SetActive(true) } else { - err = plugin.AddToDatabase(true, true) + err = pl.AddToDatabase(true, true) } if err != nil { return c.InternalError(err, w, r) } - log.Printf("Installing plugin '%s'", plugin.Name) - err = plugin.Init(plugin) + log.Printf("Installing plugin '%s'", pl.Name) + err = pl.Init(pl) if err != nil { - return c.LocalError(err.Error(), w, r, user) + return c.LocalError(err.Error(), w, r, u) } - err = c.AdminLogs.CreateExtra("install", 0, "plugin", user.GetIP(), user.ID, c.SanitiseSingleLine(plugin.Name)) + err = c.AdminLogs.CreateExtra("install", 0, "plugin", u.GetIP(), u.ID, c.SanitiseSingleLine(pl.Name)) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/panel/themes.go b/routes/panel/themes.go index 7f69eeca..c2f5ab3a 100644 --- a/routes/panel/themes.go +++ b/routes/panel/themes.go @@ -12,13 +12,13 @@ import ( p "github.com/Azareal/Gosora/common/phrases" ) -func Themes(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "themes", "themes") +func Themes(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "themes", "themes") if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageThemes { + return c.NoPermissions(w, r, u) } var pThemeList, vThemeList []*c.Theme @@ -37,28 +37,28 @@ func Themes(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_themes", "", "panel_themes", &pi}) } -func ThemesSetDefault(w http.ResponseWriter, r *http.Request, user *c.User, uname string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ThemesSetDefault(w http.ResponseWriter, r *http.Request, u *c.User, uname string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageThemes { + return c.NoPermissions(w, r, u) } theme, ok := c.Themes[uname] if !ok { - return c.LocalError("The theme isn't registered in the system", w, r, user) + return c.LocalError("The theme isn't registered in the system", w, r, u) } if theme.Disabled { - return c.LocalError("You must not enable this theme", w, r, user) + return c.LocalError("You must not enable this theme", w, r, u) } err := c.UpdateDefaultTheme(theme) if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.CreateExtra("set_default", 0, "theme", user.GetIP(), user.ID, c.SanitiseSingleLine(theme.Name)) + err = c.AdminLogs.CreateExtra("set_default", 0, "theme", u.GetIP(), u.ID, c.SanitiseSingleLine(theme.Name)) if err != nil { return c.InternalError(err, w, r) } @@ -67,13 +67,13 @@ func ThemesSetDefault(w http.ResponseWriter, r *http.Request, user *c.User, unam return nil } -func ThemesMenus(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "themes_menus", "themes") +func ThemesMenus(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "themes_menus", "themes") if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageThemes { + return c.NoPermissions(w, r, u) } var menuList []c.PanelMenuListItem @@ -92,21 +92,21 @@ func ThemesMenus(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteEr return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_themes_menus", &c.PanelMenuListPage{basePage, menuList}}) } -func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, user *c.User, smid string) c.RouteError { +func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, u *c.User, smid string) c.RouteError { // TODO: Something like Menu #1 for the title? - basePage, ferr := buildBasePage(w, r, user, "themes_menus_edit", "themes") + basePage, ferr := buildBasePage(w, r, u, "themes_menus_edit", "themes") if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageThemes { + return c.NoPermissions(w, r, u) } basePage.Header.AddScript("Sortable-1.4.0/Sortable.min.js") basePage.Header.AddScriptAsync("panel_menu_items.js") mid, err := strconv.Atoi(smid) if err != nil { - return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, u) } menuHold, err := c.Menus.Get(mid) if err == sql.ErrNoRows { @@ -138,19 +138,19 @@ func ThemesMenusEdit(w http.ResponseWriter, r *http.Request, user *c.User, smid return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "", "", "panel_themes_menus_items", &c.PanelMenuPage{basePage, mid, menuList}}) } -func ThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, user *c.User, sitemID string) c.RouteError { +func ThemesMenuItemEdit(w http.ResponseWriter, r *http.Request, u *c.User, sitemID string) c.RouteError { // TODO: Something like Menu #1 for the title? - basePage, ferr := buildBasePage(w, r, user, "themes_menus_edit", "themes") + basePage, ferr := buildBasePage(w, r, u, "themes_menus_edit", "themes") if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageThemes { + return c.NoPermissions(w, r, u) } itemID, err := strconv.Atoi(sitemID) if err != nil { - return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, user) + return c.LocalError(p.GetErrorPhrase("url_id_must_be_integer"), w, r, u) } menuItem, err := c.Menus.ItemStore().Get(itemID) if err == sql.ErrNoRows { @@ -205,23 +205,23 @@ func themesMenuItemSetters(r *http.Request, i c.MenuItem) c.MenuItem { return i } -func ThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sitemID string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sitemID string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } itemID, err := strconv.Atoi(sitemID) if err != nil { - return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) + return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, u, js) } menuItem, err := c.Menus.ItemStore().Get(itemID) if err == sql.ErrNoRows { - return c.LocalErrorJSQ("This item doesn't exist.", w, r, user, js) + return c.LocalErrorJSQ("This item doesn't exist.", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -232,7 +232,7 @@ func ThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, user *c.Us if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", menuItem.ID, "menu_item", user.GetIP(), user.ID) + err = c.AdminLogs.Create("edit", menuItem.ID, "menu_item", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -240,23 +240,23 @@ func ThemesMenuItemEditSubmit(w http.ResponseWriter, r *http.Request, user *c.Us return successRedirect("/panel/themes/menus/item/edit/"+strconv.Itoa(itemID), w, r, js) } -func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } smenuID := r.PostFormValue("mid") if smenuID == "" { - return c.LocalErrorJSQ("No menuID provided", w, r, user, js) + return c.LocalErrorJSQ("No menuID provided", w, r, u, js) } menuID, err := strconv.Atoi(smenuID) if err != nil { - return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) + return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, u, js) } menuItem := c.MenuItem{MenuID: menuID} @@ -265,7 +265,7 @@ func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, user *c. if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("create", itemID, "menu_item", user.GetIP(), user.ID) + err = c.AdminLogs.Create("create", itemID, "menu_item", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -273,23 +273,23 @@ func ThemesMenuItemCreateSubmit(w http.ResponseWriter, r *http.Request, user *c. return successRedirect("/panel/themes/menus/item/edit/"+strconv.Itoa(itemID), w, r, js) } -func ThemesMenuItemDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.User, sitemID string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ThemesMenuItemDeleteSubmit(w http.ResponseWriter, r *http.Request, u *c.User, sitemID string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } itemID, err := strconv.Atoi(sitemID) if err != nil { - return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) + return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, u, js) } menuItem, err := c.Menus.ItemStore().Get(itemID) if err == sql.ErrNoRows { - return c.LocalErrorJSQ("This item doesn't exist.", w, r, user, js) + return c.LocalErrorJSQ("This item doesn't exist.", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -299,7 +299,7 @@ func ThemesMenuItemDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c. if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("delete", menuItem.ID, "menu_item", user.GetIP(), user.ID) + err = c.AdminLogs.Create("delete", menuItem.ID, "menu_item", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -307,23 +307,23 @@ func ThemesMenuItemDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c. return successRedirect("/panel/themes/menus/", w, r, js) } -func ThemesMenuItemOrderSubmit(w http.ResponseWriter, r *http.Request, user *c.User, smid string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ThemesMenuItemOrderSubmit(w http.ResponseWriter, r *http.Request, u *c.User, smid string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } mid, err := strconv.Atoi(smid) if err != nil { - return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) + return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, u, js) } menuHold, err := c.Menus.Get(mid) if err == sql.ErrNoRows { - return c.LocalErrorJSQ("Can't find menu", w, r, user, js) + return c.LocalErrorJSQ("Can't find menu", w, r, u, js) } else if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -335,13 +335,13 @@ func ThemesMenuItemOrderSubmit(w http.ResponseWriter, r *http.Request, user *c.U for index, smiid := range strings.Split(sitems, ",") { miid, err := strconv.Atoi(smiid) if err != nil { - return c.LocalErrorJSQ("Invalid integer in menu item list", w, r, user, js) + return c.LocalErrorJSQ("Invalid integer in menu item list", w, r, u, js) } updateMap[miid] = index } menuHold.UpdateOrder(updateMap) - err = c.AdminLogs.Create("suborder", menuHold.MenuID, "menu", user.GetIP(), user.ID) + err = c.AdminLogs.Create("suborder", menuHold.MenuID, "menu", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -349,13 +349,13 @@ func ThemesMenuItemOrderSubmit(w http.ResponseWriter, r *http.Request, user *c.U return successRedirect("/panel/themes/menus/edit/"+strconv.Itoa(mid), w, r, js) } -func ThemesWidgets(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { - basePage, ferr := buildBasePage(w, r, user, "themes_widgets", "themes") +func ThemesWidgets(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { + basePage, ferr := buildBasePage(w, r, u, "themes_widgets", "themes") if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissions(w, r, user) + if !u.Perms.ManageThemes { + return c.NoPermissions(w, r, u) } basePage.Header.AddScript("widgets.js") @@ -414,20 +414,20 @@ func widgetsParseInputs(r *http.Request, widget *c.Widget) (*c.WidgetEdit, error } // ThemesWidgetsEditSubmit is an action which is triggered when someone sends an update request for a widget -func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, swid string) c.RouteError { +func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, u *c.User, swid string) c.RouteError { //fmt.Println("in ThemesWidgetsEditSubmit") - _, ferr := c.SimplePanelUserCheck(w, r, user) + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } wid, err := strconv.Atoi(swid) if err != nil { - return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) + return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, u, js) } widget, err := c.Widgets.Get(wid) if err == sql.ErrNoRows { @@ -438,14 +438,14 @@ func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user *c.Use ewidget, err := widgetsParseInputs(r, widget.Copy()) if err != nil { - return c.LocalErrorJSQ(err.Error(), w, r, user, js) + return c.LocalErrorJSQ(err.Error(), w, r, u, js) } err = ewidget.Commit() if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("edit", widget.ID, "widget", user.GetIP(), user.ID) + err = c.AdminLogs.Create("edit", widget.ID, "widget", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -454,25 +454,25 @@ func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user *c.Use } // ThemesWidgetsCreateSubmit is an action which is triggered when someone sends a create request for a widget -func ThemesWidgetsCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.User) c.RouteError { +func ThemesWidgetsCreateSubmit(w http.ResponseWriter, r *http.Request, u *c.User) c.RouteError { js := r.PostFormValue("js") == "1" - _, ferr := c.SimplePanelUserCheck(w, r, user) + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } ewidget, err := widgetsParseInputs(r, &c.Widget{}) if err != nil { - return c.LocalErrorJSQ(err.Error(), w, r, user, js) + return c.LocalErrorJSQ(err.Error(), w, r, u, js) } wid, err := ewidget.Create() if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - err = c.AdminLogs.Create("create", wid, "widget", user.GetIP(), user.ID) + err = c.AdminLogs.Create("create", wid, "widget", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -480,19 +480,19 @@ func ThemesWidgetsCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.U return successRedirect("/panel/themes/widgets/", w, r, js) } -func ThemesWidgetsDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.User, swid string) c.RouteError { - _, ferr := c.SimplePanelUserCheck(w, r, user) +func ThemesWidgetsDeleteSubmit(w http.ResponseWriter, r *http.Request, u *c.User, swid string) c.RouteError { + _, ferr := c.SimplePanelUserCheck(w, r, u) if ferr != nil { return ferr } js := r.PostFormValue("js") == "1" - if !user.Perms.ManageThemes { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.ManageThemes { + return c.NoPermissionsJSQ(w, r, u, js) } wid, err := strconv.Atoi(swid) if err != nil { - return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, user, js) + return c.LocalErrorJSQ(p.GetErrorPhrase("id_must_be_integer"), w, r, u, js) } widget, err := c.Widgets.Get(wid) if err == sql.ErrNoRows { @@ -504,7 +504,7 @@ func ThemesWidgetsDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.U if err != nil { return c.InternalError(err, w, r) } - err = c.AdminLogs.Create("delete", widget.ID, "widget", user.GetIP(), user.ID) + err = c.AdminLogs.Create("delete", widget.ID, "widget", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } diff --git a/routes/profile.go b/routes/profile.go index 5d532a69..3cd8ce84 100644 --- a/routes/profile.go +++ b/routes/profile.go @@ -110,9 +110,49 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user *c.User, h *c.Head return c.InternalError(err, w, r) } } + canMessage := (!blockedInv && user.Perms.UseConvos) || (!blockedInv && puser.IsSuperMod && user.Perms.UseConvosOnlyWithMod) || user.IsSuperMod canComment := !blockedInv && user.Perms.CreateProfileReply + showComments := profileCommentsShow(puser, user) + if !showComments { + canComment = false + } + if !profileAllowMessage(puser, user) { + canMessage = false + } - ppage := c.ProfilePage{h, reList, *puser, currentScore, nextScore, blocked, canMessage, canComment} + ppage := c.ProfilePage{h, reList, *puser, currentScore, nextScore, blocked, canMessage, canComment, showComments} return renderTemplate("profile", w, r, h, ppage) } + +func profileAllowMessage(pu, u *c.User) (canMsg bool) { + switch pu.Privacy.AllowMessage { + case 4: // Unused + canMsg = false + case 3: // mods / self + canMsg = u.IsSuperMod + //case 2: // friends + case 1: // registered + canMsg = true + default: // 0 + canMsg = true + } + return canMsg +} + +func profileCommentsShow(pu, u *c.User) (showComments bool) { + switch pu.Privacy.ShowComments { + case 5: // Unused + showComments = false + case 4: // Self + showComments = u.ID == pu.ID + //case 3: // friends + case 2: // registered + showComments = u.Loggedin + case 1: // public + showComments = true + default: // 0 + showComments = true + } + return showComments +} diff --git a/routes/profile_reply.go b/routes/profile_reply.go index 50628a3e..031af0c0 100644 --- a/routes/profile_reply.go +++ b/routes/profile_reply.go @@ -17,7 +17,6 @@ func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.Us if err != nil { return c.LocalError("Invalid UID", w, r, user) } - profileOwner, err := c.Users.Get(uid) if err == sql.ErrNoRows { return c.LocalError("The profile you're trying to post on doesn't exist.", w, r, user) @@ -30,7 +29,7 @@ func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.Us return c.InternalError(err, w, r) } // Supermods can bypass blocks so they can tell people off when they do something stupid or have to convey important information - if blocked && !user.IsSuperMod { + if (blocked || !profileCommentsShow(profileOwner, user)) && !user.IsSuperMod { return c.LocalError("You don't have permission to send messages to one of these users.", w, r, user) } @@ -56,13 +55,12 @@ func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user *c.Us return nil } -func ProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User, srid string) c.RouteError { +func ProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, u *c.User, srid string) c.RouteError { js := r.PostFormValue("js") == "1" rid, err := strconv.Atoi(srid) if err != nil { - return c.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, js) + return c.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, u, js) } - reply, err := c.Prstore.Get(rid) if err == sql.ErrNoRows { return c.PreErrorJSQ("The target reply doesn't exist.", w, r, js) @@ -74,34 +72,41 @@ func ProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user *c.User if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - if !user.Perms.CreateProfileReply { - return c.NoPermissionsJSQ(w, r, user, js) + if !u.Perms.CreateProfileReply { + return c.NoPermissionsJSQ(w, r, u, js) } // ? Does the admin understand that this group perm affects this? - if user.ID != creator.ID && !user.Perms.EditReply { - return c.NoPermissionsJSQ(w, r, user, js) + if u.ID != creator.ID && !u.Perms.EditReply { + return c.NoPermissionsJSQ(w, r, u, js) } - // TODO: Stop blocked users from modifying profile replies? + profileOwner, err := c.Users.Get(reply.ParentID) + if err == sql.ErrNoRows { + return c.LocalError("The profile you're trying to edit a post on doesn't exist.", w, r, u) + } else if err != nil { + return c.InternalError(err, w, r) + } + blocked, err := c.UserBlocks.IsBlockedBy(profileOwner.ID, u.ID) + if err != nil { + return c.InternalError(err, w, r) + } + // Supermods can bypass blocks so they can tell people off when they do something stupid or have to convey important information + if (blocked || !profileCommentsShow(profileOwner, u)) && !u.IsSuperMod { + return c.NoPermissionsJSQ(w, r, u, js) + } err = reply.SetBody(r.PostFormValue("edit_item")) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - - if !js { - http.Redirect(w, r, "/user/"+strconv.Itoa(creator.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther) - } else { - w.Write(successJSONBytes) - } - return nil + return actionSuccess(w, r, "/user/"+strconv.Itoa(creator.ID)+"#reply-"+strconv.Itoa(rid), js) } -func ProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.User, srid string) c.RouteError { +func ProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, u *c.User, srid string) c.RouteError { js := r.PostFormValue("js") == "1" rid, err := strconv.Atoi(srid) if err != nil { - return c.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, js) + return c.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, u, js) } reply, err := c.Prstore.Get(rid) @@ -115,15 +120,15 @@ func ProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.Us if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - if user.ID != creator.ID && !user.Perms.DeleteReply { - return c.NoPermissionsJSQ(w, r, user, js) + if u.ID != creator.ID && !u.Perms.DeleteReply { + return c.NoPermissionsJSQ(w, r, u, js) } err = reply.Delete() if err != nil { return c.InternalErrorJSQ(err, w, r, js) } - //log.Printf("The profile post '%d' was deleted by c.User #%d", reply.ID, user.ID) + //log.Printf("The profile post '%d' was deleted by c.User #%d", reply.ID, u.ID) if !js { //http.Redirect(w,r, "/user/" + strconv.Itoa(creator.ID), http.StatusSeeOther) @@ -131,7 +136,7 @@ func ProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user *c.Us w.Write(successJSONBytes) } - err = c.ModLogs.Create("delete", reply.ParentID, "profile-reply", user.GetIP(), user.ID) + err = c.ModLogs.Create("delete", reply.ParentID, "profile-reply", u.GetIP(), u.ID) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } diff --git a/routes/user.go b/routes/user.go index 006eb027..31c1bd52 100644 --- a/routes/user.go +++ b/routes/user.go @@ -9,46 +9,46 @@ import ( c "github.com/Azareal/Gosora/common" ) -func BanUserSubmit(w http.ResponseWriter, r *http.Request, user *c.User, suid string) c.RouteError { - if !user.Perms.BanUsers { - return c.NoPermissions(w, r, user) +func BanUserSubmit(w http.ResponseWriter, r *http.Request, u *c.User, suid string) c.RouteError { + if !u.Perms.BanUsers { + return c.NoPermissions(w, r, u) } uid, err := strconv.Atoi(suid) if err != nil { - return c.LocalError("The provided UserID is not a valid number.", w, r, user) + return c.LocalError("The provided UserID is not a valid number.", w, r, u) } if uid == -2 { - return c.LocalError("Why don't you like Merlin?", w, r, user) + return c.LocalError("Why don't you like Merlin?", w, r, u) } targetUser, err := c.Users.Get(uid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to ban no longer exists.", w, r, user) + return c.LocalError("The user you're trying to ban no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } // TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one? if targetUser.IsMod { - return c.LocalError("You may not ban another staff member.", w, r, user) + return c.LocalError("You may not ban another staff member.", w, r, u) } - if uid == user.ID { - return c.LocalError("Why are you trying to ban yourself? Stop that.", w, r, user) + if uid == u.ID { + return c.LocalError("Why are you trying to ban yourself? Stop that.", w, r, u) } if targetUser.IsBanned { - return c.LocalError("The user you're trying to unban is already banned.", w, r, user) + return c.LocalError("The user you're trying to unban is already banned.", w, r, u) } durDays, err := strconv.Atoi(r.FormValue("dur-days")) if err != nil { - return c.LocalError("You can only use whole numbers for the number of days", w, r, user) + return c.LocalError("You can only use whole numbers for the number of days", w, r, u) } durWeeks, err := strconv.Atoi(r.FormValue("dur-weeks")) if err != nil { - return c.LocalError("You can only use whole numbers for the number of weeks", w, r, user) + return c.LocalError("You can only use whole numbers for the number of weeks", w, r, u) } durMonths, err := strconv.Atoi(r.FormValue("dur-months")) if err != nil { - return c.LocalError("You can only use whole numbers for the number of months", w, r, user) + return c.LocalError("You can only use whole numbers for the number of months", w, r, u) } deletePosts := false switch r.FormValue("delete-posts") { @@ -67,13 +67,13 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user *c.User, suid st dur, _ = time.ParseDuration(strconv.Itoa(secs) + "s") } - err = targetUser.Ban(dur, user.ID) + err = targetUser.Ban(dur, u.ID) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to ban no longer exists.", w, r, user) + return c.LocalError("The user you're trying to ban no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - err = c.ModLogs.Create("ban", uid, "user", user.GetIP(), user.ID) + err = c.ModLogs.Create("ban", uid, "user", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -81,11 +81,11 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user *c.User, suid st if deletePosts { err = targetUser.DeletePosts() if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to ban no longer exists.", w, r, user) + return c.LocalError("The user you're trying to ban no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - err = c.ModLogs.Create("delete-posts", uid, "user", user.GetIP(), user.ID) + err = c.ModLogs.Create("delete-posts", uid, "user", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } @@ -93,7 +93,7 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user *c.User, suid st // TODO: Trickle the hookTable down from the router hTbl := c.GetHookTable() - skip, rerr := hTbl.VhookSkippable("action_end_ban_user", targetUser.ID, user) + skip, rerr := hTbl.VhookSkippable("action_end_ban_user", targetUser.ID, u) if skip || rerr != nil { return rerr } @@ -102,42 +102,42 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user *c.User, suid st return nil } -func UnbanUser(w http.ResponseWriter, r *http.Request, user *c.User, suid string) c.RouteError { - if !user.Perms.BanUsers { - return c.NoPermissions(w, r, user) +func UnbanUser(w http.ResponseWriter, r *http.Request, u *c.User, suid string) c.RouteError { + if !u.Perms.BanUsers { + return c.NoPermissions(w, r, u) } uid, err := strconv.Atoi(suid) if err != nil { - return c.LocalError("The provided UserID is not a valid number.", w, r, user) + return c.LocalError("The provided UserID is not a valid number.", w, r, u) } targetUser, err := c.Users.Get(uid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to unban no longer exists.", w, r, user) + return c.LocalError("The user you're trying to unban no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } if !targetUser.IsBanned { - return c.LocalError("The user you're trying to unban isn't banned.", w, r, user) + return c.LocalError("The user you're trying to unban isn't banned.", w, r, u) } err = targetUser.Unban() if err == c.ErrNoTempGroup { - return c.LocalError("The user you're trying to unban is not banned", w, r, user) + return c.LocalError("The user you're trying to unban is not banned", w, r, u) } else if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to unban no longer exists.", w, r, user) + return c.LocalError("The user you're trying to unban no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - err = c.ModLogs.Create("unban", uid, "user", user.GetIP(), user.ID) + err = c.ModLogs.Create("unban", uid, "user", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } // TODO: Trickle the hookTable down from the router hTbl := c.GetHookTable() - skip, rerr := hTbl.VhookSkippable("action_end_unban_user", targetUser.ID, user) + skip, rerr := hTbl.VhookSkippable("action_end_unban_user", targetUser.ID, u) if skip || rerr != nil { return rerr } @@ -146,23 +146,23 @@ func UnbanUser(w http.ResponseWriter, r *http.Request, user *c.User, suid string return nil } -func ActivateUser(w http.ResponseWriter, r *http.Request, user *c.User, suid string) c.RouteError { - if !user.Perms.ActivateUsers { - return c.NoPermissions(w, r, user) +func ActivateUser(w http.ResponseWriter, r *http.Request, u *c.User, suid string) c.RouteError { + if !u.Perms.ActivateUsers { + return c.NoPermissions(w, r, u) } uid, err := strconv.Atoi(suid) if err != nil { - return c.LocalError("The provided UserID is not a valid number.", w, r, user) + return c.LocalError("The provided UserID is not a valid number.", w, r, u) } targetUser, err := c.Users.Get(uid) if err == sql.ErrNoRows { - return c.LocalError("The account you're trying to activate no longer exists.", w, r, user) + return c.LocalError("The account you're trying to activate no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } if targetUser.Active { - return c.LocalError("The account you're trying to activate has already been activated.", w, r, user) + return c.LocalError("The account you're trying to activate has already been activated.", w, r, u) } err = targetUser.Activate() if err != nil { @@ -171,7 +171,7 @@ func ActivateUser(w http.ResponseWriter, r *http.Request, user *c.User, suid str targetUser, err = c.Users.Get(uid) if err == sql.ErrNoRows { - return c.LocalError("The account you're trying to activate no longer exists.", w, r, user) + return c.LocalError("The account you're trying to activate no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } @@ -181,14 +181,14 @@ func ActivateUser(w http.ResponseWriter, r *http.Request, user *c.User, suid str } targetUser.CacheRemove() - err = c.ModLogs.Create("activate", targetUser.ID, "user", user.GetIP(), user.ID) + err = c.ModLogs.Create("activate", targetUser.ID, "user", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } // TODO: Trickle the hookTable down from the router hTbl := c.GetHookTable() - skip, rerr := hTbl.VhookSkippable("action_end_activate_user", targetUser.ID, user) + skip, rerr := hTbl.VhookSkippable("action_end_activate_user", targetUser.ID, u) if skip || rerr != nil { return rerr } @@ -197,40 +197,40 @@ func ActivateUser(w http.ResponseWriter, r *http.Request, user *c.User, suid str return nil } -func DeletePostsSubmit(w http.ResponseWriter, r *http.Request, user *c.User, suid string) c.RouteError { - if !user.Perms.BanUsers { - return c.NoPermissions(w, r, user) +func DeletePostsSubmit(w http.ResponseWriter, r *http.Request, u *c.User, suid string) c.RouteError { + if !u.Perms.BanUsers { + return c.NoPermissions(w, r, u) } uid, err := strconv.Atoi(suid) if err != nil { - return c.LocalError("The provided UserID is not a valid number.", w, r, user) + return c.LocalError("The provided UserID is not a valid number.", w, r, u) } targetUser, err := c.Users.Get(uid) if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to purge posts of no longer exists.", w, r, user) + return c.LocalError("The user you're trying to purge posts of no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } // TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one? if targetUser.IsMod { - return c.LocalError("You may not purge the posts of another staff member.", w, r, user) + return c.LocalError("You may not purge the posts of another staff member.", w, r, u) } err = targetUser.DeletePosts() if err == sql.ErrNoRows { - return c.LocalError("The user you're trying to purge posts of no longer exists.", w, r, user) + return c.LocalError("The user you're trying to purge posts of no longer exists.", w, r, u) } else if err != nil { return c.InternalError(err, w, r) } - err = c.ModLogs.Create("delete-posts", uid, "user", user.GetIP(), user.ID) + err = c.ModLogs.Create("delete-posts", uid, "user", u.GetIP(), u.ID) if err != nil { return c.InternalError(err, w, r) } // TODO: Trickle the hookTable down from the router hTbl := c.GetHookTable() - skip, rerr := hTbl.VhookSkippable("action_end_delete_posts", targetUser.ID, user) + skip, rerr := hTbl.VhookSkippable("action_end_delete_posts", targetUser.ID, u) if skip || rerr != nil { return rerr } diff --git a/schema/mysql/query_login_logs.sql b/schema/mysql/query_login_logs.sql index 58a4364b..eade8bf6 100644 --- a/schema/mysql/query_login_logs.sql +++ b/schema/mysql/query_login_logs.sql @@ -1,7 +1,7 @@ CREATE TABLE `login_logs` ( `lid` int not null AUTO_INCREMENT, `uid` int not null, - `success` bool DEFAULT 0 not null, + `success` boolean DEFAULT 0 not null, `ipaddress` varchar(200) not null, `doneAt` datetime not null, primary key(`lid`) diff --git a/schema/mysql/query_registration_logs.sql b/schema/mysql/query_registration_logs.sql index 4ff25633..12112529 100644 --- a/schema/mysql/query_registration_logs.sql +++ b/schema/mysql/query_registration_logs.sql @@ -3,7 +3,7 @@ CREATE TABLE `registration_logs` ( `username` varchar(100) not null, `email` varchar(100) not null, `failureReason` varchar(100) not null, - `success` bool DEFAULT 0 not null, + `success` boolean DEFAULT 0 not null, `ipaddress` varchar(200) not null, `doneAt` datetime not null, primary key(`rlid`) diff --git a/schema/mysql/query_users.sql b/schema/mysql/query_users.sql index 59fd9e0e..ddcbd762 100644 --- a/schema/mysql/query_users.sql +++ b/schema/mysql/query_users.sql @@ -10,6 +10,8 @@ CREATE TABLE `users` ( `lastActiveAt` datetime not null, `session` varchar(200) DEFAULT '' not null, `last_ip` varchar(200) DEFAULT '' not null, + `profile_comments` int DEFAULT 0 not null, + `who_can_convo` int DEFAULT 0 not null, `enable_embeds` int DEFAULT -1 not null, `email` varchar(200) DEFAULT '' not null, `avatar` varchar(100) DEFAULT '' not null, diff --git a/schema/pgsql/query_forums_permissions.sql b/schema/pgsql/query_forums_permissions.sql index 9954e7e7..06492540 100644 --- a/schema/pgsql/query_forums_permissions.sql +++ b/schema/pgsql/query_forums_permissions.sql @@ -2,6 +2,6 @@ CREATE TABLE "forums_permissions" ( `fid` int not null, `gid` int not null, `preset` varchar (100) DEFAULT '' not null, - `permissions` text not null, + `permissions` text DEFAULT '{}' not null, primary key(`fid`,`gid`) ); \ No newline at end of file diff --git a/schema/pgsql/query_login_logs.sql b/schema/pgsql/query_login_logs.sql index 7bf96551..33d6f293 100644 --- a/schema/pgsql/query_login_logs.sql +++ b/schema/pgsql/query_login_logs.sql @@ -1,7 +1,7 @@ CREATE TABLE "login_logs" ( `lid` serial not null, `uid` int not null, - `success` bool DEFAULT 0 not null, + `success` boolean DEFAULT 0 not null, `ipaddress` varchar (200) not null, `doneAt` timestamp not null, primary key(`lid`) diff --git a/schema/pgsql/query_registration_logs.sql b/schema/pgsql/query_registration_logs.sql index 178bb48c..a0d7d6a3 100644 --- a/schema/pgsql/query_registration_logs.sql +++ b/schema/pgsql/query_registration_logs.sql @@ -3,7 +3,7 @@ CREATE TABLE "registration_logs" ( `username` varchar (100) not null, `email` varchar (100) not null, `failureReason` varchar (100) not null, - `success` bool DEFAULT 0 not null, + `success` boolean DEFAULT 0 not null, `ipaddress` varchar (200) not null, `doneAt` timestamp not null, primary key(`rlid`) diff --git a/templates/account_own_edit_privacy.html b/templates/account_own_edit_privacy.html index 43dc9c2c..1527e3fc 100644 --- a/templates/account_own_edit_privacy.html +++ b/templates/account_own_edit_privacy.html @@ -3,17 +3,18 @@
- + + -
- +
- - - + - + {{template "panel_analytics_time_range_month.html" . }}
-
-
-
+
+
+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_memory.html b/templates/panel_analytics_memory.html index 6d46060f..64814e8d 100644 --- a/templates/panel_analytics_memory.html +++ b/templates/panel_analytics_memory.html @@ -4,16 +4,16 @@ {{template "panel_analytics_time_range_month.html" . }}
-
-
-
+
+
+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_performance.html b/templates/panel_analytics_performance.html index fdc60880..05a338a2 100644 --- a/templates/panel_analytics_performance.html +++ b/templates/panel_analytics_performance.html @@ -1,25 +1,25 @@

{{lang "panel_stats_perf_head"}}

- + + + - + {{template "panel_analytics_time_range_month.html" . }}
-
-
-
+
+
+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_posts.html b/templates/panel_analytics_posts.html index e097e908..904f02f1 100644 --- a/templates/panel_analytics_posts.html +++ b/templates/panel_analytics_posts.html @@ -4,16 +4,16 @@ {{template "panel_analytics_time_range.html" . }}
-
-
-
+
+
+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_referrer_views.html b/templates/panel_analytics_referrer_views.html index 50fce0fe..f120cd66 100644 --- a/templates/panel_analytics_referrer_views.html +++ b/templates/panel_analytics_referrer_views.html @@ -4,8 +4,8 @@ {{template "panel_analytics_time_range.html" . }}
-
-
+
+
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_referrers.html b/templates/panel_analytics_referrers.html index f9db0cb5..7b8e1bb6 100644 --- a/templates/panel_analytics_referrers.html +++ b/templates/panel_analytics_referrers.html @@ -1,19 +1,19 @@

{{lang "panel_stats_referrers_head"}}

- + + - + {{template "panel_analytics_time_range.html" . }}
-
-
+
+
{{range .ItemList}}
- {{.Agent}} + {{.Agent}} {{.Count}}{{lang "panel_stats_views_suffix"}}
{{else}}
{{lang "panel_stats_referrers_no_referrers"}}
{{end}} diff --git a/templates/panel_analytics_route_views.html b/templates/panel_analytics_route_views.html index c16457bd..b1b937ac 100644 --- a/templates/panel_analytics_route_views.html +++ b/templates/panel_analytics_route_views.html @@ -4,14 +4,14 @@ {{template "panel_analytics_time_range.html" . }}
-
-
+
+
-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_routes.html b/templates/panel_analytics_routes.html index bf46e393..7a2fd77e 100644 --- a/templates/panel_analytics_routes.html +++ b/templates/panel_analytics_routes.html @@ -4,14 +4,14 @@ {{template "panel_analytics_time_range.html" . }}
-
-
+
+
-
+
{{range .ItemList}}
- {{.Route}} + {{.Route}} {{.Count}}{{lang "panel_stats_views_suffix"}}
{{else}}
{{lang "panel_stats_routes_no_routes"}}
{{end}} diff --git a/templates/panel_analytics_routes_perf.html b/templates/panel_analytics_routes_perf.html index 71784e6a..c285b413 100644 --- a/templates/panel_analytics_routes_perf.html +++ b/templates/panel_analytics_routes_perf.html @@ -4,14 +4,14 @@ {{template "panel_analytics_time_range.html" . }}
-
-
+
+
-
+
{{range .ItemList}}
- {{.Route}} + {{.Route}} {{.Count}}{{.Unit}}
{{else}}
{{lang "panel_stats_routes_no_routes"}}
{{end}} diff --git a/templates/panel_analytics_script.html b/templates/panel_analytics_script.html index f7afc5fc..a837f519 100644 --- a/templates/panel_analytics_script.html +++ b/templates/panel_analytics_script.html @@ -13,7 +13,7 @@ let legendNames = [{{range .Graph.Legends}} addInitHook("after_phrases", () => { addInitHook("end_init", () => { addInitHook("analytics_loaded", () => { - buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames); + buildStatsChart(rawLabels,seriesData,"{{.TimeRange}}",legendNames); }); }); }); diff --git a/templates/panel_analytics_script_memory.html b/templates/panel_analytics_script_memory.html index eece85a4..95c0c08a 100644 --- a/templates/panel_analytics_script_memory.html +++ b/templates/panel_analytics_script_memory.html @@ -13,8 +13,8 @@ let legendNames = [{{range .Graph.Legends}} addInitHook("after_phrases", () => { addInitHook("end_init", () => { addInitHook("analytics_loaded", () => { - memStuff(window, document, Chartist); - buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames,1); + memStuff(window,document,Chartist); + buildStatsChart(rawLabels,seriesData,"{{.TimeRange}}",legendNames,1); }); }); }); diff --git a/templates/panel_analytics_script_perf.html b/templates/panel_analytics_script_perf.html index 30bb1f5a..9b2e2f64 100644 --- a/templates/panel_analytics_script_perf.html +++ b/templates/panel_analytics_script_perf.html @@ -13,8 +13,8 @@ let legendNames = [{{range .Graph.Legends}} addInitHook("after_phrases", () => { addInitHook("end_init", () => { addInitHook("analytics_loaded", () => { - perfStuff(window, document, Chartist); - buildStatsChart(rawLabels, seriesData, "{{.TimeRange}}",legendNames,2); + perfStuff(window,document,Chartist); + buildStatsChart(rawLabels,seriesData,"{{.TimeRange}}",legendNames,2); }); }); }); diff --git a/templates/panel_analytics_system_views.html b/templates/panel_analytics_system_views.html index f8214aa7..8d84a42d 100644 --- a/templates/panel_analytics_system_views.html +++ b/templates/panel_analytics_system_views.html @@ -4,8 +4,8 @@ {{template "panel_analytics_time_range.html" . }}
-
-
+
+
{{template "panel_analytics_script.html" . }} \ No newline at end of file diff --git a/templates/panel_analytics_systems.html b/templates/panel_analytics_systems.html index 4dbb3a3a..353beff8 100644 --- a/templates/panel_analytics_systems.html +++ b/templates/panel_analytics_systems.html @@ -4,14 +4,14 @@ {{template "panel_analytics_time_range.html" . }}
-
-
+
+
-
+
{{range .ItemList}}
- {{.FriendlyAgent}} + {{.FriendlyAgent}} {{.Count}}{{lang "panel_stats_views_suffix"}}
{{else}}
{{lang "panel_stats_operating_systems_no_operating_systems"}}
{{end}} diff --git a/templates/panel_analytics_time_range_month.html b/templates/panel_analytics_time_range_month.html index 65e6d4e6..7336aacd 100644 --- a/templates/panel_analytics_time_range_month.html +++ b/templates/panel_analytics_time_range_month.html @@ -1,9 +1,9 @@ - + + + + + + - \ No newline at end of file + \ No newline at end of file diff --git a/templates/panel_analytics_topics.html b/templates/panel_analytics_topics.html index 5a2092e8..92678980 100644 --- a/templates/panel_analytics_topics.html +++ b/templates/panel_analytics_topics.html @@ -4,16 +4,16 @@ {{template "panel_analytics_time_range.html" . }}
-
-
-
+
+
+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_analytics_views.html b/templates/panel_analytics_views.html index 952e1eb8..8ea2d110 100644 --- a/templates/panel_analytics_views.html +++ b/templates/panel_analytics_views.html @@ -4,16 +4,16 @@ {{template "panel_analytics_time_range.html" . }}
-
-
-
+
+
+

{{lang "panel_stats_details_head"}}

-
+
{{range .ViewItems}}
{{.Time}} diff --git a/templates/panel_backups.html b/templates/panel_backups.html index ea557114..7e114883 100644 --- a/templates/panel_backups.html +++ b/templates/panel_backups.html @@ -1,12 +1,12 @@

{{lang "panel_backups_head"}}

-
+
{{range .Backups}} {{else}}
{{lang "panel_backups_no_backups"}}
{{end}} diff --git a/templates/panel_forum_edit.html b/templates/panel_forum_edit.html index 120d85d8..d5312499 100644 --- a/templates/panel_forum_edit.html +++ b/templates/panel_forum_edit.html @@ -1,15 +1,15 @@

{{.Name}}{{lang "panel_forum_head_suffix"}}

-
+
@@ -23,7 +23,7 @@
+ {{lang "panel_forum_edit_button"}}
diff --git a/templates/panel_forum_edit_perms.html b/templates/panel_forum_edit_perms.html index 0342df6b..907a9e96 100644 --- a/templates/panel_forum_edit_perms.html +++ b/templates/panel_forum_edit_perms.html @@ -1,7 +1,7 @@

{{.Name}}{{lang "panel_forum_head_suffix"}}

- +
{{range .Perms}}
@@ -16,7 +16,7 @@
{{end}}
-
+
\ No newline at end of file diff --git a/templates/panel_modlogs.html b/templates/panel_modlogs.html index 4d07d154..bf371c16 100644 --- a/templates/panel_modlogs.html +++ b/templates/panel_modlogs.html @@ -1,7 +1,7 @@

{{lang "panel_logs_mod_head"}}

-
+
{{range .Logs}}
diff --git a/templates/panel_pages_edit.html b/templates/panel_pages_edit.html index d48bbb98..5b600f20 100644 --- a/templates/panel_pages_edit.html +++ b/templates/panel_pages_edit.html @@ -1,15 +1,15 @@

{{lang "panel_pages_edit_head"}}

-
-
+ +
@@ -17,7 +17,7 @@
-
+
\ No newline at end of file diff --git a/templates/panel_plugins.html b/templates/panel_plugins.html index 08d6129a..ea076d7d 100644 --- a/templates/panel_plugins.html +++ b/templates/panel_plugins.html @@ -1,20 +1,20 @@

{{lang "panel_plugins_head"}}

-
+
{{range .ItemList}}
- {{.Name}}
+ {{.Name}}
{{lang "panel_plugins_author_prefix"}}{{.Author}}
- {{if .Settings}}{{lang "panel_plugins_settings"}}{{end}} - {{if .Active}}{{lang "panel_plugins_deactivate"}} + {{if .Settings}}{{lang "panel_plugins_settings"}}{{end}} + {{if .Active}}{{lang "panel_plugins_deactivate"}} {{else if .Installable}} {{/** TODO: Write a custom template interpreter to fix this nonsense **/}} - {{if .Installed}}{{lang "panel_plugins_activate"}}{{else}}{{lang "panel_plugins_install"}}{{end}} - {{else}}{{lang "panel_plugins_activate"}}{{end}} + {{if .Installed}}{{lang "panel_plugins_activate"}}{{else}}{{lang "panel_plugins_install"}}{{end}} + {{else}}{{lang "panel_plugins_activate"}}{{end}}
{{end}} diff --git a/templates/panel_themes_menus_items.html b/templates/panel_themes_menus_items.html index 710823f0..47afb845 100644 --- a/templates/panel_themes_menus_items.html +++ b/templates/panel_themes_menus_items.html @@ -4,14 +4,14 @@

{{lang "panel_hints_reorder"}}

-
+
{{range .ItemList}} -
+
- {{.Name}} + {{.Name}} - - + +
{{end}}
@@ -21,9 +21,9 @@

{{lang "panel_themes_menus_create_head"}}

-
- -
+ + +
{{/** TODO: Let an admin move a menu item from one menu to another? **/}}
diff --git a/templates/profile.html b/templates/profile.html index 60cc0b55..8a14552a 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -58,7 +58,7 @@ {{if .CurrentUser.Loggedin}} {{if .CurrentUser.Perms.BanUsers}} - {{if .ShowComments}} +
{{template "profile_comments_row.html" . }}
{{end}} {{if .CurrentUser.Loggedin}} {{if .CanComment}}