diff --git a/common/forum_store.go b/common/forum_store.go index 004b1963..f6bac5fc 100644 --- a/common/forum_store.go +++ b/common/forum_store.go @@ -37,6 +37,7 @@ type ForumStore interface { Delete(id int) error AddTopic(tid int, uid int, fid int) error RemoveTopic(fid int) error + RemoveTopics(fid, count int) error UpdateLastTopic(tid int, uid int, fid int) error Exists(id int) bool GetAll() ([]*Forum, error) @@ -45,7 +46,7 @@ type ForumStore interface { GetAllVisibleIDs() ([]int, error) //GetChildren(parentID int, parentType string) ([]*Forum,error) //GetFirstChild(parentID int, parentType string) (*Forum,error) - Create(forumName string, forumDesc string, active bool, preset string) (int, error) + Create(name string, desc string, active bool, preset string) (int, error) UpdateOrder(updateMap map[int]int) error Count() int @@ -53,7 +54,7 @@ type ForumStore interface { type ForumCache interface { CacheGet(id int) (*Forum, error) - CacheSet(forum *Forum) error + CacheSet(f *Forum) error CacheDelete(id int) Length() int } @@ -311,21 +312,18 @@ func (s *MemoryForumStore) AddTopic(tid int, uid int, fid int) error { return s.Reload(fid) } -// TODO: Make this update more atomic -func (s *MemoryForumStore) RemoveTopic(fid int) error { - _, err := s.removeTopics.Exec(1, fid) - if err != nil { - return err - } - +func (s *MemoryForumStore) RefreshTopic(fid int) (err error) { var tid int err = s.lastTopic.QueryRow(fid).Scan(&tid) if err == sql.ErrNoRows { - _, err = s.updateCache.Exec(0, 0, fid) - if err != nil { - return err + f, err := s.CacheGet(fid) + if err != nil || f.LastTopicID != 0 { + _, err = s.updateCache.Exec(0, 0, fid) + if err != nil { + return err + } + s.Reload(fid) } - s.Reload(fid) return nil } if err != nil { @@ -345,6 +343,22 @@ func (s *MemoryForumStore) RemoveTopic(fid int) error { return nil } +// TODO: Make this update more atomic +func (s *MemoryForumStore) RemoveTopic(fid int) error { + _, err := s.removeTopics.Exec(1, fid) + if err != nil { + return err + } + return s.RefreshTopic(fid) +} +func (s *MemoryForumStore) RemoveTopics(fid int, count int) error { + _, err := s.removeTopics.Exec(count, fid) + if err != nil { + return err + } + return s.RefreshTopic(fid) +} + // DEPRECATED. forum.Update() will be the way to do this in the future, once it's completed // TODO: Have a pointer to the last topic rather than storing it on the forum itself func (s *MemoryForumStore) UpdateLastTopic(tid int, uid int, fid int) error { diff --git a/common/page_store.go b/common/page_store.go index 4c0288bd..e51eec19 100644 --- a/common/page_store.go +++ b/common/page_store.go @@ -141,7 +141,7 @@ func (s *DefaultPageStore) GetByName(name string) (*CustomPage, error) { return p, s.parseAllowedGroups(rawAllowedGroups, p) } -func (s *DefaultPageStore) GetOffset(offset int, perPage int) (pages []*CustomPage, err error) { +func (s *DefaultPageStore) GetOffset(offset, perPage int) (pages []*CustomPage, err error) { rows, err := s.getOffset.Query(offset, perPage) if err != nil { return pages, err diff --git a/common/parser.go b/common/parser.go index 8616986b..53f71fa9 100644 --- a/common/parser.go +++ b/common/parser.go @@ -403,7 +403,7 @@ func AddHashLinkType(prefix string, handler func(*strings.Builder, string, *int) hashLinkTypes[prefix[0]] = prefix } -func WriteURL(sb *strings.Builder, url string, label string) { +func WriteURL(sb *strings.Builder, url, label string) { sb.Write(URLOpen) sb.WriteString(url) sb.Write(URLOpen2) diff --git a/common/phrases/phrases.go b/common/phrases/phrases.go index 96e52141..3f80cf43 100644 --- a/common/phrases/phrases.go +++ b/common/phrases/phrases.go @@ -126,7 +126,7 @@ func InitPhrases(lang string) error { langPack.TmplIndicesToPhrases = make([][][]byte, len(langTmplIndicesToNames)) for tmplID, phraseNames := range langTmplIndicesToNames { - var phraseSet = make([][]byte, len(phraseNames)) + phraseSet := make([][]byte, len(phraseNames)) for index, phraseName := range phraseNames { phrase, ok := langPack.TmplPhrases[phraseName] if !ok { diff --git a/common/profile_reply.go b/common/profile_reply.go index e4ac4ce2..387da248 100644 --- a/common/profile_reply.go +++ b/common/profile_reply.go @@ -32,8 +32,8 @@ func init() { DbInits.Add(func(acc *qgen.Accumulator) error { ur := "users_replies" profileReplyStmts = ProfileReplyStmts{ - edit: acc.Update(ur).Set("content = ?, parsed_content = ?").Where("rid = ?").Prepare(), - delete: acc.Delete(ur).Where("rid = ?").Prepare(), + edit: acc.Update(ur).Set("content=?,parsed_content=?").Where("rid=?").Prepare(), + delete: acc.Delete(ur).Where("rid=?").Prepare(), } return acc.FirstError() }) @@ -45,6 +45,7 @@ func BlankProfileReply(id int) *ProfileReply { } // TODO: Write tests for this +// TODO: Remove alerts. func (r *ProfileReply) Delete() error { _, err := profileReplyStmts.delete.Exec(r.ID) return err diff --git a/common/reply.go b/common/reply.go index 0c8441e6..2d0f226c 100644 --- a/common/reply.go +++ b/common/reply.go @@ -67,13 +67,14 @@ type ReplyStmts struct { func init() { DbInits.Add(func(acc *qgen.Accumulator) error { + re := "replies" replyStmts = ReplyStmts{ - isLiked: acc.Select("likes").Columns("targetItem").Where("sentBy = ? and targetItem = ? and targetType='replies'").Prepare(), + isLiked: acc.Select("likes").Columns("targetItem").Where("sentBy=? and targetItem=? and targetType='replies'").Prepare(), createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy").Fields("?,?,?,?").Prepare(), - edit: acc.Update("replies").Set("content = ?, parsed_content = ?").Where("rid = ? AND poll = 0").Prepare(), - setPoll: acc.Update("replies").Set("poll = ?").Where("rid = ? AND poll = 0").Prepare(), - delete: acc.Delete("replies").Where("rid = ?").Prepare(), - addLikesToReply: acc.Update("replies").Set("likeCount = likeCount + ?").Where("rid = ?").Prepare(), + edit: acc.Update(re).Set("content = ?, parsed_content = ?").Where("rid = ? AND poll = 0").Prepare(), + setPoll: acc.Update(re).Set("poll = ?").Where("rid = ? AND poll = 0").Prepare(), + delete: acc.Delete(re).Where("rid = ?").Prepare(), + addLikesToReply: acc.Update(re).Set("likeCount = likeCount + ?").Where("rid = ?").Prepare(), removeRepliesFromTopic: acc.Update("topics").Set("postCount = postCount - ?").Where("tid = ?").Prepare(), } return acc.FirstError() @@ -105,6 +106,8 @@ func (r *Reply) Like(uid int) (err error) { return err } +// TODO: Refresh topic list? +// TODO: Remove alerts. func (r *Reply) Delete() error { _, err := replyStmts.delete.Exec(r.ID) if err != nil { @@ -112,9 +115,9 @@ func (r *Reply) Delete() error { } // TODO: Move this bit to *Topic _, err = replyStmts.removeRepliesFromTopic.Exec(1, r.ParentID) - tcache := Topics.GetCache() - if tcache != nil { - tcache.Remove(r.ParentID) + tc := Topics.GetCache() + if tc != nil { + tc.Remove(r.ParentID) } _ = Rstore.GetCache().Remove(r.ID) return err diff --git a/common/reply_cache.go b/common/reply_cache.go index 4be786a7..7dea426c 100644 --- a/common/reply_cache.go +++ b/common/reply_cache.go @@ -1,7 +1,7 @@ package common import ( - "log" + //"log" "sync" "sync/atomic" ) @@ -90,7 +90,7 @@ func (s *MemoryReplyCache) Set(item *Reply) error { // Add adds a reply to the cache, similar to Set, but it's only intended for new items. This method might be deprecated in the near future, use Set. May return a capacity overflow error. // ? Is this redundant if we have Set? Are the efficiency wins worth this? Is this even used? func (s *MemoryReplyCache) Add(item *Reply) error { - log.Print("MemoryReplyCache.Add") + //log.Print("MemoryReplyCache.Add") s.Lock() if int(s.length) >= s.capacity { s.Unlock() diff --git a/common/templates/templates.go b/common/templates/templates.go index 9a58ca01..9d2a0645 100644 --- a/common/templates/templates.go +++ b/common/templates/templates.go @@ -1172,10 +1172,10 @@ ArgLoop: for i := pos + 2; i < len(node.Args); i++ { op := node.Args[i].String() if op != "" { - if op[0] == '.' || op[0] == '$' { + if /*op[0] == '.' || */op[0] == '$' { panic("langf args cannot be dynamic") } - if op[0] != '"' && !unicode.IsDigit(rune(op[0])) { + if op[0] != '.' && op[0] != '"' && !unicode.IsDigit(rune(op[0])) { break } olist = append(olist, op) @@ -1187,6 +1187,14 @@ ArgLoop: ob := "," for _, op := range olist { + if op[0] == '.' { + param, val3 := c.compileIfVarSub(con, op) + if !val3.IsValid() { + panic("val3 is invalid") + } + ob += param + "," + continue + } allNum := true for _, o := range op { if !unicode.IsDigit(o) { diff --git a/common/theme_list.go b/common/theme_list.go index 687e8c10..7bb5e87b 100644 --- a/common/theme_list.go +++ b/common/theme_list.go @@ -43,10 +43,10 @@ func init() { DbInits.Add(func(acc *qgen.Accumulator) error { t := "themes" themeStmts = ThemeStmts{ - getAll: acc.Select(t).Columns("uname, default").Prepare(), + getAll: acc.Select(t).Columns("uname,default").Prepare(), isDefault: acc.Select(t).Columns("default").Where("uname = ?").Prepare(), update: acc.Update(t).Set("default = ?").Where("uname = ?").Prepare(), - add: acc.Insert(t).Columns("uname, default").Fields("?,?").Prepare(), + add: acc.Insert(t).Columns("uname,default").Fields("?,?").Prepare(), } return acc.FirstError() }) @@ -275,7 +275,7 @@ func ResetTemplateOverrides() { } // CreateThemeTemplate creates a theme template on the current default theme -func CreateThemeTemplate(theme string, name string) { +func CreateThemeTemplate(theme, name string) { Themes[theme].TmplPtr[name] = func(pi Page, w http.ResponseWriter) error { mapping, ok := Themes[DefaultThemeBox.Load().(string)].TemplatesMap[name] if !ok { diff --git a/common/topic.go b/common/topic.go index ec45e348..fa77e1f6 100644 --- a/common/topic.go +++ b/common/topic.go @@ -339,6 +339,7 @@ func (t *Topic) Delete() error { return err } + // TODO: Clear reply cache too _, err = topicStmts.delete.Exec(t.ID) t.cacheRemove() if err != nil { diff --git a/common/user.go b/common/user.go index 34f8f266..d41ea705 100644 --- a/common/user.go +++ b/common/user.go @@ -123,16 +123,20 @@ type UserStmts struct { delete *sql.Stmt setAvatar *sql.Stmt setName *sql.Stmt - incTopics *sql.Stmt - updateLevel *sql.Stmt update *sql.Stmt // TODO: Split these into a sub-struct - incScore *sql.Stmt - incPosts *sql.Stmt - incBigposts *sql.Stmt - incMegaposts *sql.Stmt - incLiked *sql.Stmt + incScore *sql.Stmt + incPosts *sql.Stmt + incBigposts *sql.Stmt + incMegaposts *sql.Stmt + incPostStats *sql.Stmt + incBigpostStats *sql.Stmt + incMegapostStats *sql.Stmt + incLiked *sql.Stmt + incTopics *sql.Stmt + updateLevel *sql.Stmt + resetStats *sql.Stmt decLiked *sql.Stmt updateLastIP *sql.Stmt @@ -141,6 +145,10 @@ type UserStmts struct { setPassword *sql.Stmt scheduleAvatarResize *sql.Stmt + + deletePosts *sql.Stmt + deleteProfilePosts *sql.Stmt + deleteReplyPosts *sql.Stmt } var userStmts UserStmts @@ -155,16 +163,21 @@ func init() { delete: acc.Delete(u).Where(w).Prepare(), setAvatar: acc.Update(u).Set("avatar=?").Where(w).Prepare(), setName: acc.Update(u).Set("name=?").Where(w).Prepare(), - incTopics: acc.SimpleUpdate(u, "topics=topics+?", w), - updateLevel: acc.SimpleUpdate(u, "level=?", w), update: acc.Update(u).Set("name=?,email=?,group=?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this - incScore: acc.Update(u).Set("score=score+?").Where(w).Prepare(), - incPosts: acc.Update(u).Set("posts=posts+?").Where(w).Prepare(), - incBigposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?").Where(w).Prepare(), - incMegaposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?,megaposts=megaposts+?").Where(w).Prepare(), - incLiked: acc.Update(u).Set("liked=liked+?,lastLiked=UTC_TIMESTAMP()").Where(w).Prepare(), - decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(), + incScore: acc.Update(u).Set("score=score+?").Where(w).Prepare(), + incPosts: acc.Update(u).Set("posts=posts+?").Where(w).Prepare(), + incBigposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?").Where(w).Prepare(), + incMegaposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?,megaposts=megaposts+?").Where(w).Prepare(), + incPostStats: acc.Update(u).Set("posts=posts+?,score=score+?,level=?").Where(w).Prepare(), + incBigpostStats: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?,score=score+?,level=?").Where(w).Prepare(), + incMegapostStats: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?,megaposts=megaposts+?,score=score+?,level=?").Where(w).Prepare(), + incTopics: acc.SimpleUpdate(u, "topics=topics+?", w), + updateLevel: acc.SimpleUpdate(u, "level=?", w), + resetStats: acc.Update(u).Set("score=0,posts=0,bigposts=0,megaposts=0,topics=0,level=0").Where(w).Prepare(), + + incLiked: acc.Update(u).Set("liked=liked+?,lastLiked=UTC_TIMESTAMP()").Where(w).Prepare(), + decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(), //recalcLastLiked: acc... updateLastIP: acc.SimpleUpdate(u, "last_ip=?", w), updatePrivacy: acc.Update(u).Set("enable_embeds=?").Where(w).Prepare(), @@ -172,6 +185,10 @@ func init() { setPassword: acc.Update(u).Set("password=?,salt=?").Where(w).Prepare(), scheduleAvatarResize: acc.Insert("users_avatar_queue").Columns("uid").Fields("?").Prepare(), + + deletePosts: acc.Select("topics").Columns("tid,parentID").Where("createdBy=?").Prepare(), + deleteProfilePosts: acc.Select("users_replies").Columns("rid").Where("createdBy=?").Prepare(), + deleteReplyPosts: acc.Select("replies").Columns("rid,tid").Where("createdBy=?").Prepare(), } return acc.FirstError() }) @@ -302,6 +319,107 @@ func (u *User) Delete() error { return nil } +func (u *User) DeletePosts() error { + rows, err := userStmts.deletePosts.Query(u.ID) + if err != nil { + return err + } + defer rows.Close() + defer TopicListThaw.Thaw() + defer u.CacheRemove() + + updatedForums := make(map[int]int) // forum[count] + tc := Topics.GetCache() + for rows.Next() { + var tid, parentID int + err := rows.Scan(&tid, &parentID) + if err != nil { + return err + } + // TODO: Clear reply cache too + _, err = topicStmts.delete.Exec(tid) + if tc != nil { + tc.Remove(tid) + } + if err != nil { + return err + } + updatedForums[parentID] = updatedForums[parentID] + 1 + + _, err = topicStmts.deleteActivitySubs.Exec(tid) + if err != nil { + return err + } + _, err = topicStmts.deleteActivity.Exec(tid) + if err != nil { + return err + } + } + if err = rows.Err(); err != nil { + return err + } + err = u.ResetPostStats() + if err != nil { + return err + } + for fid, count := range updatedForums { + err := Forums.RemoveTopics(fid, count) + if err != nil && err != ErrNoRows { + return err + } + } + + rows, err = userStmts.deleteProfilePosts.Query(u.ID) + if err != nil { + return err + } + defer rows.Close() + + for rows.Next() { + var rid int + err := rows.Scan(&rid) + if err != nil { + return err + } + _, err = profileReplyStmts.delete.Exec(rid) + if err != nil { + return err + } + // TODO: Remove alerts. + } + err = rows.Err() + if err != nil { + return err + } + + rows, err = userStmts.deleteReplyPosts.Query(u.ID) + if err != nil { + return err + } + defer rows.Close() + + rc := Rstore.GetCache() + for rows.Next() { + var rid, tid int + err := rows.Scan(&rid,&tid) + if err != nil { + return err + } + _, err = replyStmts.delete.Exec(rid) + if err != nil { + return err + } + // TODO: Move this bit to *Topic + _, err = replyStmts.removeRepliesFromTopic.Exec(1, tid) + if tc != nil { + tc.Remove(tid) + } + _ = rc.Remove(rid) + // TODO: Remove alerts. + } + return rows.Err() +} + func (u *User) bindStmt(stmt *sql.Stmt, params ...interface{}) (err error) { params = append(params, u.ID) _, err = stmt.Exec(params...) @@ -339,7 +457,7 @@ func (u *User) ChangeGroup(group int) (err error) { } func (u *User) GetIP() string { - spl := strings.Split(u.LastIP,"-") + spl := strings.Split(u.LastIP, "-") return spl[len(spl)-1] } @@ -365,7 +483,6 @@ func (u *User) Update(name, email string, group int) (err error) { } func (u *User) IncreasePostStats(wcount int, topic bool) (err error) { - var mod int baseScore := 1 if topic { _, err = userStmts.incTopics.Exec(1, u.ID) @@ -376,38 +493,28 @@ func (u *User) IncreasePostStats(wcount int, topic bool) (err error) { } settings := SettingBox.Load().(SettingMap) + var mod, level int if wcount >= settings["megapost_min_words"].(int) { - _, err = userStmts.incMegaposts.Exec(1, 1, 1, u.ID) mod = 4 + level = GetLevel(u.Score + baseScore + mod) + _, err = userStmts.incMegapostStats.Exec(1, 1, 1, baseScore+mod, level, u.ID) } else if wcount >= settings["bigpost_min_words"].(int) { - _, err = userStmts.incBigposts.Exec(1, 1, u.ID) mod = 1 + level = GetLevel(u.Score + baseScore + mod) + _, err = userStmts.incBigpostStats.Exec(1, 1, baseScore+mod, level, u.ID) } else { - _, err = userStmts.incPosts.Exec(1, u.ID) + level = GetLevel(u.Score + baseScore + mod) + _, err = userStmts.incPostStats.Exec(1, baseScore+mod, level, u.ID) } if err != nil { return err } - - _, err = userStmts.incScore.Exec(baseScore+mod, u.ID) - if err != nil { - return err - } - //log.Print(u.Score + baseScore + mod) - // TODO: Use a transaction to prevent level desyncs? - level := GetLevel(u.Score + baseScore + mod) - //log.Print(level) - _, err = userStmts.updateLevel.Exec(level, u.ID) - if err != nil { - return err - } err = GroupPromotions.PromoteIfEligible(u, level, u.Posts+1) u.CacheRemove() return err } func (u *User) DecreasePostStats(wcount int, topic bool) (err error) { - var mod int baseScore := -1 if topic { _, err = userStmts.incTopics.Exec(-1, u.ID) @@ -417,26 +524,24 @@ func (u *User) DecreasePostStats(wcount int, topic bool) (err error) { baseScore = -2 } + // TODO: Use a transaction to prevent level desyncs? + var mod int settings := SettingBox.Load().(SettingMap) if wcount >= settings["megapost_min_words"].(int) { - _, err = userStmts.incMegaposts.Exec(-1, -1, -1, u.ID) mod = 4 + _, err = userStmts.incMegapostStats.Exec(-1, -1, -1, baseScore-mod, GetLevel(u.Score-baseScore-mod), u.ID) } else if wcount >= settings["bigpost_min_words"].(int) { - _, err = userStmts.incBigposts.Exec(-1, -1, u.ID) mod = 1 + _, err = userStmts.incBigpostStats.Exec(-1, -1, baseScore-mod, GetLevel(u.Score-baseScore-mod), u.ID) } else { - _, err = userStmts.incPosts.Exec(-1, u.ID) - } - if err != nil { - return err + _, err = userStmts.incPostStats.Exec(-1, baseScore-mod, GetLevel(u.Score-baseScore-mod), u.ID) } + u.CacheRemove() + return err +} - _, err = userStmts.incScore.Exec(baseScore-mod, u.ID) - if err != nil { - return err - } - // TODO: Use a transaction to prevent level desyncs? - _, err = userStmts.updateLevel.Exec(GetLevel(u.Score-baseScore-mod), u.ID) +func (u *User) ResetPostStats() (err error) { + _, err = userStmts.resetStats.Exec(u.ID) u.CacheRemove() return err } diff --git a/common/user_store.go b/common/user_store.go index fb50c71e..e343269b 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -119,7 +119,7 @@ func (s *DefaultUserStore) GetByName(name string) (*User, error) { // TODO: Optimise this, so we don't wind up hitting the database every-time for small gaps // TODO: Make this a little more consistent with DefaultGroupStore's GetRange method -func (s *DefaultUserStore) GetOffset(offset int, perPage int) (users []*User, err error) { +func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*User, err error) { rows, err := s.getOffset.Query(offset, perPage) if err != nil { return users, err diff --git a/common/widgets.go b/common/widgets.go index 1951f65b..8e95eead 100644 --- a/common/widgets.go +++ b/common/widgets.go @@ -276,10 +276,10 @@ func InitWidgets() (fi error) { return nil } -func releaseWidgets(widgets []*Widget) { - for _, widget := range widgets { - if widget.ShutdownFunc != nil { - widget.ShutdownFunc(widget) +func releaseWidgets(ws []*Widget) { + for _, w := range ws { + if w.ShutdownFunc != nil { + w.ShutdownFunc(w) } } } diff --git a/gen_router.go b/gen_router.go index 8bcbd377..715614a0 100644 --- a/gen_router.go +++ b/gen_router.go @@ -148,6 +148,7 @@ var RouteMap = map[string]interface{}{ "routes.UnbanUser": routes.UnbanUser, "routes.ActivateUser": routes.ActivateUser, "routes.IPSearch": routes.IPSearch, + "routes.DeletePostsSubmit": routes.DeletePostsSubmit, "routes.CreateTopicSubmit": routes.CreateTopicSubmit, "routes.EditTopicSubmit": routes.EditTopicSubmit, "routes.DeleteTopicSubmit": routes.DeleteTopicSubmit, @@ -319,48 +320,49 @@ var routeMapEnum = map[string]int{ "routes.UnbanUser": 122, "routes.ActivateUser": 123, "routes.IPSearch": 124, - "routes.CreateTopicSubmit": 125, - "routes.EditTopicSubmit": 126, - "routes.DeleteTopicSubmit": 127, - "routes.StickTopicSubmit": 128, - "routes.UnstickTopicSubmit": 129, - "routes.LockTopicSubmit": 130, - "routes.UnlockTopicSubmit": 131, - "routes.MoveTopicSubmit": 132, - "routes.LikeTopicSubmit": 133, - "routes.AddAttachToTopicSubmit": 134, - "routes.RemoveAttachFromTopicSubmit": 135, - "routes.ViewTopic": 136, - "routes.CreateReplySubmit": 137, - "routes.ReplyEditSubmit": 138, - "routes.ReplyDeleteSubmit": 139, - "routes.ReplyLikeSubmit": 140, - "routes.AddAttachToReplySubmit": 141, - "routes.RemoveAttachFromReplySubmit": 142, - "routes.ProfileReplyCreateSubmit": 143, - "routes.ProfileReplyEditSubmit": 144, - "routes.ProfileReplyDeleteSubmit": 145, - "routes.PollVote": 146, - "routes.PollResults": 147, - "routes.AccountLogin": 148, - "routes.AccountRegister": 149, - "routes.AccountLogout": 150, - "routes.AccountLoginSubmit": 151, - "routes.AccountLoginMFAVerify": 152, - "routes.AccountLoginMFAVerifySubmit": 153, - "routes.AccountRegisterSubmit": 154, - "routes.AccountPasswordReset": 155, - "routes.AccountPasswordResetSubmit": 156, - "routes.AccountPasswordResetToken": 157, - "routes.AccountPasswordResetTokenSubmit": 158, - "routes.DynamicRoute": 159, - "routes.UploadedFile": 160, - "routes.StaticFile": 161, - "routes.RobotsTxt": 162, - "routes.SitemapXml": 163, - "routes.OpenSearchXml": 164, - "routes.BadRoute": 165, - "routes.HTTPSRedirect": 166, + "routes.DeletePostsSubmit": 125, + "routes.CreateTopicSubmit": 126, + "routes.EditTopicSubmit": 127, + "routes.DeleteTopicSubmit": 128, + "routes.StickTopicSubmit": 129, + "routes.UnstickTopicSubmit": 130, + "routes.LockTopicSubmit": 131, + "routes.UnlockTopicSubmit": 132, + "routes.MoveTopicSubmit": 133, + "routes.LikeTopicSubmit": 134, + "routes.AddAttachToTopicSubmit": 135, + "routes.RemoveAttachFromTopicSubmit": 136, + "routes.ViewTopic": 137, + "routes.CreateReplySubmit": 138, + "routes.ReplyEditSubmit": 139, + "routes.ReplyDeleteSubmit": 140, + "routes.ReplyLikeSubmit": 141, + "routes.AddAttachToReplySubmit": 142, + "routes.RemoveAttachFromReplySubmit": 143, + "routes.ProfileReplyCreateSubmit": 144, + "routes.ProfileReplyEditSubmit": 145, + "routes.ProfileReplyDeleteSubmit": 146, + "routes.PollVote": 147, + "routes.PollResults": 148, + "routes.AccountLogin": 149, + "routes.AccountRegister": 150, + "routes.AccountLogout": 151, + "routes.AccountLoginSubmit": 152, + "routes.AccountLoginMFAVerify": 153, + "routes.AccountLoginMFAVerifySubmit": 154, + "routes.AccountRegisterSubmit": 155, + "routes.AccountPasswordReset": 156, + "routes.AccountPasswordResetSubmit": 157, + "routes.AccountPasswordResetToken": 158, + "routes.AccountPasswordResetTokenSubmit": 159, + "routes.DynamicRoute": 160, + "routes.UploadedFile": 161, + "routes.StaticFile": 162, + "routes.RobotsTxt": 163, + "routes.SitemapXml": 164, + "routes.OpenSearchXml": 165, + "routes.BadRoute": 166, + "routes.HTTPSRedirect": 167, } var reverseRouteMapEnum = map[int]string{ 0: "routes.Overview", @@ -488,48 +490,49 @@ var reverseRouteMapEnum = map[int]string{ 122: "routes.UnbanUser", 123: "routes.ActivateUser", 124: "routes.IPSearch", - 125: "routes.CreateTopicSubmit", - 126: "routes.EditTopicSubmit", - 127: "routes.DeleteTopicSubmit", - 128: "routes.StickTopicSubmit", - 129: "routes.UnstickTopicSubmit", - 130: "routes.LockTopicSubmit", - 131: "routes.UnlockTopicSubmit", - 132: "routes.MoveTopicSubmit", - 133: "routes.LikeTopicSubmit", - 134: "routes.AddAttachToTopicSubmit", - 135: "routes.RemoveAttachFromTopicSubmit", - 136: "routes.ViewTopic", - 137: "routes.CreateReplySubmit", - 138: "routes.ReplyEditSubmit", - 139: "routes.ReplyDeleteSubmit", - 140: "routes.ReplyLikeSubmit", - 141: "routes.AddAttachToReplySubmit", - 142: "routes.RemoveAttachFromReplySubmit", - 143: "routes.ProfileReplyCreateSubmit", - 144: "routes.ProfileReplyEditSubmit", - 145: "routes.ProfileReplyDeleteSubmit", - 146: "routes.PollVote", - 147: "routes.PollResults", - 148: "routes.AccountLogin", - 149: "routes.AccountRegister", - 150: "routes.AccountLogout", - 151: "routes.AccountLoginSubmit", - 152: "routes.AccountLoginMFAVerify", - 153: "routes.AccountLoginMFAVerifySubmit", - 154: "routes.AccountRegisterSubmit", - 155: "routes.AccountPasswordReset", - 156: "routes.AccountPasswordResetSubmit", - 157: "routes.AccountPasswordResetToken", - 158: "routes.AccountPasswordResetTokenSubmit", - 159: "routes.DynamicRoute", - 160: "routes.UploadedFile", - 161: "routes.StaticFile", - 162: "routes.RobotsTxt", - 163: "routes.SitemapXml", - 164: "routes.OpenSearchXml", - 165: "routes.BadRoute", - 166: "routes.HTTPSRedirect", + 125: "routes.DeletePostsSubmit", + 126: "routes.CreateTopicSubmit", + 127: "routes.EditTopicSubmit", + 128: "routes.DeleteTopicSubmit", + 129: "routes.StickTopicSubmit", + 130: "routes.UnstickTopicSubmit", + 131: "routes.LockTopicSubmit", + 132: "routes.UnlockTopicSubmit", + 133: "routes.MoveTopicSubmit", + 134: "routes.LikeTopicSubmit", + 135: "routes.AddAttachToTopicSubmit", + 136: "routes.RemoveAttachFromTopicSubmit", + 137: "routes.ViewTopic", + 138: "routes.CreateReplySubmit", + 139: "routes.ReplyEditSubmit", + 140: "routes.ReplyDeleteSubmit", + 141: "routes.ReplyLikeSubmit", + 142: "routes.AddAttachToReplySubmit", + 143: "routes.RemoveAttachFromReplySubmit", + 144: "routes.ProfileReplyCreateSubmit", + 145: "routes.ProfileReplyEditSubmit", + 146: "routes.ProfileReplyDeleteSubmit", + 147: "routes.PollVote", + 148: "routes.PollResults", + 149: "routes.AccountLogin", + 150: "routes.AccountRegister", + 151: "routes.AccountLogout", + 152: "routes.AccountLoginSubmit", + 153: "routes.AccountLoginMFAVerify", + 154: "routes.AccountLoginMFAVerifySubmit", + 155: "routes.AccountRegisterSubmit", + 156: "routes.AccountPasswordReset", + 157: "routes.AccountPasswordResetSubmit", + 158: "routes.AccountPasswordResetToken", + 159: "routes.AccountPasswordResetTokenSubmit", + 160: "routes.DynamicRoute", + 161: "routes.UploadedFile", + 162: "routes.StaticFile", + 163: "routes.RobotsTxt", + 164: "routes.SitemapXml", + 165: "routes.OpenSearchXml", + 166: "routes.BadRoute", + 167: "routes.HTTPSRedirect", } var osMapEnum = map[string]int{ "unknown": 0, @@ -687,7 +690,7 @@ type HTTPSRedirect struct {} func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.Header().Set("Connection", "close") - co.RouteViewCounter.Bump(166) + co.RouteViewCounter.Bump(167) dest := "https://" + req.Host + req.URL.String() http.Redirect(w, req, dest, http.StatusTemporaryRedirect) } @@ -895,7 +898,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { co.GlobalViewCounter.Bump() if prefix == "/s" { //old prefix: /static - co.RouteViewCounter.Bump(161) + co.RouteViewCounter.Bump(162) req.URL.Path += extraData routes.StaticFile(w, req) return @@ -2132,6 +2135,19 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } err = routes.IPSearch(w,req,user,head) + case "/users/delete-posts/submit/": + err = c.NoSessionMismatch(w,req,user) + if err != nil { + return err + } + + err = c.MemberOnly(w,req,user) + if err != nil { + return err + } + + co.RouteViewCounter.Bump(125) + err = routes.DeletePostsSubmit(w,req,user,extraData) } case "/topic": switch(req.URL.Path) { @@ -2150,7 +2166,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(125) + co.RouteViewCounter.Bump(126) err = routes.CreateTopicSubmit(w,req,user) case "/topic/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2163,7 +2179,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(126) + co.RouteViewCounter.Bump(127) err = routes.EditTopicSubmit(w,req,user,extraData) case "/topic/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2177,7 +2193,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - co.RouteViewCounter.Bump(127) + co.RouteViewCounter.Bump(128) err = routes.DeleteTopicSubmit(w,req,user) case "/topic/stick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2190,7 +2206,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(128) + co.RouteViewCounter.Bump(129) err = routes.StickTopicSubmit(w,req,user,extraData) case "/topic/unstick/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2203,7 +2219,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(129) + co.RouteViewCounter.Bump(130) err = routes.UnstickTopicSubmit(w,req,user,extraData) case "/topic/lock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2217,7 +2233,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } req.URL.Path += extraData - co.RouteViewCounter.Bump(130) + co.RouteViewCounter.Bump(131) err = routes.LockTopicSubmit(w,req,user) case "/topic/unlock/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2230,7 +2246,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(131) + co.RouteViewCounter.Bump(132) err = routes.UnlockTopicSubmit(w,req,user,extraData) case "/topic/move/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2243,7 +2259,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(132) + co.RouteViewCounter.Bump(133) err = routes.MoveTopicSubmit(w,req,user,extraData) case "/topic/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2256,7 +2272,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(133) + co.RouteViewCounter.Bump(134) err = routes.LikeTopicSubmit(w,req,user,extraData) case "/topic/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2273,7 +2289,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(134) + co.RouteViewCounter.Bump(135) err = routes.AddAttachToTopicSubmit(w,req,user,extraData) case "/topic/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2286,10 +2302,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(135) + co.RouteViewCounter.Bump(136) err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData) default: - co.RouteViewCounter.Bump(136) + co.RouteViewCounter.Bump(137) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2313,7 +2329,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(137) + co.RouteViewCounter.Bump(138) err = routes.CreateReplySubmit(w,req,user) case "/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2326,7 +2342,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(138) + co.RouteViewCounter.Bump(139) err = routes.ReplyEditSubmit(w,req,user,extraData) case "/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2339,7 +2355,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(139) + co.RouteViewCounter.Bump(140) err = routes.ReplyDeleteSubmit(w,req,user,extraData) case "/reply/like/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2352,7 +2368,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(140) + co.RouteViewCounter.Bump(141) err = routes.ReplyLikeSubmit(w,req,user,extraData) case "/reply/attach/add/submit/": err = c.MemberOnly(w,req,user) @@ -2369,7 +2385,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(141) + co.RouteViewCounter.Bump(142) err = routes.AddAttachToReplySubmit(w,req,user,extraData) case "/reply/attach/remove/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2382,7 +2398,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(142) + co.RouteViewCounter.Bump(143) err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData) } case "/profile": @@ -2398,7 +2414,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(143) + co.RouteViewCounter.Bump(144) err = routes.ProfileReplyCreateSubmit(w,req,user) case "/profile/reply/edit/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2411,7 +2427,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(144) + co.RouteViewCounter.Bump(145) err = routes.ProfileReplyEditSubmit(w,req,user,extraData) case "/profile/reply/delete/submit/": err = c.NoSessionMismatch(w,req,user) @@ -2424,7 +2440,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(145) + co.RouteViewCounter.Bump(146) err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData) } case "/poll": @@ -2440,23 +2456,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(146) + co.RouteViewCounter.Bump(147) err = routes.PollVote(w,req,user,extraData) case "/poll/results/": - co.RouteViewCounter.Bump(147) + co.RouteViewCounter.Bump(148) err = routes.PollResults(w,req,user,extraData) } case "/accounts": switch(req.URL.Path) { case "/accounts/login/": - co.RouteViewCounter.Bump(148) + co.RouteViewCounter.Bump(149) head, err := c.UserCheck(w,req,&user) if err != nil { return err } err = routes.AccountLogin(w,req,user,head) case "/accounts/create/": - co.RouteViewCounter.Bump(149) + co.RouteViewCounter.Bump(150) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2473,7 +2489,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(150) + co.RouteViewCounter.Bump(151) err = routes.AccountLogout(w,req,user) case "/accounts/login/submit/": err = c.ParseForm(w,req,user) @@ -2481,10 +2497,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(151) + co.RouteViewCounter.Bump(152) err = routes.AccountLoginSubmit(w,req,user) case "/accounts/mfa_verify/": - co.RouteViewCounter.Bump(152) + co.RouteViewCounter.Bump(153) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2496,7 +2512,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(153) + co.RouteViewCounter.Bump(154) err = routes.AccountLoginMFAVerifySubmit(w,req,user) case "/accounts/create/submit/": err = c.ParseForm(w,req,user) @@ -2504,10 +2520,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(154) + co.RouteViewCounter.Bump(155) err = routes.AccountRegisterSubmit(w,req,user) case "/accounts/password-reset/": - co.RouteViewCounter.Bump(155) + co.RouteViewCounter.Bump(156) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2519,10 +2535,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(156) + co.RouteViewCounter.Bump(157) err = routes.AccountPasswordResetSubmit(w,req,user) case "/accounts/password-reset/token/": - co.RouteViewCounter.Bump(157) + co.RouteViewCounter.Bump(158) head, err := c.UserCheck(w,req,&user) if err != nil { return err @@ -2534,7 +2550,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c return err } - co.RouteViewCounter.Bump(158) + co.RouteViewCounter.Bump(159) err = routes.AccountPasswordResetTokenSubmit(w,req,user) } /*case "/sitemaps": // TODO: Count these views @@ -2551,7 +2567,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c h.Del("Content-Type") h.Del("Content-Encoding") } - co.RouteViewCounter.Bump(160) + co.RouteViewCounter.Bump(161) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? r.UploadHandler(w,req) // TODO: Count these views @@ -2561,7 +2577,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c // TODO: Add support for favicons and robots.txt files switch(extraData) { case "robots.txt": - co.RouteViewCounter.Bump(162) + co.RouteViewCounter.Bump(163) return routes.RobotsTxt(w,req) case "favicon.ico": gzw, ok := w.(c.GzipResponseWriter) @@ -2575,10 +2591,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c routes.StaticFile(w,req) return nil case "opensearch.xml": - co.RouteViewCounter.Bump(164) + co.RouteViewCounter.Bump(165) return routes.OpenSearchXml(w,req) /*case "sitemap.xml": - co.RouteViewCounter.Bump(163) + co.RouteViewCounter.Bump(164) return routes.SitemapXml(w,req)*/ } return c.NotFound(w,req,nil) @@ -2589,7 +2605,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c r.RUnlock() if ok { - co.RouteViewCounter.Bump(159) // TODO: Be more specific about *which* dynamic route it is + co.RouteViewCounter.Bump(160) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData return handle(w,req,user) } @@ -2600,7 +2616,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c } else { r.DumpRequest(req,"Bad Route") } - co.RouteViewCounter.Bump(165) + co.RouteViewCounter.Bump(166) return c.NotFound(w,req,nil) } return err diff --git a/langs/english.json b/langs/english.json index 38d32132..321e700b 100644 --- a/langs/english.json +++ b/langs/english.json @@ -720,33 +720,38 @@ "paginator.next_page":"›", "paginator.next_page_aria":"Go to the next page", - "profile_login_for_options":"Login for options", - "profile_send_message":"Send Message", - "profile_add_friend":"Add Friend", - "profile_unban":"Unban", - "profile_ban":"Ban", - "profile_block":"Block", - "profile_unblock":"Unblock", - "profile_report_user_tooltip":"Report User", - "profile_report_user_aria":"Report User", - "profile_ban_user_head":"Ban User", - "profile_ban_user_notice":"If all the fields are left blank, the ban will be permanent.", - "profile_ban_user_days":"Days", - "profile_ban_user_weeks":"Weeks", - "profile_ban_user_months":"Months", - "profile_ban_user_reason":"Reason", - "profile_ban_user_button":"Ban User", - "profile_comments_head":"Comments", - "profile_comments_edit_tooltip":"Edit Item", - "profile_comments_edit_aria":"Edit Item", - "profile_comments_delete_tooltip":"Delete Item", - "profile_comments_delete_aria":"Delete Item", - "profile_comments_report_tooltip":"Report Item", - "profile_comments_report_aria":"Report Item", - "profile_comments_form_content":"Insert comment here", - "profile_comments_form_button":"Create Reply", - "profile_comments_form_guest":"You need to login to comment on this profile.", - "profile_owner_tag":"Profile Owner", + "profile.login_for_options":"Login for options", + "profile.send_message":"Send Message", + "profile.add_friend":"Add Friend", + "profile.unban":"Unban", + "profile.ban":"Ban", + "profile.delete_posts":"Delete Posts", + "profile.block":"Block", + "profile.unblock":"Unblock", + "profile.report_user_tooltip":"Report User", + "profile.report_user_aria":"Report User", + "profile.ban_user_head":"Ban User", + "profile.ban_user_notice":"If all the fields are left blank, the ban will be permanent.", + "profile.ban_user_days":"Days", + "profile.ban_user_weeks":"Weeks", + "profile.ban_user_months":"Months", + "profile.ban_user_reason":"Reason", + "profile.ban_user_button":"Ban User", + "profile.ban_delete_posts":"Delete Posts", + "profile.delete_posts_head":"Delete Posts", + "profile.delete_posts_notice":"Would you like to delete %d posts?", + "profile.delete_posts_button":"Delete Posts", + "profile.comments_head":"Comments", + "profile.comments_edit_tooltip":"Edit Item", + "profile.comments_edit_aria":"Edit Item", + "profile.comments_delete_tooltip":"Delete Item", + "profile.comments_delete_aria":"Delete Item", + "profile.comments_report_tooltip":"Report Item", + "profile.comments_report_aria":"Report Item", + "profile.comments_form_content":"Insert comment here", + "profile.comments_form_button":"Create Reply", + "profile.comments_form_guest":"You need to login to comment on this profile.", + "profile.owner_tag":"Profile Owner", "ip_search_head":"IP Search", "ip_search_search_button":"Search", diff --git a/public/profile_member.js b/public/profile_member.js index a6e03099..07ec2ca0 100644 --- a/public/profile_member.js +++ b/public/profile_member.js @@ -4,6 +4,9 @@ function handle_profile_hashbit() { case "ban_user": hash_class = "ban_user_hash"; break; + case "delete_posts": + hash_class = "delete_posts_hash"; + break; default: console.log("Unknown hashbit"); return; diff --git a/query_gen/utils.go b/query_gen/utils.go index d47e16cf..91b71137 100644 --- a/query_gen/utils.go +++ b/query_gen/utils.go @@ -505,7 +505,7 @@ func skipFunctionCall(data string, index int) int { return index } -func writeFile(name string, content string) (err error) { +func writeFile(name, content string) (err error) { f, err := os.Create(name) if err != nil { return err diff --git a/router_gen/routes.go b/router_gen/routes.go index b3fd1772..aa3c4b58 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -111,6 +111,7 @@ func usersRoutes() *RouteGroup { Action("routes.UnbanUser", "/users/unban/", "extraData"), Action("routes.ActivateUser", "/users/activate/", "extraData"), MView("routes.IPSearch", "/users/ips/"), // TODO: .Perms("ViewIPs")? + Action("routes.DeletePostsSubmit", "/users/delete-posts/submit/", "extraData"), ) } diff --git a/routes/profile.go b/routes/profile.go index cbea1541..58a60598 100644 --- a/routes/profile.go +++ b/routes/profile.go @@ -88,7 +88,7 @@ func ViewProfile(w http.ResponseWriter, r *http.Request, user c.User, header *c. if group.Tag != "" { ru.Tag = group.Tag } else if puser.ID == ru.CreatedBy { - ru.Tag = phrases.GetTmplPhrase("profile_owner_tag") + ru.Tag = phrases.GetTmplPhrase("profile.owner_tag") } // TODO: Add a hook here diff --git a/routes/user.go b/routes/user.go index f38e06e6..dfc9ffca 100644 --- a/routes/user.go +++ b/routes/user.go @@ -13,7 +13,6 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid str if !user.Perms.BanUsers { return c.NoPermissions(w, r, user) } - uid, err := strconv.Atoi(suid) if err != nil { return c.LocalError("The provided UserID is not a valid number.", w, r, user) @@ -28,7 +27,6 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid str } else if err != nil { return c.InternalError(err, w, r) } - // TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one? if targetUser.IsMod { return c.LocalError("You may not ban another staff member.", w, r, user) @@ -40,44 +38,59 @@ func BanUserSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid str return c.LocalError("The user you're trying to unban is already banned.", w, r, user) } - durationDays, err := strconv.Atoi(r.FormValue("ban-duration-days")) + durDays, err := strconv.Atoi(r.FormValue("dur-days")) if err != nil { return c.LocalError("You can only use whole numbers for the number of days", w, r, user) } - - durationWeeks, err := strconv.Atoi(r.FormValue("ban-duration-weeks")) + durWeeks, err := strconv.Atoi(r.FormValue("dur-weeks")) if err != nil { return c.LocalError("You can only use whole numbers for the number of weeks", w, r, user) } - - durationMonths, err := strconv.Atoi(r.FormValue("ban-duration-months")) + durMonths, err := strconv.Atoi(r.FormValue("dur-months")) if err != nil { return c.LocalError("You can only use whole numbers for the number of months", w, r, user) } - - var duration time.Duration - if durationDays > 1 && durationWeeks > 1 && durationMonths > 1 { - duration, _ = time.ParseDuration("0") - } else { - var seconds int - seconds += durationDays * int(c.Day) - seconds += durationWeeks * int(c.Week) - seconds += durationMonths * int(c.Month) - duration, _ = time.ParseDuration(strconv.Itoa(seconds) + "s") + deletePosts := false + switch r.FormValue("delete-posts") { + case "1": + deletePosts = true } - err = targetUser.Ban(duration, user.ID) + var dur time.Duration + if durDays > 1 && durWeeks > 1 && durMonths > 1 { + dur, _ = time.ParseDuration("0") + } else { + var secs int + secs += durDays * int(c.Day) + secs += durWeeks * int(c.Week) + secs += durMonths * int(c.Month) + dur, _ = time.ParseDuration(strconv.Itoa(secs) + "s") + } + + err = targetUser.Ban(dur, user.ID) if err == sql.ErrNoRows { return c.LocalError("The user you're trying to ban no longer exists.", w, r, user) } else if err != nil { return c.InternalError(err, w, r) } - err = c.ModLogs.Create("ban", uid, "user", user.GetIP(), user.ID) if err != nil { return c.InternalError(err, w, r) } + if deletePosts { + err = targetUser.DeletePosts() + if err == sql.ErrNoRows { + return c.LocalError("The user you're trying to ban no longer exists.", w, r, user) + } else if err != nil { + return c.InternalError(err, w, r) + } + err = c.ModLogs.Create("delete-posts", uid, "user", user.GetIP(), user.ID) + if err != nil { + return c.InternalError(err, w, r) + } + } + // TODO: Trickle the hookTable down from the router hTbl := c.GetHookTable() skip, rerr := hTbl.VhookSkippable("action_end_ban_user", targetUser.ID, &user) @@ -93,7 +106,6 @@ func UnbanUser(w http.ResponseWriter, r *http.Request, user c.User, suid string) if !user.Perms.BanUsers { return c.NoPermissions(w, r, user) } - uid, err := strconv.Atoi(suid) if err != nil { return c.LocalError("The provided UserID is not a valid number.", w, r, user) @@ -139,7 +151,6 @@ func ActivateUser(w http.ResponseWriter, r *http.Request, user c.User, suid stri if !user.Perms.ActivateUsers { return c.NoPermissions(w, r, user) } - uid, err := strconv.Atoi(suid) if err != nil { return c.LocalError("The provided UserID is not a valid number.", w, r, user) @@ -175,3 +186,45 @@ func ActivateUser(w http.ResponseWriter, r *http.Request, user c.User, suid stri http.Redirect(w, r, "/user/"+strconv.Itoa(targetUser.ID), http.StatusSeeOther) return nil } + +func DeletePostsSubmit(w http.ResponseWriter, r *http.Request, user c.User, suid string) c.RouteError { + if !user.Perms.BanUsers { + return c.NoPermissions(w, r, user) + } + uid, err := strconv.Atoi(suid) + if err != nil { + return c.LocalError("The provided UserID is not a valid number.", w, r, user) + } + + targetUser, err := c.Users.Get(uid) + if err == sql.ErrNoRows { + return c.LocalError("The user you're trying to purge posts of no longer exists.", w, r, user) + } else if err != nil { + return c.InternalError(err, w, r) + } + // TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one? + if targetUser.IsMod { + return c.LocalError("You may not purge the posts of another staff member.", w, r, user) + } + + err = targetUser.DeletePosts() + if err == sql.ErrNoRows { + return c.LocalError("The user you're trying to purge posts of no longer exists.", w, r, user) + } else if err != nil { + return c.InternalError(err, w, r) + } + err = c.ModLogs.Create("delete-posts", uid, "user", user.GetIP(), user.ID) + if err != nil { + return c.InternalError(err, w, r) + } + + // TODO: Trickle the hookTable down from the router + hTbl := c.GetHookTable() + skip, rerr := hTbl.VhookSkippable("action_end_delete_posts", targetUser.ID, &user) + if skip || rerr != nil { + return rerr + } + + http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther) + return nil +} diff --git a/templates/convo.html b/templates/convo.html index fcec6396..032c9fab 100644 --- a/templates/convo.html +++ b/templates/convo.html @@ -14,10 +14,10 @@
diff --git a/templates/convo_row.html b/templates/convo_row.html index cbc31d53..ebb40268 100644 --- a/templates/convo_row.html +++ b/templates/convo_row.html @@ -6,11 +6,11 @@ {{.User.Name}} - {{if .CanModify}} + {{if .CanModify}} - {{end}} + {{end}} - + {{if .User.Tag}}{{.User.Tag}}{{end}} diff --git a/templates/convo_row_alt.html b/templates/convo_row_alt.html index 064b9b56..e2c91503 100644 --- a/templates/convo_row_alt.html +++ b/templates/convo_row_alt.html @@ -10,12 +10,12 @@ {{if .CanModify}} - + - + {{end}} - +