gosora/mod_routes.go
Azareal 5ba7aa74f7 Renamed the pre_render_panel_mod_log pre-render hook to pre_render_panel_modlogs.
Added the half-second task type, you'll why later ;)
Reduced the amount of code duplication in the panel routes (saved a hundred lines).
Added the two day time range option for graphs.
We now track the discord, lynx and blank user agents.
Renamed some template files for consistency and to help stamp out some duplicate code.

Began work on topic move.
2018-01-11 08:03:17 +00:00

790 lines
22 KiB
Go

package main
import (
//"log"
//"fmt"
"encoding/json"
"html"
"log"
"net/http"
"strconv"
"time"
"./common"
)
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Make sure this route is member only
func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
isJs := (r.PostFormValue("js") == "1")
tid, err := strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
if err != nil {
return common.PreErrorJSQ("The provided TopicID is not a valid number.", w, r, isJs)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreErrorJSQ("The topic you tried to edit doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.EditTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = topic.Update(r.PostFormValue("topic_name"), r.PostFormValue("topic_content"))
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.Forums.UpdateLastTopic(topic.ID, user.ID, topic.ParentID)
if err != nil && err != ErrNoRows {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
} else {
_, _ = w.Write(successJSONBytes)
}
return nil
}
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Make sure this route is member only
func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
if common.ReqIsJson(r) {
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
//log.Print("r.Body: ", r.Body)
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
//log.Print("parse err: ", err)
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
isJs = true
} else {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
tids = append(tids, tid)
}
if len(tids) == 0 {
return common.LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreErrorJSQ("The topic you tried to delete doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.DeleteTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
// We might be able to handle this err better
err = topic.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("delete", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// ? - We might need to add soft-delete before we can do an action reply for this
/*_, err = stmts.createActionReply.Exec(tid,"delete",ipaddress,user.ID)
if err != nil {
return common.InternalErrorJSQ(err,w,r,isJs)
}*/
log.Printf("Topic #%d was deleted by common.User #%d", tid, user.ID)
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return nil
}
func routeStickTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/stick/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to pin doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.PinTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Stick()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("stick", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("stick", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unstick/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to unpin doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.PinTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Unstick()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unstick", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("unstick", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func routeLockTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
// TODO: Move this to some sort of middleware
var tids []int
var isJs = false
if common.ReqIsJson(r) {
if r.Body == nil {
return common.PreErrorJS("No request body", w, r)
}
err := json.NewDecoder(r.Body).Decode(&tids)
if err != nil {
return common.PreErrorJS("We weren't able to parse your data", w, r)
}
isJs = true
} else {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/lock/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
tids = append(tids, tid)
}
if len(tids) == 0 {
return common.LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
}
for _, tid := range tids {
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreErrorJSQ("The topic you tried to lock doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.CloseTopic {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = topic.Lock()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("lock", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = topic.CreateActionReply("lock", user.LastIP, user)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
}
if len(tids) == 1 {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
}
return nil
}
func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unlock/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to unlock doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.CloseTopic {
return common.NoPermissions(w, r, user)
}
err = topic.Unlock()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unlock", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("unlock", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
func routeMoveTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
return common.NoPermissions(w, r, user)
tid, err := strconv.Atoi(r.URL.Path[len("/topic/move/submit/"):])
if err != nil {
return common.PreError("The provided TopicID is not a valid number.", w, r)
}
topic, err := common.Topics.Get(tid)
if err == ErrNoRows {
return common.PreError("The topic you tried to move doesn't exist.", w, r)
} else if err != nil {
return common.InternalError(err, w, r)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, topic.ParentID)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic { // TODO: MoveTopic permission?
return common.NoPermissions(w, r, user)
}
err = topic.Unlock()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("move", tid, "topic", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
err = topic.CreateActionReply("move", user.LastIP, user)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
return nil
}
// TODO: Disable stat updates in posts handled by plugin_guilds
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
isJs := (r.PostFormValue("js") == "1")
rid, err := strconv.Atoi(r.URL.Path[len("/reply/edit/submit/"):])
if err != nil {
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
}
// Get the Reply ID..
var tid int
err = stmts.getReplyTID.QueryRow(rid).Scan(&tid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
var fid int
err = stmts.getTopicFID.QueryRow(tid).Scan(&fid)
if err == ErrNoRows {
return common.PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, fid)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.EditReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
content := common.PreparseMessage(html.UnescapeString(r.PostFormValue("edit_item")))
_, err = stmts.editReply.Exec(content, common.ParseMessage(content, fid, "forums"), rid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs {
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
// TODO: Refactor this
// TODO: Disable stat updates in posts handled by plugin_guilds
func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.PreError("Bad Form", w, r)
}
isJs := (r.PostFormValue("isJs") == "1")
rid, err := strconv.Atoi(r.URL.Path[len("/reply/delete/submit/"):])
if err != nil {
return common.PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
}
reply, err := common.Rstore.Get(rid)
if err == ErrNoRows {
return common.PreErrorJSQ("The reply you tried to delete doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
var fid int
err = stmts.getTopicFID.QueryRow(reply.ParentID).Scan(&fid)
if err == ErrNoRows {
return common.PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// TODO: Add hooks to make use of headerLite
_, ferr := common.SimpleForumUserCheck(w, r, &user, fid)
if ferr != nil {
return ferr
}
if !user.Perms.ViewTopic || !user.Perms.DeleteReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
err = reply.Delete()
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
//log.Printf("Reply #%d was deleted by common.User #%d", rid, user.ID)
if !isJs {
//http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
replyCreator, err := common.Users.Get(reply.CreatedBy)
if err == nil {
wcount := common.WordCount(reply.Content)
err = replyCreator.DecreasePostStats(wcount, false)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
} else if err != ErrNoRows {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.ModLogs.Create("delete", reply.ParentID, "reply", user.LastIP, user.ID)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
return nil
}
func routeProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.LocalError("Bad Form", w, r, user)
}
isJs := (r.PostFormValue("js") == "1")
rid, err := strconv.Atoi(r.URL.Path[len("/profile/reply/edit/submit/"):])
if err != nil {
return common.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
}
// Get the Reply ID..
var uid int
err = stmts.getUserReplyUID.QueryRow(rid).Scan(&uid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if user.ID != uid && !user.Perms.EditReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
content := common.PreparseMessage(html.UnescapeString(r.PostFormValue("edit_item")))
_, err = stmts.editProfileReply.Exec(content, common.ParseMessage(content, 0, ""), rid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if !isJs {
http.Redirect(w, r, "/user/"+strconv.Itoa(uid)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
func routeProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
err := r.ParseForm()
if err != nil {
return common.LocalError("Bad Form", w, r, user)
}
isJs := (r.PostFormValue("isJs") == "1")
rid, err := strconv.Atoi(r.URL.Path[len("/profile/reply/delete/submit/"):])
if err != nil {
return common.LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
}
var uid int
err = stmts.getUserReplyUID.QueryRow(rid).Scan(&uid)
if err == ErrNoRows {
return common.LocalErrorJSQ("The reply you tried to delete doesn't exist.", w, r, user, isJs)
} else if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
if user.ID != uid && !user.Perms.DeleteReply {
return common.NoPermissionsJSQ(w, r, user, isJs)
}
_, err = stmts.deleteProfileReply.Exec(rid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
//log.Printf("The profile post '%d' was deleted by common.User #%d", rid, user.ID)
if !isJs {
//http.Redirect(w,r, "/user/" + strconv.Itoa(uid), http.StatusSeeOther)
} else {
w.Write(successJSONBytes)
}
return nil
}
func routeIps(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
headerVars, ferr := common.UserCheck(w, r, &user)
if ferr != nil {
return ferr
}
if !user.Perms.ViewIPs {
return common.NoPermissions(w, r, user)
}
var ip = r.FormValue("ip")
var uid int
var reqUserList = make(map[int]bool)
rows, err := stmts.findUsersByIPUsers.Query(ip)
if err != nil {
return common.InternalError(err, w, r)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&uid)
if err != nil {
return common.InternalError(err, w, r)
}
reqUserList[uid] = true
}
err = rows.Err()
if err != nil {
return common.InternalError(err, w, r)
}
rows2, err := stmts.findUsersByIPTopics.Query(ip)
if err != nil {
return common.InternalError(err, w, r)
}
defer rows2.Close()
for rows2.Next() {
err := rows2.Scan(&uid)
if err != nil {
return common.InternalError(err, w, r)
}
reqUserList[uid] = true
}
err = rows2.Err()
if err != nil {
return common.InternalError(err, w, r)
}
rows3, err := stmts.findUsersByIPReplies.Query(ip)
if err != nil {
return common.InternalError(err, w, r)
}
defer rows3.Close()
for rows3.Next() {
err := rows3.Scan(&uid)
if err != nil {
return common.InternalError(err, w, r)
}
reqUserList[uid] = true
}
err = rows3.Err()
if err != nil {
return common.InternalError(err, w, r)
}
// Convert the user ID map to a slice, then bulk load the users
var idSlice = make([]int, len(reqUserList))
var i int
for userID := range reqUserList {
idSlice[i] = userID
i++
}
// TODO: What if a user is deleted via the Control Panel?
userList, err := common.Users.BulkGetMap(idSlice)
if err != nil {
return common.InternalError(err, w, r)
}
pi := common.IPSearchPage{common.GetTitlePhrase("ip-search"), user, headerVars, userList, ip}
if common.PreRenderHooks["pre_render_ips"] != nil {
if common.RunPreRenderHook("pre_render_ips", w, r, &user, &pi) {
return nil
}
}
err = common.Templates.ExecuteTemplate(w, "ip-search.html", pi)
if err != nil {
return common.InternalError(err, w, r)
}
return nil
}
func routeBanSubmit(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
if !user.Perms.BanUsers {
return common.NoPermissions(w, r, user)
}
uid, err := strconv.Atoi(suid)
if err != nil {
return common.LocalError("The provided UserID is not a valid number.", w, r, user)
}
if uid == -2 {
return common.LocalError("Why don't you like Merlin?", w, r, user)
}
targetUser, err := common.Users.Get(uid)
if err == ErrNoRows {
return common.LocalError("The user you're trying to ban no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
// TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one?
if targetUser.IsMod {
return common.LocalError("You may not ban another staff member.", w, r, user)
}
if uid == user.ID {
return common.LocalError("Why are you trying to ban yourself? Stop that.", w, r, user)
}
if targetUser.IsBanned {
return common.LocalError("The user you're trying to unban is already banned.", w, r, user)
}
durationDays, err := strconv.Atoi(r.FormValue("ban-duration-days"))
if err != nil {
return common.LocalError("You can only use whole numbers for the number of days", w, r, user)
}
durationWeeks, err := strconv.Atoi(r.FormValue("ban-duration-weeks"))
if err != nil {
return common.LocalError("You can only use whole numbers for the number of weeks", w, r, user)
}
durationMonths, err := strconv.Atoi(r.FormValue("ban-duration-months"))
if err != nil {
return common.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 * common.Day
seconds += durationWeeks * common.Week
seconds += durationMonths * common.Month
duration, _ = time.ParseDuration(strconv.Itoa(seconds) + "s")
}
err = targetUser.Ban(duration, user.ID)
if err == ErrNoRows {
return common.LocalError("The user you're trying to ban no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("ban", uid, "user", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
return nil
}
func routeUnban(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
if !user.Perms.BanUsers {
return common.NoPermissions(w, r, user)
}
uid, err := strconv.Atoi(suid)
if err != nil {
return common.LocalError("The provided UserID is not a valid number.", w, r, user)
}
targetUser, err := common.Users.Get(uid)
if err == ErrNoRows {
return common.LocalError("The user you're trying to unban no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
if !targetUser.IsBanned {
return common.LocalError("The user you're trying to unban isn't banned.", w, r, user)
}
err = targetUser.Unban()
if err == common.ErrNoTempGroup {
return common.LocalError("The user you're trying to unban is not banned", w, r, user)
} else if err == ErrNoRows {
return common.LocalError("The user you're trying to unban no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("unban", uid, "user", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
return nil
}
func routeActivate(w http.ResponseWriter, r *http.Request, user common.User, suid string) common.RouteError {
if !user.Perms.ActivateUsers {
return common.NoPermissions(w, r, user)
}
uid, err := strconv.Atoi(suid)
if err != nil {
return common.LocalError("The provided UserID is not a valid number.", w, r, user)
}
targetUser, err := common.Users.Get(uid)
if err == ErrNoRows {
return common.LocalError("The account you're trying to activate no longer exists.", w, r, user)
} else if err != nil {
return common.InternalError(err, w, r)
}
if targetUser.Active {
return common.LocalError("The account you're trying to activate has already been activated.", w, r, user)
}
err = targetUser.Activate()
if err != nil {
return common.InternalError(err, w, r)
}
err = common.ModLogs.Create("activate", targetUser.ID, "user", user.LastIP, user.ID)
if err != nil {
return common.InternalError(err, w, r)
}
http.Redirect(w, r, "/user/"+strconv.Itoa(targetUser.ID), http.StatusSeeOther)
return nil
}