From 0c1d6f0516e460db2ff9ac336a32561fd3d8b116 Mon Sep 17 00:00:00 2001 From: Azareal Date: Tue, 4 Feb 2020 21:47:03 +1000 Subject: [PATCH] Add CreateProfileReply and AutoEmbed group permissions. Log profile reply deletions in the moderator log. Split the global permissions in the UI to make them easier to manage. Experiment with showing group ID in group edit header. Avoid loading groups multiple times for the same profile reply. Initialise disabled IP log points to empty string rather than 0. Add CreateProfileReply perm phrase. Add AutoEmbed perm phrase. Add panel_group_mod_permissions phrase. Add panel_logs_mod_action_profile_reply_delete phrase. --- cmd/query_gen/main.go | 8 ++-- common/pages.go | 5 +- common/parser.go | 24 ++++++---- common/permissions.go | 18 ++++--- common/profile_reply.go | 2 +- common/profile_reply_store.go | 4 +- common/reply.go | 2 +- common/reply_store.go | 4 +- common/report_store.go | 4 +- common/topic.go | 34 ++++++++------ common/topic_store.go | 4 +- general_test.go | 2 +- langs/english.json | 4 ++ misc_test.go | 68 +++++++++++++-------------- parser_test.go | 8 ++-- routes/panel/groups.go | 16 +++++-- routes/panel/logs.go | 26 +++++++--- routes/profile.go | 33 ++++++------- routes/profile_reply.go | 10 +++- routes/reply.go | 4 +- routes/topic.go | 25 ++++++---- schema/mssql/inserts.sql | 6 +-- schema/mysql/inserts.sql | 6 +-- schema/pgsql/inserts.sql | 6 +-- templates/panel_group_edit.html | 2 +- templates/panel_group_edit_perms.html | 25 +++++++++- 26 files changed, 215 insertions(+), 135 deletions(-) diff --git a/cmd/query_gen/main.go b/cmd/query_gen/main.go index a01f6d84..fc54f933 100644 --- a/cmd/query_gen/main.go +++ b/cmd/query_gen/main.go @@ -118,6 +118,8 @@ func seedTables(a qgen.Adapter) error { UploadFiles UploadAvatars UseConvos + CreateProfileReply + AutoEmbed // CreateConvo ? // CreateConvoReply ? @@ -142,7 +144,7 @@ func seedTables(a qgen.Adapter) error { } return string(jBytes) } - addGroup := func(name string, perms c.Perms, mod bool, admin bool, banned bool, tag string) { + addGroup := func(name string, perms c.Perms, mod, admin, banned bool, tag string) { mi, ai, bi := "0", "0", "0" if mod { mi = "1" @@ -161,10 +163,10 @@ func seedTables(a qgen.Adapter) error { perms.EditGroupAdmin = false addGroup("Administrator", perms, true, true, false, "Admin") - perms = c.Perms{BanUsers: true, ActivateUsers: true, EditUser: true, EditUserEmail: false, EditUserGroup: true, ViewIPs: true, UploadFiles: true, UploadAvatars: true, UseConvos: true, ViewTopic: true, LikeItem: true, CreateTopic: true, EditTopic: true, DeleteTopic: true, CreateReply: true, EditReply: true, DeleteReply: true, PinTopic: true, CloseTopic: true, MoveTopic: true} + perms = c.Perms{BanUsers: true, ActivateUsers: true, EditUser: true, EditUserEmail: false, EditUserGroup: true, ViewIPs: true, UploadFiles: true, UploadAvatars: true, UseConvos: true, CreateProfileReply: true, AutoEmbed: true, ViewTopic: true, LikeItem: true, CreateTopic: true, EditTopic: true, DeleteTopic: true, CreateReply: true, EditReply: true, DeleteReply: true, PinTopic: true, CloseTopic: true, MoveTopic: true} addGroup("Moderator", perms, true, false, false, "Mod") - perms = c.Perms{UploadFiles: true, UploadAvatars: true, UseConvos: true, ViewTopic: true, LikeItem: true, CreateTopic: true, CreateReply: true} + perms = c.Perms{UploadFiles: true, UploadAvatars: true, UseConvos: true, CreateProfileReply: true, AutoEmbed: true, ViewTopic: true, LikeItem: true, CreateTopic: true, CreateReply: true} addGroup("Member", perms, false, false, false, "") perms = c.Perms{ViewTopic: true} diff --git a/common/pages.go b/common/pages.go index d5aa7bf2..54be0736 100644 --- a/common/pages.go +++ b/common/pages.go @@ -251,8 +251,8 @@ type AccountBlocksPage struct { type AccountPrivacyPage struct { *Header ProfileComments bool - ReceiveConvos bool - EnableEmbeds bool + ReceiveConvos bool + EnableEmbeds bool } type AccountDashPage struct { @@ -594,6 +594,7 @@ type PanelEditGroupPermsPage struct { Name string LocalPerms []NameLangToggle GlobalPerms []NameLangToggle + ModPerms []NameLangToggle } type GroupPromotionExtend struct { diff --git a/common/parser.go b/common/parser.go index d851ee17..2e17844b 100644 --- a/common/parser.go +++ b/common/parser.go @@ -465,19 +465,25 @@ func (ps *ParseSettings) CopyPtr() *ParseSettings { // TODO: Write a test for this // TODO: We need a lot more hooks here. E.g. To add custom media types and handlers. // TODO: Use templates to reduce the amount of boilerplate? -func ParseMessage(msg string, sectionID int, sectionType string, settings *ParseSettings /*, user User*/) string { +func ParseMessage(msg string, sectionID int, sectionType string, settings *ParseSettings, user *User) string { if settings == nil { settings = DefaultParseSettings } + if user == nil { + user = &GuestUser + } // TODO: Word boundary detection for these to avoid mangling code - msg = strings.Replace(msg, ":)", "😀", -1) - msg = strings.Replace(msg, ":(", "😞", -1) - msg = strings.Replace(msg, ":D", "😃", -1) - msg = strings.Replace(msg, ":P", "😛", -1) - msg = strings.Replace(msg, ":O", "😲", -1) - msg = strings.Replace(msg, ":p", "😛", -1) - msg = strings.Replace(msg, ":o", "😲", -1) - msg = strings.Replace(msg, ";)", "😉", -1) + rep := func(find, replace string) { + msg = strings.Replace(msg, find, replace, -1) + } + rep(":)", "😀") + rep(":(", "😞") + rep(":D", "😃") + rep(":P", "😛") + rep(":O", "😲") + rep(":p", "😛") + rep(":o", "😲") + rep(";)", "😉") // Word filter list. E.g. Swear words and other things the admins don't like wordFilters, err := WordFilters.GetAll() diff --git a/common/permissions.go b/common/permissions.go index 6346b9b6..750ae635 100644 --- a/common/permissions.go +++ b/common/permissions.go @@ -40,6 +40,8 @@ var GlobalPermList = []string{ "UploadFiles", "UploadAvatars", "UseConvos", + "CreateProfileReply", + "AutoEmbed", } // Permission Structure: ActionComponent[Subcomponent]Flag @@ -66,9 +68,11 @@ type Perms struct { ViewIPs bool `json:",omitempty"` // Global non-staff permissions - UploadFiles bool `json:",omitempty"` - UploadAvatars bool `json:",omitempty"` - UseConvos bool `json:",omitempty"` + UploadFiles bool `json:",omitempty"` + UploadAvatars bool `json:",omitempty"` + UseConvos bool `json:",omitempty"` + CreateProfileReply bool `json:",omitempty"` + AutoEmbed bool `json:",omitempty"` // Forum permissions ViewTopic bool `json:",omitempty"` @@ -122,9 +126,11 @@ func init() { ViewAdminLogs: true, ViewIPs: true, - UploadFiles: true, - UploadAvatars: true, - UseConvos: true, + UploadFiles: true, + UploadAvatars: true, + UseConvos: true, + CreateProfileReply: true, + AutoEmbed: true, ViewTopic: true, LikeItem: true, diff --git a/common/profile_reply.go b/common/profile_reply.go index e4cb44e5..37a3024e 100644 --- a/common/profile_reply.go +++ b/common/profile_reply.go @@ -65,7 +65,7 @@ func (r *ProfileReply) Delete() error { func (r *ProfileReply) SetBody(content string) error { content = PreparseMessage(html.UnescapeString(content)) - _, err := profileReplyStmts.edit.Exec(content, ParseMessage(content, 0, "", nil), r.ID) + _, err := profileReplyStmts.edit.Exec(content, ParseMessage(content, 0, "", nil, nil), r.ID) return err } diff --git a/common/profile_reply_store.go b/common/profile_reply_store.go index 542d2ca1..0565accd 100644 --- a/common/profile_reply_store.go +++ b/common/profile_reply_store.go @@ -50,9 +50,9 @@ func (s *SQLProfileReplyStore) Exists(id int) bool { func (s *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ip string) (id int, err error) { if Config.DisablePostIP { - ip = "0" + ip = "" } - res, err := s.create.Exec(profileID, content, ParseMessage(content, 0, "", nil), createdBy, ip) + res, err := s.create.Exec(profileID, content, ParseMessage(content, 0, "", nil, nil), createdBy, ip) if err != nil { return 0, err } diff --git a/common/reply.go b/common/reply.go index 2968bf9a..5055049d 100644 --- a/common/reply.go +++ b/common/reply.go @@ -199,7 +199,7 @@ func (r *Reply) SetPost(content string) error { return err } content = PreparseMessage(html.UnescapeString(content)) - parsedContent := ParseMessage(content, topic.ParentID, "forums", nil) + parsedContent := ParseMessage(content, topic.ParentID, "forums", nil, nil) _, err = replyStmts.edit.Exec(content, parsedContent, r.ID) // TODO: Sniff if this changed anything to see if we hit an existing poll _ = Rstore.GetCache().Remove(r.ID) return err diff --git a/common/reply_store.go b/common/reply_store.go index 186eadaf..a1b719cb 100644 --- a/common/reply_store.go +++ b/common/reply_store.go @@ -99,9 +99,9 @@ func (s *SQLReplyStore) Exists(id int) bool { // TODO: Write a test for this func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) (rid int, err error) { if Config.DisablePostIP { - ip = "0" + ip = "" } - res, err := s.create.Exec(t.ID, content, ParseMessage(content, t.ParentID, "forums", nil), ip, WordCount(content), uid) + res, err := s.create.Exec(t.ID, content, ParseMessage(content, t.ParentID, "forums", nil, nil), ip, WordCount(content), uid) if err != nil { return 0, err } diff --git a/common/report_store.go b/common/report_store.go index b7360ba1..096b7656 100644 --- a/common/report_store.go +++ b/common/report_store.go @@ -46,9 +46,9 @@ func (s *DefaultReportStore) Create(title, content string, u *User, itemType str ip := u.GetIP() if Config.DisablePostIP { - ip = "0" + ip = "" } - res, err := s.create.Exec(title, content, ParseMessage(content, 0, "", nil), ip, u.ID, u.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID) + res, err := s.create.Exec(title, content, ParseMessage(content, 0, "", nil, nil), ip, u.ID, u.ID, itemType+"_"+strconv.Itoa(itemID), ReportForumID) if err != nil { return 0, err } diff --git a/common/topic.go b/common/topic.go index ce751799..4d8dde31 100644 --- a/common/topic.go +++ b/common/topic.go @@ -400,7 +400,7 @@ func handleAttachments(stmt *sql.Stmt, id int) error { } // TODO: Only load a row per createdBy, maybe with group by? -func handleTopicReplies(umap map[int]struct{}, uid int, tid int) error { +func handleTopicReplies(umap map[int]struct{}, uid, tid int) error { rows, err := userStmts.getRepliesOfTopic.Query(uid, tid) if err != nil { return err @@ -505,7 +505,7 @@ func (t *Topic) Update(name, content string) error { } content = PreparseMessage(html.UnescapeString(content)) - parsedContent := ParseMessage(content, t.ParentID, "forums", nil) + parsedContent := ParseMessage(content, t.ParentID, "forums", nil, nil) _, err := topicStmts.edit.Exec(name, content, parsedContent, t.ID) t.cacheRemove() return err @@ -518,9 +518,9 @@ func (t *Topic) SetPoll(pollID int) error { } // TODO: Have this go through the ReplyStore? -func (t *Topic) CreateActionReply(action string, ip string, uid int) (err error) { +func (t *Topic) CreateActionReply(action, ip string, uid int) (err error) { if Config.DisablePostIP { - ip = "0" + ip = "" } res, err := topicStmts.createAction.Exec(t.ID, action, ip, uid) if err != nil { @@ -566,13 +566,13 @@ var unlockai = "🔓" var stickai = "📌" var unstickai = "📌" + aipost -func (ru *ReplyUser) Init() error { +func (ru *ReplyUser) Init() (group *Group, err error) { ru.UserLink = BuildProfileURL(NameToSlug(ru.CreatedByName), ru.CreatedBy) ru.ContentLines = strings.Count(ru.Content, "\n") postGroup, err := Groups.Get(ru.Group) if err != nil { - return err + return nil, err } if postGroup.IsMod { ru.ClassName = Config.StaffCSS @@ -581,9 +581,6 @@ func (ru *ReplyUser) Init() error { // TODO: Make a function for this? Build a more sophisticated noavatar handling system? Do bulk user loads and let the c.UserStore initialise this? ru.Avatar, ru.MicroAvatar = BuildAvatar(ru.CreatedBy, ru.Avatar) - if ru.Tag == "" { - ru.Tag = postGroup.Tag - } // We really shouldn't have inline HTML, we should do something about this... if ru.ActionType != "" { @@ -604,18 +601,18 @@ func (ru *ReplyUser) Init() error { forum, err := Forums.Get(fid) if err == nil { ru.ActionType = p.GetTmplPhrasef("topic.action_topic_move_dest", forum.Link, forum.Name, ru.UserLink, ru.CreatedByName) - return nil + return postGroup, nil } } default: // TODO: Only fire this off if a corresponding phrase for the ActionType doesn't exist? Or maybe have some sort of action registry? ru.ActionType = p.GetTmplPhrasef("topic.action_topic_default", ru.ActionType) - return nil + return postGroup, nil } ru.ActionType = p.GetTmplPhrasef("topic.action_topic_"+action, ru.UserLink, ru.CreatedByName) } - return nil + return postGroup, nil } // TODO: Factor TopicUser into a *Topic and *User, as this starting to become overly complicated x.x @@ -648,12 +645,21 @@ func (t *TopicUser) Replies(offset, pFrag int, user *User) (rlist []*ReplyUser, hTbl := GetHookTable() rf := func(r *ReplyUser) error { //log.Printf("before r: %+v\n", r) - err := r.Init() + group, err := r.Init() if err != nil { return err } //log.Printf("after r: %+v\n", r) - r.ContentHtml = ParseMessage(r.Content, t.ParentID, "forums", user.ParseSettings) + + var parseSettings *ParseSettings + if !group.Perms.AutoEmbed && (user.ParseSettings == nil || !user.ParseSettings.NoEmbed) { + parseSettings = DefaultParseSettings.CopyPtr() + parseSettings.NoEmbed = true + } else { + parseSettings = user.ParseSettings + } + + r.ContentHtml = ParseMessage(r.Content, t.ParentID, "forums", parseSettings, user) // TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do. if r.ContentHtml == r.Content { r.ContentHtml = r.Content diff --git a/common/topic_store.go b/common/topic_store.go index c508fba8..d062aeee 100644 --- a/common/topic_store.go +++ b/common/topic_store.go @@ -222,14 +222,14 @@ func (s *DefaultTopicStore) Create(fid int, name, content string, uid int, ip st return 0, ErrLongTitle } - parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums", nil)) + parsedContent := strings.TrimSpace(ParseMessage(content, fid, "forums", nil, nil)) if parsedContent == "" { return 0, ErrNoBody } // TODO: Move this statement into the topic store if Config.DisablePostIP { - ip = "0" + ip = "" } res, err := s.create.Exec(fid, name, content, parsedContent, uid, ip, WordCount(content), uid) if err != nil { diff --git a/general_test.go b/general_test.go index 50e8ee5f..b50dd7ff 100644 --- a/general_test.go +++ b/general_test.go @@ -936,7 +936,7 @@ func BenchmarkParserSerial(b *testing.B) { f := func(name, msg string) func(b *testing.B) { return func(b *testing.B) { for i := 0; i < b.N; i++ { - _ = c.ParseMessage(msg, 0, "", nil) + _ = c.ParseMessage(msg, 0, "", nil, nil) } } } diff --git a/langs/english.json b/langs/english.json index 62365b2d..e1424deb 100644 --- a/langs/english.json +++ b/langs/english.json @@ -31,6 +31,8 @@ "UploadFiles": "Can upload files", "UploadAvatars": "Can upload avatars", "UseConvos":"Can use conversations", + "CreateProfileReply": "Can create profile replies", + "AutoEmbed":"Automatically embed media they post", "ViewTopic": "Can view topics", "LikeItem": "Can like items", @@ -919,6 +921,7 @@ "panel_group_tag_placeholder":"VIP", "panel_group_update_button":"Update Group", "panel_group_extended_permissions":"Extended Permissions", + "panel_group_mod_permissions":"Moderator Permissions", "panel_group_promotions_level_prefix":"level ", "panel_group_promotions_posts_prefix":"posts ", @@ -1029,6 +1032,7 @@ "panel_logs_mod_action_topic_move_dest":"%s was moved to %s by %s", "panel_logs_mod_action_topic_unknown":"Unknown action '%s' on elementType '%s' by %s", "panel_logs_mod_action_reply_delete":"A reply in %s was deleted by %s", + "panel_logs_mod_action_profile_reply_delete":"A reply on %s's profile was deleted by %s", "panel_logs_mod_action_user_ban":"%s was banned by %s", "panel_logs_mod_action_user_unban":"%s was unbanned by %s", "panel_logs_mod_action_user_delete-posts":"%s had their posts purged by %s", diff --git a/misc_test.go b/misc_test.go index 74069816..ad3d9e7d 100644 --- a/misc_test.go +++ b/misc_test.go @@ -59,17 +59,17 @@ func TestUserStore(t *testing.T) { func userStoreTest(t *testing.T, newUserID int) { ucache := c.Users.GetCache() // Go doesn't have short-circuiting, so this'll allow us to do one liner tests - isCacheLengthZero := func(ucache c.UserCache) bool { - if ucache == nil { + isCacheLengthZero := func(uc c.UserCache) bool { + if uc == nil { return true } - return ucache.Length() == 0 + return uc.Length() == 0 } - cacheLength := func(ucache c.UserCache) int { - if ucache == nil { + cacheLength := func(uc c.UserCache) int { + if uc == nil { return 0 } - return ucache.Length() + return uc.Length() } expect(t, isCacheLengthZero(ucache), fmt.Sprintf("The initial ucache length should be zero, not %d", cacheLength(ucache))) @@ -84,7 +84,7 @@ func userStoreTest(t *testing.T, newUserID int) { user, err := c.Users.Get(1) recordMustExist(t, err, "Couldn't find UID #1") - expectW := func(cond bool, expec bool, prefix string, suffix string) { + expectW := func(cond, expec bool, prefix, suffix string) { midfix := "should not be" if expec { midfix = "should be" @@ -93,7 +93,7 @@ func userStoreTest(t *testing.T, newUserID int) { } // TODO: Add email checks too? Do them separately? - expectUser := func(u *c.User, uid int, name string, group int, super bool, admin bool, mod bool, banned bool) { + expectUser := func(u *c.User, uid int, name string, group int, super, admin, mod, banned bool) { expect(t, u.ID == uid, fmt.Sprintf("u.ID should be %d. Got '%d' instead.", uid, u.ID)) expect(t, u.Name == name, fmt.Sprintf("u.Name should be '%s', not '%s'", name, u.Name)) expectW(u.Group == group, true, u.Name, "in group"+strconv.Itoa(group)) @@ -258,7 +258,7 @@ func userStoreTest(t *testing.T, newUserID int) { dummyRequest2 := httptest.NewRequest("", "/forum/"+strconv.Itoa(generalForumID), bytesBuffer) var user2 *c.User - changeGroupTest := func(oldGroup int, newGroup int) { + changeGroupTest := func(oldGroup, newGroup int) { err = user.ChangeGroup(newGroup) expectNilErr(t, err) // ! I don't think ChangeGroup should be changing the value of user... Investigate this. @@ -270,7 +270,7 @@ func userStoreTest(t *testing.T, newUserID int) { *user2 = *user } - changeGroupTest2 := func(rank string, firstShouldBe bool, secondShouldBe bool) { + changeGroupTest2 := func(rank string, firstShouldBe, secondShouldBe bool) { head, err := c.UserCheck(dummyResponseRecorder, dummyRequest1, user) if err != nil { t.Fatal(err) @@ -364,7 +364,7 @@ func expectNilErr(t *testing.T, item error) { } } -func expectIntToBeX(t *testing.T, item int, expect int, errmsg string) { +func expectIntToBeX(t *testing.T, item, expect int, errmsg string) { if item != expect { debug.PrintStack() t.Fatalf(errmsg, item) @@ -452,14 +452,14 @@ func TestTopicStore(t *testing.T) { c.Config.DisablePostIP = false topicStoreTest(t, 2, "::1") c.Config.DisablePostIP = true - topicStoreTest(t, 3, "0") + topicStoreTest(t, 3, "") c.Topics, err = c.NewDefaultTopicStore(nil) expectNilErr(t, err) c.Config.DisablePostIP = false topicStoreTest(t, 4, "::1") c.Config.DisablePostIP = true - topicStoreTest(t, 5, "0") + topicStoreTest(t, 5, "") } func topicStoreTest(t *testing.T, newID int, ip string) { var topic *c.Topic @@ -914,20 +914,20 @@ func TestReplyStore(t *testing.T) { c.Config.DisablePostIP = false testReplyStore(t, 2, 1, "::1") c.Config.DisablePostIP = true - testReplyStore(t, 5, 3, "0") + testReplyStore(t, 5, 3, "") } func testReplyStore(t *testing.T, newID, newPostCount int, ip string) { - replyTest2 := func(reply *c.Reply, err error, rid int, parentID int, createdBy int, content string, ip string) { + replyTest2 := func(r *c.Reply, err error, rid, parentID, createdBy int, content, ip string) { expectNilErr(t, err) - expect(t, reply.ID == rid, fmt.Sprintf("RID #%d has the wrong ID. It should be %d not %d", rid, rid, reply.ID)) - expect(t, reply.ParentID == parentID, fmt.Sprintf("The parent topic of RID #%d should be %d not %d", rid, parentID, reply.ParentID)) - expect(t, reply.CreatedBy == createdBy, fmt.Sprintf("The creator of RID #%d should be %d not %d", rid, createdBy, reply.CreatedBy)) - expect(t, reply.Content == content, fmt.Sprintf("The contents of RID #%d should be '%s' not %s", rid, content, reply.Content)) - expect(t, reply.IP == ip, fmt.Sprintf("The IP of RID#%d should be '%s' not %s", rid, ip, reply.IP)) + expect(t, r.ID == rid, fmt.Sprintf("RID #%d has the wrong ID. It should be %d not %d", rid, rid, r.ID)) + expect(t, r.ParentID == parentID, fmt.Sprintf("The parent topic of RID #%d should be %d not %d", rid, parentID, r.ParentID)) + expect(t, r.CreatedBy == createdBy, fmt.Sprintf("The creator of RID #%d should be %d not %d", rid, createdBy, r.CreatedBy)) + expect(t, r.Content == content, fmt.Sprintf("The contents of RID #%d should be '%s' not %s", rid, content, r.Content)) + expect(t, r.IP == ip, fmt.Sprintf("The IP of RID#%d should be '%s' not %s", rid, ip, r.IP)) } - replyTest := func(rid int, parentID int, createdBy int, content string, ip string) { + replyTest := func(rid, parentID, createdBy int, content, ip string) { reply, err := c.Rstore.Get(rid) replyTest2(reply, err, rid, parentID, createdBy, content, ip) reply, err = c.Rstore.GetCache().Get(rid) @@ -1015,7 +1015,7 @@ func TestProfileReplyStore(t *testing.T) { c.Config.DisablePostIP = false testProfileReplyStore(t, 1, "::1") c.Config.DisablePostIP = true - testProfileReplyStore(t, 2, "0") + testProfileReplyStore(t, 2, "") } func testProfileReplyStore(t *testing.T, newID int, ip string) { // ? - Commented this one out as strong constraints like this put an unreasonable load on the database, we only want errors if a delete which should succeed fails @@ -1466,22 +1466,22 @@ func TestWidgets(t *testing.T) { expectNilErr(t, err) expect(t, wid == 1, "wid should be 1") + wtest := func(w, w2 *c.Widget) { + expect(t, w.Position == w2.Position, "wrong position") + expect(t, w.Side == w2.Side, "wrong side") + expect(t, w.Type == w2.Type, "wrong type") + expect(t, w2.Enabled, "not enabled") + expect(t, w.Location == w2.Location, "wrong location") + } + // TODO: Do a test for the widget body widget2, err := c.Widgets.Get(1) expectNilErr(t, err) - expect(t, widget2.Position == widget.Position, "wrong position") - expect(t, widget2.Side == widget.Side, "wrong side") - expect(t, widget2.Type == widget.Type, "wrong type") - expect(t, widget2.Enabled, "not enabled") - expect(t, widget2.Location == widget.Location, "wrong location") + wtest(widget, widget2) widgets = c.Docks.RightSidebar.Items expect(t, len(widgets) == 1, fmt.Sprintf("RightSidebar should have 1 item, not %d", len(widgets))) - expect(t, widgets[0].Position == widget.Position, "wrong position") - expect(t, widgets[0].Side == widget.Side, "wrong side") - expect(t, widgets[0].Type == widget.Type, "wrong type") - expect(t, widgets[0].Enabled, "not enabled") - expect(t, widgets[0].Location == widget.Location, "wrong location") + wtest(widget, widgets[0]) widget2.Enabled = false ewidget = &c.WidgetEdit{widget2, map[string]string{"Name": "Test", "Text": "Testing"}} @@ -1493,7 +1493,7 @@ func TestWidgets(t *testing.T) { expect(t, widget2.Position == widget.Position, "wrong position") expect(t, widget2.Side == widget.Side, "wrong side") expect(t, widget2.Type == widget.Type, "wrong type") - expect(t, !widget2.Enabled, "not enabled") + expect(t, !widget2.Enabled, "should not be enabled") expect(t, widget2.Location == widget.Location, "wrong location") widgets = c.Docks.RightSidebar.Items @@ -1501,7 +1501,7 @@ func TestWidgets(t *testing.T) { expect(t, widgets[0].Position == widget.Position, "wrong position") expect(t, widgets[0].Side == widget.Side, "wrong side") expect(t, widgets[0].Type == widget.Type, "wrong type") - expect(t, !widgets[0].Enabled, "not enabled") + expect(t, !widgets[0].Enabled, "should not be enabled") expect(t, widgets[0].Location == widget.Location, "wrong location") err = widget2.Delete() diff --git a/parser_test.go b/parser_test.go index fc7c2c70..c4061016 100644 --- a/parser_test.go +++ b/parser_test.go @@ -300,7 +300,7 @@ func TestParser(t *testing.T) { // TODO: Fix this hack and make the results a bit more reproducible, push the tests further in the process. for _, item := range l.Items { - if res := c.ParseMessage(item.Msg, 1, "forums", nil); res != item.Expects { + if res := c.ParseMessage(item.Msg, 1, "forums", nil, nil); res != item.Expects { if item.Name != "" { t.Error("Name: ", item.Name) } @@ -321,7 +321,7 @@ func TestParser(t *testing.T) { l.Add("//"+c.Site.URL+"\n", ""+c.Site.URL+"
") l.Add("//"+c.Site.URL+"\n//"+c.Site.URL, ""+c.Site.URL+"
"+c.Site.URL+"") for _, item := range l.Items { - if res := c.ParseMessage(item.Msg, 1, "forums", nil); res != item.Expects { + if res := c.ParseMessage(item.Msg, 1, "forums", nil, nil); res != item.Expects { if item.Name != "" { t.Error("Name: ", item.Name) } @@ -345,7 +345,7 @@ func TestParser(t *testing.T) { } c.WriteURL(sb, c.BuildTopicURL("", tid), "#nnid-"+strconv.Itoa(tid)) }) - res := c.ParseMessage("#nnid-1", 1, "forums", nil) + res := c.ParseMessage("#nnid-1", 1, "forums", nil, nil) expect := "#nnid-1" if res != expect { t.Error("Bad output:", "'"+res+"'") @@ -363,7 +363,7 @@ func TestParser(t *testing.T) { } c.WriteURL(sb, c.BuildTopicURL("", tid), "#longidnameneedtooverflowhack-"+strconv.Itoa(tid)) }) - res = c.ParseMessage("#longidnameneedtooverflowhack-1", 1, "forums", nil) + res = c.ParseMessage("#longidnameneedtooverflowhack-1", 1, "forums", nil,nil) expect = "#longidnameneedtooverflowhack-1" if res != expect { t.Error("Bad output:", "'"+res+"'") diff --git a/routes/panel/groups.go b/routes/panel/groups.go index e7c2ee4f..fa684a3c 100644 --- a/routes/panel/groups.go +++ b/routes/panel/groups.go @@ -336,6 +336,17 @@ func GroupsEditPerms(w http.ResponseWriter, r *http.Request, user c.User, sgid s globalPerms = append(globalPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm}) } + addPerm("UploadFiles", g.Perms.UploadFiles) + addPerm("UploadAvatars", g.Perms.UploadAvatars) + addPerm("UseConvos", g.Perms.UseConvos) + addPerm("CreateProfileReply", g.Perms.CreateProfileReply) + addPerm("AutoEmbed", g.Perms.AutoEmbed) + + var modPerms []c.NameLangToggle + addPerm = func(permStr string, perm bool) { + modPerms = append(modPerms, c.NameLangToggle{permStr, p.GetPermPhrase(permStr), perm}) + } + addPerm("BanUsers", g.Perms.BanUsers) addPerm("ActivateUsers", g.Perms.ActivateUsers) addPerm("EditUser", g.Perms.EditUser) @@ -355,11 +366,8 @@ func GroupsEditPerms(w http.ResponseWriter, r *http.Request, user c.User, sgid s addPerm("ManagePlugins", g.Perms.ManagePlugins) addPerm("ViewAdminLogs", g.Perms.ViewAdminLogs) addPerm("ViewIPs", g.Perms.ViewIPs) - addPerm("UploadFiles", g.Perms.UploadFiles) - addPerm("UploadAvatars", g.Perms.UploadAvatars) - addPerm("UseConvos", g.Perms.UseConvos) - pi := c.PanelEditGroupPermsPage{basePage, g.ID, g.Name, localPerms, globalPerms} + pi := c.PanelEditGroupPermsPage{basePage, g.ID, g.Name, localPerms, globalPerms, modPerms} return renderTemplate("panel_group_edit_perms", w, r, basePage.Header, pi) } diff --git a/routes/panel/logs.go b/routes/panel/logs.go index d314a835..3233ec4a 100644 --- a/routes/panel/logs.go +++ b/routes/panel/logs.go @@ -39,21 +39,21 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError // TODO: Log errors when something really screwy is going on? // TODO: Base the slugs on the localised usernames? -func handleUnknownUser(user *c.User, err error) *c.User { +func handleUnknownUser(u *c.User, err error) *c.User { if err != nil { return &c.User{Name: p.GetTmplPhrase("user_unknown"), Link: c.BuildProfileURL("unknown", 0)} } - return user + return u } -func handleUnknownTopic(topic *c.Topic, err error) *c.Topic { +func handleUnknownTopic(t *c.Topic, err error) *c.Topic { if err != nil { return &c.Topic{Title: p.GetTmplPhrase("topic_unknown"), Link: c.BuildTopicURL("unknown", 0)} } - return topic + return t } // TODO: Move the log building logic into /common/ and it's own abstraction -func topicElementTypeAction(action string, elementType string, elementID int, actor *c.User, topic *c.Topic) (out string) { +func topicElementTypeAction(action, elementType string, elementID int, actor *c.User, topic *c.Topic) (out string) { if action == "delete" { return p.GetTmplPhrasef("panel_logs_mod_action_topic_delete", elementID, actor.Link, actor.Name) } @@ -80,7 +80,7 @@ func topicElementTypeAction(action string, elementType string, elementID int, ac return fmt.Sprintf(out, topic.Link, topic.Title, actor.Link, actor.Name) } -func modlogsElementType(action string, elementType string, elementID int, actor *c.User) (out string) { +func modlogsElementType(action, elementType string, elementID int, actor *c.User) (out string) { switch elementType { case "topic": topic := handleUnknownTopic(c.Topics.Get(elementID)) @@ -93,6 +93,18 @@ func modlogsElementType(action string, elementType string, elementID int, actor topic := handleUnknownTopic(c.TopicByReplyID(elementID)) out = p.GetTmplPhrasef("panel_logs_mod_action_reply_delete", topic.Link, topic.Title, actor.Link, actor.Name) } + case "profile-reply": + if action == "delete" { + // TODO: Optimise this + var profile *c.User + profileReply, err := c.Prstore.Get(elementID) + if err != nil { + profile = &c.User{Name: p.GetTmplPhrase("user_unknown"), Link: c.BuildProfileURL("unknown", 0)} + } else { + profile = handleUnknownUser(c.Users.Get(profileReply.ParentID)) + } + out = p.GetTmplPhrasef("panel_logs_mod_action_profile_reply_delete", profile.Link, profile.Name, actor.Link, actor.Name) + } } if out == "" { out = p.GetTmplPhrasef("panel_logs_mod_action_unknown", action, elementType, actor.Link, actor.Name) @@ -100,7 +112,7 @@ func modlogsElementType(action string, elementType string, elementID int, actor return out } -func adminlogsElementType(action string, elementType string, elementID int, actor *c.User, extra string) (out string) { +func adminlogsElementType(action, elementType string, elementID int, actor *c.User, extra string) (out string) { switch elementType { // TODO: Record more detail for this, e.g. which field/s was changed case "user": diff --git a/routes/profile.go b/routes/profile.go index 58a60598..2a13ac2c 100644 --- a/routes/profile.go +++ b/routes/profile.go @@ -20,7 +20,7 @@ var profileStmts ProfileStmts func init() { c.DbInits.Add(func(acc *qgen.Accumulator) error { profileStmts = ProfileStmts{ - getReplies: acc.SimpleLeftJoin("users_replies", "users", "users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group", "users_replies.createdBy = users.uid", "users_replies.uid = ?", "", ""), + getReplies: acc.SimpleLeftJoin("users_replies", "users", "users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group", "users_replies.createdBy=users.uid", "users_replies.uid=?", "", ""), } return acc.FirstError() }) @@ -28,11 +28,10 @@ func init() { // TODO: Remove the View part of the name? func ViewProfile(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { - var err error - var replyCreatedAt time.Time - var replyContent, replyCreatedByName, replyAvatar string - var rid, replyCreatedBy, replyLastEdit, replyLastEditBy, replyGroup int - var replyList []*c.ReplyUser + var reCreatedAt time.Time + var reContent, reCreatedByName, reAvatar string + var rid, reCreatedBy, reLastEdit, reLastEditBy, reGroup int + var reList []*c.ReplyUser // TODO: Do a 301 if it's the wrong username? Do a canonical too? _, pid, err := ParseSEOURL(r.URL.Path[len("/user/"):]) @@ -71,28 +70,24 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user c.User, header *c. defer rows.Close() for rows.Next() { - err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &replyGroup) + err := rows.Scan(&rid, &reContent, &reCreatedBy, &reCreatedAt, &reLastEdit, &reLastEditBy, &reAvatar, &reCreatedByName, &reGroup) if err != nil { return c.InternalError(err, w, r) } - replyLiked := false - replyLikeCount := 0 - ru := &c.ReplyUser{Reply: c.Reply{rid, puser.ID, replyContent, replyCreatedBy, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, 0, "", replyLiked, replyLikeCount, 0, ""}, ContentHtml: c.ParseMessage(replyContent, 0, "", user.ParseSettings), CreatedByName: replyCreatedByName, Avatar: replyAvatar, Level: 0} - ru.Init() - - group, err := c.Groups.Get(ru.Group) + reLiked := false + reLikeCount := 0 + ru := &c.ReplyUser{Reply: c.Reply{rid, puser.ID, reContent, reCreatedBy, reGroup, reCreatedAt, reLastEdit, reLastEditBy, 0, "", reLiked, reLikeCount, 0, ""}, ContentHtml: c.ParseMessage(reContent, 0, "", user.ParseSettings, &user), CreatedByName: reCreatedByName, Avatar: reAvatar, Level: 0} + _, err = ru.Init() if err != nil { return c.InternalError(err, w, r) } - if group.Tag != "" { - ru.Tag = group.Tag - } else if puser.ID == ru.CreatedBy { + if puser.ID == ru.CreatedBy { ru.Tag = phrases.GetTmplPhrase("profile.owner_tag") } // TODO: Add a hook here - replyList = append(replyList, ru) + reList = append(reList, ru) } if err := rows.Err(); err != nil { return c.InternalError(err, w, r) @@ -114,8 +109,8 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user c.User, header *c. } } canMessage := (!blockedInv && user.Perms.UseConvos) || user.IsSuperMod - canComment := !blockedInv && user.Perms.ViewTopic && user.Perms.CreateReply + canComment := !blockedInv && user.Perms.CreateProfileReply - ppage := c.ProfilePage{header, replyList, *puser, currentScore, nextScore, blocked, canMessage, canComment} + ppage := c.ProfilePage{header, reList, *puser, currentScore, nextScore, blocked, canMessage, canComment} return renderTemplate("profile", w, r, header, ppage) } diff --git a/routes/profile_reply.go b/routes/profile_reply.go index c8bafa6f..88a8ebe2 100644 --- a/routes/profile_reply.go +++ b/routes/profile_reply.go @@ -10,7 +10,7 @@ import ( ) func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { - if !user.Perms.ViewTopic || !user.Perms.CreateReply { + if !user.Perms.CreateProfileReply { return c.NoPermissions(w, r, user) } uid, err := strconv.Atoi(r.PostFormValue("uid")) @@ -74,6 +74,9 @@ 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) + } // ? 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) @@ -127,5 +130,10 @@ func ProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user c.Use } else { w.Write(successJSONBytes) } + + err = c.ModLogs.Create("delete", reply.ParentID, "profile-reply", user.GetIP(), user.ID) + if err != nil { + return c.InternalErrorJSQ(err, w, r, js) + } return nil } diff --git a/routes/reply.go b/routes/reply.go index ac8dd5cf..c896def2 100644 --- a/routes/reply.go +++ b/routes/reply.go @@ -194,7 +194,7 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro prid, _ := strconv.Atoi(r.FormValue("prid")) if js && (prid == 0 || rids[0] == prid) { - outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums", user.ParseSettings)}) + outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums", user.ParseSettings, &user)}) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } @@ -267,7 +267,7 @@ func ReplyEditSubmit(w http.ResponseWriter, r *http.Request, user c.User, srid s if !js { http.Redirect(w, r, "/topic/"+strconv.Itoa(topic.ID)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther) } else { - outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums", user.ParseSettings)}) + outBytes, err := json.Marshal(JsonReply{c.ParseMessage(reply.Content, topic.ParentID, "forums", user.ParseSettings, &user)}) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } diff --git a/routes/topic.go b/routes/topic.go index 6ac9531d..07a411b8 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -72,14 +72,7 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He header.Title = topic.Title header.Path = c.BuildTopicURL(c.NameToSlug(topic.Title), topic.ID) - // TODO: Cache ContentHTML when possible? - topic.ContentHTML = c.ParseMessage(topic.Content, topic.ParentID, "forums", user.ParseSettings) - // TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do. - if topic.ContentHTML == topic.Content { - topic.ContentHTML = topic.Content - } topic.ContentLines = strings.Count(topic.Content, "\n") - if len(topic.Content) > 200 { header.OGDesc = topic.Content[:197] + "..." } else { @@ -90,6 +83,22 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He if err != nil { return c.InternalError(err, w, r) } + + var parseSettings *c.ParseSettings + if !postGroup.Perms.AutoEmbed && (user.ParseSettings == nil || !user.ParseSettings.NoEmbed) { + parseSettings = c.DefaultParseSettings.CopyPtr() + parseSettings.NoEmbed = true + } else { + parseSettings = user.ParseSettings + } + + // TODO: Cache ContentHTML when possible? + topic.ContentHTML = c.ParseMessage(topic.Content, topic.ParentID, "forums", parseSettings, &user) + // TODO: Do this more efficiently by avoiding the allocations entirely in ParseMessage, if there's nothing to do. + if topic.ContentHTML == topic.Content { + topic.ContentHTML = topic.Content + } + topic.Tag = postGroup.Tag if postGroup.IsMod { topic.ClassName = c.Config.StaffCSS @@ -604,7 +613,7 @@ func EditTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s if !js { http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) } else { - outBytes, err := json.Marshal(JsonReply{c.ParseMessage(topic.Content, topic.ParentID, "forums", user.ParseSettings)}) + outBytes, err := json.Marshal(JsonReply{c.ParseMessage(topic.Content, topic.ParentID, "forums", user.ParseSettings, &user)}) if err != nil { return c.InternalErrorJSQ(err, w, r, js) } diff --git a/schema/mssql/inserts.sql b/schema/mssql/inserts.sql index 0811face..5be1b651 100644 --- a/schema/mssql/inserts.sql +++ b/schema/mssql/inserts.sql @@ -7,9 +7,9 @@ INSERT INTO [settings] ([name],[content],[type]) VALUES ('rapid_loading','1','bo INSERT INTO [settings] ([name],[content],[type]) VALUES ('google_site_verify','','html-attribute'); INSERT INTO [themes] ([uname],[default]) VALUES ('cosora',1); INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,1); -INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin'); -INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,0,0,'Mod'); -INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Member','{"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"CreateReply":true}','{}',0,0,0,""); +INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin'); +INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,0,0,'Mod'); +INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Member','{"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"CreateReply":true}','{}',0,0,0,""); INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Banned','{"ViewTopic":true}','{}',0,0,1,""); INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Awaiting Activation','{"ViewTopic":true}','{}',0,0,0,""); INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[is_banned],[tag]) VALUES ('Not Loggedin','{"ViewTopic":true}','{}',0,0,0,'Guest'); diff --git a/schema/mysql/inserts.sql b/schema/mysql/inserts.sql index 69af871e..5835a901 100644 --- a/schema/mysql/inserts.sql +++ b/schema/mysql/inserts.sql @@ -15,9 +15,9 @@ INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('rapid_loading','1','boo INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('google_site_verify','','html-attribute'); INSERT INTO `themes`(`uname`,`default`) VALUES ('cosora',1); INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1); -INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin'); -INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,0,0,'Mod'); -INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Member','{"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"CreateReply":true}','{}',0,0,0,""); +INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin'); +INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,0,0,'Mod'); +INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Member','{"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"CreateReply":true}','{}',0,0,0,""); INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Banned','{"ViewTopic":true}','{}',0,0,1,""); INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Awaiting Activation','{"ViewTopic":true}','{}',0,0,0,""); INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`is_banned`,`tag`) VALUES ('Not Loggedin','{"ViewTopic":true}','{}',0,0,0,'Guest'); diff --git a/schema/pgsql/inserts.sql b/schema/pgsql/inserts.sql index 5e32e921..c68a1cb2 100644 --- a/schema/pgsql/inserts.sql +++ b/schema/pgsql/inserts.sql @@ -7,9 +7,9 @@ INSERT INTO "settings"("name","content","type") VALUES ('rapid_loading','1','boo INSERT INTO "settings"("name","content","type") VALUES ('google_site_verify','','html-attribute'); INSERT INTO "themes"("uname","default") VALUES ('cosora',1); INSERT INTO "emails"("email","uid","validated") VALUES ('admin@localhost',1,1); -INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin'); -INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,0,0,'Mod'); -INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Member','{"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"CreateReply":true}','{}',0,0,0,""); +INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,1,0,'Admin'); +INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true,"MoveTopic":true}','{}',1,0,0,'Mod'); +INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Member','{"UploadFiles":true,"UploadAvatars":true,"UseConvos":true,"CreateProfileReply":true,"AutoEmbed":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"CreateReply":true}','{}',0,0,0,""); INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Banned','{"ViewTopic":true}','{}',0,0,1,""); INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Awaiting Activation','{"ViewTopic":true}','{}',0,0,0,""); INSERT INTO "users_groups"("name","permissions","plugin_perms","is_mod","is_admin","is_banned","tag") VALUES ('Not Loggedin','{"ViewTopic":true}','{}',0,0,0,'Guest'); diff --git a/templates/panel_group_edit.html b/templates/panel_group_edit.html index b994458b..9ab98c70 100644 --- a/templates/panel_group_edit.html +++ b/templates/panel_group_edit.html @@ -4,7 +4,7 @@
{{template "panel_before_head.html" . }}
-

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

+

{{.Name}}{{lang "panel_group_head_suffix"}} - #{{.ID}}

diff --git a/templates/panel_group_edit_perms.html b/templates/panel_group_edit_perms.html index 0d9f03af..e59ecab5 100644 --- a/templates/panel_group_edit_perms.html +++ b/templates/panel_group_edit_perms.html @@ -4,7 +4,7 @@
{{template "panel_before_head.html" . }}
-

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

+

{{.Name}}{{lang "panel_group_head_suffix"}} - #{{.ID}}

{{if .CurrentUser.Perms.EditGroupLocalPerms}} @@ -50,6 +50,29 @@
{{end}} + {{if .CurrentUser.Perms.EditGroupGlobalPerms}} +
+

{{lang "panel_group_mod_permissions"}}

+
+
+ {{range .ModPerms}} +
+
+ {{.LangStr}} +
+ +
+
+
+ {{end}} +
+
+
+
+ {{end}}