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.
This commit is contained in:
parent
2178efb8f0
commit
0c1d6f0516
|
@ -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}
|
||||
|
|
|
@ -594,6 +594,7 @@ type PanelEditGroupPermsPage struct {
|
|||
Name string
|
||||
LocalPerms []NameLangToggle
|
||||
GlobalPerms []NameLangToggle
|
||||
ModPerms []NameLangToggle
|
||||
}
|
||||
|
||||
type GroupPromotionExtend struct {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -40,6 +40,8 @@ var GlobalPermList = []string{
|
|||
"UploadFiles",
|
||||
"UploadAvatars",
|
||||
"UseConvos",
|
||||
"CreateProfileReply",
|
||||
"AutoEmbed",
|
||||
}
|
||||
|
||||
// Permission Structure: ActionComponent[Subcomponent]Flag
|
||||
|
@ -69,6 +71,8 @@ type Perms struct {
|
|||
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"`
|
||||
|
@ -125,6 +129,8 @@ func init() {
|
|||
UploadFiles: true,
|
||||
UploadAvatars: true,
|
||||
UseConvos: true,
|
||||
CreateProfileReply: true,
|
||||
AutoEmbed: true,
|
||||
|
||||
ViewTopic: true,
|
||||
LikeItem: true,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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":"<a href='%s'>%s</a> was moved to <a href='%s'>%s</a> by <a href='%s'>%s</a>",
|
||||
"panel_logs_mod_action_topic_unknown":"Unknown action '%s' on elementType '%s' by <a href='%s'>%s</a>",
|
||||
"panel_logs_mod_action_reply_delete":"A reply in <a href='%s'>%s</a> was deleted by <a href='%s'>%s</a>",
|
||||
"panel_logs_mod_action_profile_reply_delete":"A reply on <a href='%s'>%s</a>'s profile was deleted by <a href='%s'>%s</a>",
|
||||
"panel_logs_mod_action_user_ban":"<a href='%s'>%s</a> was banned by <a href='%s'>%s</a>",
|
||||
"panel_logs_mod_action_user_unban":"<a href='%s'>%s</a> was unbanned by <a href='%s'>%s</a>",
|
||||
"panel_logs_mod_action_user_delete-posts":"<a href='%s'>%s</a> had their posts purged by <a href='%s'>%s</a>",
|
||||
|
|
68
misc_test.go
68
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()
|
||||
|
|
|
@ -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", "<a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a><br>")
|
||||
l.Add("//"+c.Site.URL+"\n//"+c.Site.URL, "<a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a><br><a href='https://"+c.Site.URL+"'>"+c.Site.URL+"</a>")
|
||||
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 := "<a href='/topic/1'>#nnid-1</a>"
|
||||
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 = "<a href='/topic/1'>#longidnameneedtooverflowhack-1</a>"
|
||||
if res != expect {
|
||||
t.Error("Bad output:", "'"+res+"'")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}}</h1></div>
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}} - #{{.ID}}</h1></div>
|
||||
</div>
|
||||
<div id="panel_group" class="colstack_item the_form">
|
||||
<form action="/panel/groups/edit/submit/{{.ID}}?s={{.CurrentUser.Session}}" method="post">
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<main class="colstack_right">
|
||||
{{template "panel_before_head.html" . }}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}}</h1></div>
|
||||
<div class="rowitem"><h1>{{.Name}}{{lang "panel_group_head_suffix"}} - #{{.ID}}</h1></div>
|
||||
</div>
|
||||
<form action="/panel/groups/edit/perms/submit/{{.ID}}?s={{.CurrentUser.Session}}" method="post">
|
||||
{{if .CurrentUser.Perms.EditGroupLocalPerms}}
|
||||
|
@ -50,6 +50,29 @@
|
|||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .CurrentUser.Perms.EditGroupGlobalPerms}}
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>{{lang "panel_group_mod_permissions"}}</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item rowlist formlist the_form panel_group_perms">
|
||||
{{range .ModPerms}}
|
||||
<div class="formrow">
|
||||
<div class="formitem">
|
||||
<a>{{.LangStr}}</a>
|
||||
<div class="to_right">
|
||||
<select name="perm-{{.Name}}">
|
||||
<option{{if .Toggle}} selected{{end}} value=1>{{lang "option_yes"}}</option>
|
||||
<option{{if not .Toggle}} selected{{end}} value=0>{{lang "option_no"}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="panel-button" class="formbutton form_middle_button">{{lang "panel_group_update_button"}}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue