cce87e2d70
Fixed a problem with padding on Edge. You now need MoveTopic in the source forum and the destination forum to move a topic rather than just having it in the source forum. Move action posts should show up properly now. Improved the CSS for action posts on Cosora. You can view alerts on Cosora.
793 lines
23 KiB
Go
793 lines
23 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
|
|
func routeEditTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
|
isJs := (r.PostFormValue("js") == "1")
|
|
|
|
tid, err := strconv.Atoi(stid)
|
|
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
|
|
func routeDeleteTopicSubmit(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 routeStickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
|
tid, err := strconv.Atoi(stid)
|
|
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 routeUnstickTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
|
tid, err := strconv.Atoi(stid)
|
|
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 routeLockTopicSubmit(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 routeUnlockTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, stid string) common.RouteError {
|
|
tid, err := strconv.Atoi(stid)
|
|
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
|
|
}
|
|
|
|
// ! JS only route
|
|
// TODO: Figure a way to get this route to work without JS
|
|
func routeMoveTopicSubmit(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError {
|
|
fid, err := strconv.Atoi(sfid)
|
|
if err != nil {
|
|
return common.PreErrorJS("The provided Forum ID is not a valid number.", w, r)
|
|
}
|
|
|
|
// TODO: Move this to some sort of middleware
|
|
var tids []int
|
|
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)
|
|
}
|
|
if len(tids) == 0 {
|
|
return common.LocalErrorJS("You haven't provided any IDs", w, r)
|
|
}
|
|
|
|
for _, tid := range tids {
|
|
topic, err := common.Topics.Get(tid)
|
|
if err == ErrNoRows {
|
|
return common.PreErrorJS("The topic you tried to move doesn't exist.", w, r)
|
|
} else if err != nil {
|
|
return common.InternalErrorJS(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.MoveTopic {
|
|
return common.NoPermissionsJS(w, r, user)
|
|
}
|
|
_, ferr = common.SimpleForumUserCheck(w, r, &user, fid)
|
|
if ferr != nil {
|
|
return ferr
|
|
}
|
|
if !user.Perms.ViewTopic || !user.Perms.MoveTopic {
|
|
return common.NoPermissionsJS(w, r, user)
|
|
}
|
|
|
|
err = topic.MoveTo(fid)
|
|
if err != nil {
|
|
return common.InternalErrorJS(err, w, r)
|
|
}
|
|
|
|
// TODO: Log more data so we can list the destination forum in the action post?
|
|
err = common.ModLogs.Create("move", tid, "topic", user.LastIP, user.ID)
|
|
if err != nil {
|
|
return common.InternalErrorJS(err, w, r)
|
|
}
|
|
err = topic.CreateActionReply("move", user.LastIP, user)
|
|
if err != nil {
|
|
return common.InternalErrorJS(err, w, r)
|
|
}
|
|
}
|
|
|
|
if len(tids) == 1 {
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), 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, srid string) common.RouteError {
|
|
isJs := (r.PostFormValue("js") == "1")
|
|
|
|
rid, err := strconv.Atoi(srid)
|
|
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, srid string) common.RouteError {
|
|
isJs := (r.PostFormValue("isJs") == "1")
|
|
|
|
rid, err := strconv.Atoi(srid)
|
|
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, srid string) common.RouteError {
|
|
isJs := (r.PostFormValue("js") == "1")
|
|
|
|
rid, err := strconv.Atoi(srid)
|
|
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, srid string) common.RouteError {
|
|
isJs := (r.PostFormValue("isJs") == "1")
|
|
|
|
rid, err := strconv.Atoi(srid)
|
|
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
|
|
}
|