Add option to delete all of a user's posts.
Add limited dynanicism to langf. Eliminate a few queries when updating user stats. Minor clean-up. Namespace the profile language strings.
This commit is contained in:
parent
a569772e9c
commit
69a2430e5a
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
197
common/user.go
197
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
268
gen_router.go
268
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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
<form action="/user/convo/create/submit/{{.Convo.ID}}?s={{.CurrentUser.Session}}" method="post">
|
||||
<div class="colstack_item topic_reply_form" style="border-top:none;">
|
||||
<div class="formrow">
|
||||
<div class="formitem"><textarea class="input_content" name="content" placeholder="{{lang "profile_comments_form_content"}}"></textarea></div>
|
||||
<div class="formitem"><textarea class="input_content" name="content" placeholder="{{lang "profile.comments_form_content"}}"></textarea></div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">{{lang "profile_comments_form_button"}}</button></div>
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">{{lang "profile.comments_form_button"}}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<span class="controls">
|
||||
<a href="{{.User.Link}}" class="real_username username">{{.User.Name}}</a>
|
||||
|
||||
{{if .CanModify}}<a href="/user/convo/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
{{if .CanModify}}<a href="/user/convo/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_edit_tooltip"}}" aria-label="{{lang "profile.comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
|
||||
<a href="/user/convo/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
|
||||
<a href="/user/convo/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_delete_tooltip"}}" aria-label="{{lang "profile.comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
|
||||
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=convo-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=convo-reply"><button class="username report_item flag_label" title="{{lang "profile.comments_report_tooltip"}}" aria-label="{{lang "profile.comments_report_aria"}}"></button></a>
|
||||
|
||||
{{if .User.Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.User.Tag}}</a>{{end}}
|
||||
</span>
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
</div>
|
||||
<span class="controls">
|
||||
{{if .CanModify}}
|
||||
<a href="/user/convo/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
<a href="/user/convo/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_edit_tooltip"}}" aria-label="{{lang "profile.comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
|
||||
<a href="/user/convo/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
|
||||
<a href="/user/convo/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_delete_tooltip"}}" aria-label="{{lang "profile.comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
|
||||
{{end}}
|
||||
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=convo-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=convo-reply"><button class="username report_item flag_label" title="{{lang "profile.comments_report_tooltip"}}" aria-label="{{lang "profile.comments_report_aria"}}"></button></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="content_column">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div id="profile_left_pane" class="rowmenu">
|
||||
<div class="topBlock">
|
||||
<div class="rowitem avatarRow">
|
||||
<a href="{{.ProfileOwner.Avatar}}"><img src="{{.ProfileOwner.Avatar}}" class="avatar" alt="Avatar" title="{{.ProfileOwner.Name}}'s Avatar" aria-hidden="true" /></a>
|
||||
<a href="{{.ProfileOwner.Avatar}}"><img src="{{.ProfileOwner.Avatar}}" class="avatar" alt="Avatar" title="{{.ProfileOwner.Name}}'s Avatar" aria-hidden="true"/></a>
|
||||
</div>
|
||||
<div class="rowitem nameRow">
|
||||
<span class="profileName" title="{{.ProfileOwner.Name}}">{{.ProfileOwner.Name}}</span>{{if .ProfileOwner.Tag}}<span class="username" title="{{.ProfileOwner.Tag}}">{{.ProfileOwner.Tag}}</span>{{end}}
|
||||
|
@ -25,23 +25,29 @@
|
|||
</div>
|
||||
<div class="passiveBlock">
|
||||
{{if not .CurrentUser.Loggedin}}<div class="rowitem passive">
|
||||
<a class="profile_menu_item">{{lang "profile_login_for_options"}}</a>
|
||||
<a class="profile_menu_item">{{lang "profile.login_for_options"}}</a>
|
||||
</div>{{else}}
|
||||
{{if .CanMessage}}<div class="rowitem passive">
|
||||
<a href="/user/convos/create/" class="profile_menu_item">{{lang "profile_send_message"}}</a>
|
||||
<a href="/user/convos/create/" class="profile_menu_item">{{lang "profile.send_message"}}</a>
|
||||
</div>{{end}}
|
||||
<!--<div class="rowitem passive">
|
||||
<a class="profile_menu_item">{{lang "profile_add_friend"}}</a>
|
||||
<a class="profile_menu_item">{{lang "profile.add_friend"}}</a>
|
||||
</div>-->
|
||||
|
||||
{{if (.CurrentUser.IsSuperMod) and not (.ProfileOwner.IsSuperMod)}}<div class="rowitem passive">
|
||||
{{if .ProfileOwner.IsBanned}}<a href="/users/unban/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" class="profile_menu_item">{{lang "profile_unban"}}</a>
|
||||
{{else}}<a href="#ban_user" class="profile_menu_item">{{lang "profile_ban"}}</a>{{end}}
|
||||
</div>{{end}}
|
||||
<div class="rowitem passive">
|
||||
{{if .Blocked}}<a href="/user/block/remove/{{.ProfileOwner.ID}}" class="profile_menu_item">{{lang "profile_unblock"}}</a>{{else}}<a href="/user/block/create/{{.ProfileOwner.ID}}" class="profile_menu_item">{{lang "profile_block"}}</a>{{end}}
|
||||
{{if .ProfileOwner.IsBanned}}<a href="/users/unban/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" class="profile_menu_item">{{lang "profile.unban"}}</a>
|
||||
{{else}}<a href="#ban_user" class="profile_menu_item">{{lang "profile.ban"}}</a>{{end}}
|
||||
</div>
|
||||
<div class="rowitem passive">
|
||||
<a href="/report/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}&type=user" class="profile_menu_item report_item" aria-label="{{lang "profile_report_user_aria"}}" title="{{lang "profile_report_user_tooltip"}}"></a>
|
||||
<a href="#delete_posts" class="profile_menu_item">{{lang "profile.delete_posts"}}</a>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="rowitem passive">
|
||||
{{if .Blocked}}<a href="/user/block/remove/{{.ProfileOwner.ID}}" class="profile_menu_item">{{lang "profile.unblock"}}</a>{{else}}<a href="/user/block/create/{{.ProfileOwner.ID}}" class="profile_menu_item">{{lang "profile.block"}}</a>{{end}}
|
||||
</div>
|
||||
<div class="rowitem passive">
|
||||
<a href="/report/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}&type=user" class="profile_menu_item report_item" aria-label="{{lang "profile.report_user_aria"}}" title="{{lang "profile.report_user_tooltip"}}"></a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
@ -53,37 +59,58 @@
|
|||
{{if .CurrentUser.Perms.BanUsers}}
|
||||
<!-- TODO: Inline the display:none; CSS -->
|
||||
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display:none;">
|
||||
<div class="rowitem"><h1><a>{{lang "profile_ban_user_head"}}</a></h1></div>
|
||||
<div class="rowitem"><h1><a>{{lang "profile.ban_user_head"}}</a></h1></div>
|
||||
</div>
|
||||
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" method="post" style="display:none;">
|
||||
<div class="the_form">
|
||||
{{/** TODO: Put a JS duration calculator here instead of this text? **/}}
|
||||
<div class="colline">{{lang "profile_ban_user_notice"}}</div>
|
||||
<div class="colline">{{lang "profile.ban_user_notice"}}</div>
|
||||
<div class="colstack_item">
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem formlabel"><a>{{lang "profile_ban_user_days"}}</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "profile.ban_user_days"}}</a></div>
|
||||
<div class="formitem">
|
||||
<input name="ban-duration-days" type="number" value=0 min=0 />
|
||||
<input name="dur-days" type="number" value=0 min=0/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "profile_ban_user_weeks"}}</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "profile.ban_user_weeks"}}</a></div>
|
||||
<div class="formitem">
|
||||
<input name="ban-duration-weeks" type="number" value=0 min=0 />
|
||||
<input name="dur-weeks" type="number" value=0 min=0/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "profile_ban_user_months"}}</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "profile.ban_user_months"}}</a></div>
|
||||
<div class="formitem">
|
||||
<input name="ban-duration-months" type="number" value=0 min=0 />
|
||||
<input name="dur-months" type="number" value=0 min=0/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "profile.ban_delete_posts"}}</a></div>
|
||||
<div class="formitem"><select name="delete-posts">
|
||||
<option value=1>{{lang "option_yes"}}</option>
|
||||
<option selected value=0>{{lang "option_no"}}</option>
|
||||
</select></div>
|
||||
</div>
|
||||
{{/**<!--<div class="formrow">
|
||||
<div class="formitem formlabel"><a>{{lang "profile_ban_user_reason"}}</a></div>
|
||||
<div class="formitem formlabel"><a>{{lang "profile.ban_user_reason"}}</a></div>
|
||||
<div class="formitem"><textarea name="ban-reason" placeholder="A really horrible person" required></textarea></div>
|
||||
</div>-->**/}}
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="ban-button" class="formbutton form_middle_button">{{lang "profile_ban_user_button"}}</button></div>
|
||||
<div class="formitem"><button name="ban-button" class="formbutton form_middle_button">{{lang "profile.ban_user_button"}}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="delete_posts_head" class="colstack_item colstack_head hash_hide delete_posts_hash" style="display:none;">
|
||||
<div class="rowitem"><h1><a>{{lang "profile.delete_posts_head"}}</a></h1></div>
|
||||
</div>
|
||||
<form id="delete_posts_form" class="hash_hide delete_posts_hash" action="/users/delete-posts/submit/{{.ProfileOwner.ID}}?s={{.CurrentUser.Session}}" method="post" style="display:none;">
|
||||
<div class="the_form">
|
||||
<div class="colline">{{langf "profile.delete_posts_notice" .ProfileOwner.Posts}}</div>
|
||||
<div class="colstack_item">
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem"><button name="delete-posts-button" class="formbutton form_middle_button">{{lang "profile.delete_posts_button"}}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -92,7 +119,7 @@
|
|||
{{end}}
|
||||
|
||||
<div id="profile_comments_head" class="colstack_item colstack_head hash_hide">
|
||||
<div class="rowitem"><h1><a>{{lang "profile_comments_head"}}</a></h1></div>
|
||||
<div class="rowitem"><h1><a>{{lang "profile.comments_head"}}</a></h1></div>
|
||||
</div>
|
||||
<div id="profile_comments" class="colstack_item hash_hide">{{template "profile_comments_row.html" . }}</div>
|
||||
|
||||
|
@ -102,17 +129,17 @@
|
|||
<input name="uid" value='{{.ProfileOwner.ID}}' type="hidden" />
|
||||
<div class="colstack_item topic_reply_form" style="border-top:none;">
|
||||
<div class="formrow">
|
||||
<div class="formitem"><textarea class="input_content" name="content" placeholder="{{lang "profile_comments_form_content"}}"></textarea></div>
|
||||
<div class="formitem"><textarea class="input_content" name="content" placeholder="{{lang "profile.comments_form_content"}}"></textarea></div>
|
||||
</div>
|
||||
<div class="formrow quick_button_row">
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">{{lang "profile_comments_form_button"}}</button></div>
|
||||
<div class="formitem"><button name="reply-button" class="formbutton">{{lang "profile.comments_form_button"}}</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="colstack_item" style="border-top:none;">
|
||||
<div class="rowitem passive">{{lang "profile_comments_form_guest"}}</div>
|
||||
<div class="rowitem passive">{{lang "profile.comments_form_guest"}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<span class="controls">
|
||||
<a href="{{.UserLink}}" class="real_username username">{{.CreatedByName}}</a>
|
||||
|
||||
{{if $.CurrentUser.IsMod}}<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
{{if $.CurrentUser.IsMod}}<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_edit_tooltip"}}" aria-label="{{lang "profile.comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_delete_tooltip"}}" aria-label="{{lang "profile.comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>{{end}}
|
||||
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile.comments_report_tooltip"}}" aria-label="{{lang "profile.comments_report_aria"}}"></button></a>
|
||||
|
||||
{{if .Tag}}<a class="username hide_on_mobile user_tag" style="float:right;">{{.Tag}}</a>{{end}}
|
||||
</span>
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
</div>
|
||||
<span class="controls">
|
||||
{{if $.CurrentUser.IsMod}}
|
||||
<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_edit_tooltip"}}" aria-label="{{lang "profile_comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile_comments_delete_tooltip"}}" aria-label="{{lang "profile_comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
|
||||
<a href="/profile/reply/edit/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_edit_tooltip"}}" aria-label="{{lang "profile.comments_edit_aria"}}"><button class="username edit_item edit_label"></button></a>
|
||||
<a href="/profile/reply/delete/submit/{{.ID}}?s={{$.CurrentUser.Session}}" class="mod_button" title="{{lang "profile.comments_delete_tooltip"}}" aria-label="{{lang "profile.comments_delete_aria"}}"><button class="username delete_item delete_label"></button></a>
|
||||
{{end}}
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile_comments_report_tooltip"}}" aria-label="{{lang "profile_comments_report_aria"}}"></button></a>
|
||||
<a class="mod_button" href="/report/submit/{{.ID}}?s={{$.CurrentUser.Session}}&type=user-reply"><button class="username report_item flag_label" title="{{lang "profile.comments_report_tooltip"}}" aria-label="{{lang "profile.comments_report_aria"}}"></button></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="content_column">
|
||||
|
|
Loading…
Reference in New Issue