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}} + + + {{lang "option_yes"}} + {{lang "option_no"}} + + + + + {{end}} + + {{lang "panel_group_update_button"}} + + + {{end}}