2016-12-07 09:34:09 +00:00
|
|
|
package main
|
|
|
|
|
2017-05-11 13:04:43 +00:00
|
|
|
import (
|
2017-06-16 10:41:30 +00:00
|
|
|
//"log"
|
|
|
|
//"fmt"
|
2017-10-30 09:57:08 +00:00
|
|
|
"encoding/json"
|
2017-09-03 04:50:31 +00:00
|
|
|
"html"
|
2017-10-30 09:57:08 +00:00
|
|
|
"log"
|
2017-05-11 13:04:43 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2017-09-03 04:50:31 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
2017-05-11 13:04:43 +00:00
|
|
|
)
|
2016-12-07 09:34:09 +00:00
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
|
2017-11-02 04:12:51 +00:00
|
|
|
// TODO: Disable stat updates in posts handled by plugin_guilds
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeEditTopic(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("Bad Form", w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
isJs := (r.PostFormValue("js") == "1")
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The provided TopicID is not a valid number.", w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-28 22:16:34 +00:00
|
|
|
topic, err := topics.Get(tid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The topic you tried to edit doesn't exist.", w, r, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
2017-10-30 09:57:08 +00:00
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-02-05 16:36:54 +00:00
|
|
|
if !user.Perms.ViewTopic || !user.Perms.EditTopic {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
topicName := r.PostFormValue("topic_name")
|
|
|
|
topicContent := html.EscapeString(r.PostFormValue("topic_content"))
|
2017-09-28 22:16:34 +00:00
|
|
|
err = topic.Update(topicName, topicContent)
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-28 22:16:34 +00:00
|
|
|
err = fstore.UpdateLastTopic(topic.ID, user.ID, topic.ParentID)
|
2017-09-22 02:21:17 +00:00
|
|
|
if err != nil && err != ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-04-02 13:00:40 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
if !isJs {
|
|
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
2016-12-07 09:34:09 +00:00
|
|
|
} else {
|
2017-09-03 04:50:31 +00:00
|
|
|
_, _ = w.Write(successJSONBytes)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
|
2017-11-02 04:12:51 +00:00
|
|
|
// TODO: Disable stat updates in posts handled by plugin_guilds
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|
|
|
// TODO: Move this to some sort of middleware
|
|
|
|
var tids []int
|
|
|
|
var isJs = false
|
|
|
|
if r.Header.Get("Content-type") == "application/json" {
|
|
|
|
if r.Body == nil {
|
|
|
|
return 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 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 PreError("The provided TopicID is not a valid number.", w, r)
|
|
|
|
}
|
|
|
|
tids = append(tids, tid)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
if len(tids) == 0 {
|
|
|
|
return LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
for _, tid := range tids {
|
|
|
|
topic, err := topics.Get(tid)
|
|
|
|
if err == ErrNoRows {
|
|
|
|
return PreErrorJSQ("The topic you tried to delete doesn't exist.", w, r, isJs)
|
|
|
|
} else if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
|
|
|
}
|
|
|
|
if !user.Perms.ViewTopic || !user.Perms.DeleteTopic {
|
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
// We might be able to handle this err better
|
|
|
|
err = topic.Delete()
|
|
|
|
if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
|
|
|
return LocalErrorJSQ("Bad IP", w, r, user, isJs)
|
|
|
|
}
|
|
|
|
err = addModLog("delete", tid, "topic", ipaddress, user.ID)
|
|
|
|
if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
// ? - We might need to add soft-delete before we can do an action reply for this
|
|
|
|
/*_, err = createActionReplyStmt.Exec(tid,"delete",ipaddress,user.ID)
|
|
|
|
if err != nil {
|
|
|
|
return InternalErrorJSQ(err,w,r,isJs)
|
|
|
|
}*/
|
|
|
|
|
|
|
|
log.Printf("Topic #%d was deleted by User #%d", tid, user.ID)
|
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeStickTopic(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/stick/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("The provided TopicID is not a valid number.", w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-15 22:20:01 +00:00
|
|
|
topic, err := topics.Get(tid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("The topic you tried to pin doesn't exist.", w, r)
|
2017-01-31 05:13:38 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
2017-10-30 09:57:08 +00:00
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-02-05 16:36:54 +00:00
|
|
|
if !user.Perms.ViewTopic || !user.Perms.PinTopic {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
err = topic.Stick()
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
// ! - Can we use user.LastIP here? It might be racey, if another thread mutates it... We need to fix this.
|
2017-04-06 17:37:32 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad IP", w, r, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
err = addModLog("stick", tid, "topic", ipaddress, user.ID)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
err = topic.CreateActionReply("stick", ipaddress, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unstick/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("The provided TopicID is not a valid number.", w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-15 22:20:01 +00:00
|
|
|
topic, err := topics.Get(tid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("The topic you tried to unpin doesn't exist.", w, r)
|
2017-01-31 05:13:38 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
2017-10-30 09:57:08 +00:00
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-02-05 16:36:54 +00:00
|
|
|
if !user.Perms.ViewTopic || !user.Perms.PinTopic {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
err = topic.Unstick()
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-04-06 17:37:32 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad IP", w, r, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
err = addModLog("unstick", tid, "topic", ipaddress, user.ID)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
err = topic.CreateActionReply("unstick", ipaddress, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeLockTopic(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|
|
|
// TODO: Move this to some sort of middleware
|
|
|
|
var tids []int
|
|
|
|
var isJs = false
|
|
|
|
if r.Header.Get("Content-type") == "application/json" {
|
|
|
|
if r.Body == nil {
|
|
|
|
return PreErrorJS("No request body", w, r)
|
|
|
|
}
|
|
|
|
err := json.NewDecoder(r.Body).Decode(&tids)
|
|
|
|
if err != nil {
|
|
|
|
return 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 PreError("The provided TopicID is not a valid number.", w, r)
|
|
|
|
}
|
|
|
|
tids = append(tids, tid)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
if len(tids) == 0 {
|
|
|
|
return LocalErrorJSQ("You haven't provided any IDs", w, r, user, isJs)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
for _, tid := range tids {
|
|
|
|
topic, err := topics.Get(tid)
|
|
|
|
if err == ErrNoRows {
|
|
|
|
return PreErrorJSQ("The topic you tried to lock doesn't exist.", w, r, isJs)
|
|
|
|
} else if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
|
|
|
}
|
|
|
|
if !user.Perms.ViewTopic || !user.Perms.CloseTopic {
|
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
err = topic.Lock()
|
|
|
|
if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ! - Can we use user.LastIP here? It might be racey, if another thread mutates it... We need to fix this.
|
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
|
|
|
return LocalErrorJSQ("Bad IP", w, r, user, isJs)
|
|
|
|
}
|
|
|
|
err = addModLog("lock", tid, "topic", ipaddress, user.ID)
|
|
|
|
if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
|
|
|
err = topic.CreateActionReply("lock", ipaddress, user)
|
|
|
|
if err != nil {
|
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
|
|
|
|
if len(tids) == 1 {
|
|
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tids[0]), http.StatusSeeOther)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2017-09-22 02:21:17 +00:00
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unlock/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("The provided TopicID is not a valid number.", w, r)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
topic, err := topics.Get(tid)
|
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("The topic you tried to unlock doesn't exist.", w, r)
|
2017-09-22 02:21:17 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Add hooks to make use of headerLite
|
2017-10-30 09:57:08 +00:00
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
if !user.Perms.ViewTopic || !user.Perms.CloseTopic {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err = topic.Unlock()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ! - Can we use user.LastIP here? It might be racey, if another thread mutates it... We need to fix this.
|
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad IP", w, r, user)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
err = addModLog("unlock", tid, "topic", ipaddress, user.ID)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-22 02:21:17 +00:00
|
|
|
}
|
|
|
|
err = topic.CreateActionReply("unlock", ipaddress, user)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-02-16 06:47:55 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-11-02 04:12:51 +00:00
|
|
|
// TODO: Disable stat updates in posts handled by plugin_guilds
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeReplyEditSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("Bad Form", w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
isJs := (r.PostFormValue("js") == "1")
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 09:34:09 +00:00
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/reply/edit/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 09:34:09 +00:00
|
|
|
// Get the Reply ID..
|
|
|
|
var tid int
|
2017-09-18 17:03:52 +00:00
|
|
|
err = getReplyTIDStmt.QueryRow(rid).Scan(&tid)
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-01-31 05:13:38 +00:00
|
|
|
var fid int
|
2017-09-18 17:03:52 +00:00
|
|
|
err = getTopicFIDStmt.QueryRow(tid).Scan(&fid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
2017-10-30 09:57:08 +00:00
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, fid)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-02-05 16:36:54 +00:00
|
|
|
if !user.Perms.ViewTopic || !user.Perms.EditReply {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
Added Quick Topic.
Added Attachments.
Added Attachment Media Embeds.
Renamed a load of *Store and *Cache methods to reduce the amount of unneccesary typing.
Added petabytes as a unit and cleaned up a few of the friendly units.
Refactored the username change logic to make it easier to maintain.
Refactored the avatar change logic to make it easier to maintain.
Shadow now uses CSS Variables for most of it's colours. We have plans to transpile this to support older browsers later on!
Snuck some CSS Variables into Tempra Conflux.
Added the GroupCache interface to MemoryGroupStore.
Added the Length method to MemoryGroupStore.
Added support for a site short name.
Added the UploadFiles permission.
Renamed more functions.
Fixed the background for the left gutter on the postbit for Tempra Simple and Shadow.
Added support for if statements operating on int8, int16, int32, int32, int64, uint, uint8, uint16, uint32, uint64, float32, and float64 for the template compiler.
Added support for if statements operating on slices and maps for the template compiler.
Fixed a security exploit in reply editing.
Fixed a bug in the URL detector in the parser where it couldn't find URLs with non-standard ports.
Fixed buttons having blue outlines on focus on Shadow.
Refactored the topic creation logic to make it easier to maintain.
Made a few responsive fixes, but there's still more to do in the following commits!
2017-10-05 10:20:28 +00:00
|
|
|
content := html.EscapeString(preparseMessage(r.PostFormValue("edit_item")))
|
|
|
|
_, err = editReplyStmt.Exec(content, parseMessage(content, fid, "forums"), rid)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
Added Quick Topic.
Added Attachments.
Added Attachment Media Embeds.
Renamed a load of *Store and *Cache methods to reduce the amount of unneccesary typing.
Added petabytes as a unit and cleaned up a few of the friendly units.
Refactored the username change logic to make it easier to maintain.
Refactored the avatar change logic to make it easier to maintain.
Shadow now uses CSS Variables for most of it's colours. We have plans to transpile this to support older browsers later on!
Snuck some CSS Variables into Tempra Conflux.
Added the GroupCache interface to MemoryGroupStore.
Added the Length method to MemoryGroupStore.
Added support for a site short name.
Added the UploadFiles permission.
Renamed more functions.
Fixed the background for the left gutter on the postbit for Tempra Simple and Shadow.
Added support for if statements operating on int8, int16, int32, int32, int64, uint, uint8, uint16, uint32, uint64, float32, and float64 for the template compiler.
Added support for if statements operating on slices and maps for the template compiler.
Fixed a security exploit in reply editing.
Fixed a bug in the URL detector in the parser where it couldn't find URLs with non-standard ports.
Fixed buttons having blue outlines on focus on Shadow.
Refactored the topic creation logic to make it easier to maintain.
Made a few responsive fixes, but there's still more to do in the following commits!
2017-10-05 10:20:28 +00:00
|
|
|
}
|
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
if !isJs {
|
|
|
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
|
2016-12-07 09:34:09 +00:00
|
|
|
} else {
|
2017-09-03 04:50:31 +00:00
|
|
|
w.Write(successJSONBytes)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 03:24:14 +00:00
|
|
|
// TODO: Refactor this
|
2017-11-02 04:12:51 +00:00
|
|
|
// TODO: Disable stat updates in posts handled by plugin_guilds
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreError("Bad Form", w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
isJs := (r.PostFormValue("isJs") == "1")
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 09:34:09 +00:00
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/reply/delete/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The provided Reply ID is not a valid number.", w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-12 03:24:14 +00:00
|
|
|
reply, err := rstore.Get(rid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The reply you tried to delete doesn't exist.", w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-01-31 05:13:38 +00:00
|
|
|
var fid int
|
2017-09-18 17:03:52 +00:00
|
|
|
err = getTopicFIDStmt.QueryRow(reply.ParentID).Scan(&fid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return PreErrorJSQ("The parent topic doesn't exist.", w, r, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: Add hooks to make use of headerLite
|
2017-10-30 09:57:08 +00:00
|
|
|
_, ferr := SimpleForumUserCheck(w, r, &user, fid)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-02-05 16:36:54 +00:00
|
|
|
if !user.Perms.ViewTopic || !user.Perms.DeleteReply {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
2017-01-31 05:13:38 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-12 03:24:14 +00:00
|
|
|
err = reply.Delete()
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-12 03:24:14 +00:00
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
//log.Printf("Reply #%d was deleted by User #%d", rid, user.ID)
|
2017-09-03 04:50:31 +00:00
|
|
|
if !isJs {
|
2016-12-07 09:34:09 +00:00
|
|
|
//http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
|
|
|
|
} else {
|
2017-09-03 04:50:31 +00:00
|
|
|
w.Write(successJSONBytes)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-15 22:20:01 +00:00
|
|
|
replyCreator, err := users.Get(reply.CreatedBy)
|
2017-09-10 16:57:22 +00:00
|
|
|
if err == nil {
|
|
|
|
wcount := wordCount(reply.Content)
|
|
|
|
err = replyCreator.decreasePostStats(wcount, false)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-09-10 16:57:22 +00:00
|
|
|
}
|
|
|
|
} else if err != ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-01-12 02:55:08 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-04-06 17:37:32 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalErrorJSQ("Bad IP", w, r, user, isJs)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
err = addModLog("delete", reply.ParentID, "reply", ipaddress, user.ID)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2017-02-15 10:49:30 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeProfileReplyEditSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad Form", w, r, user)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
isJs := (r.PostFormValue("js") == "1")
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 09:34:09 +00:00
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/profile/reply/edit/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 13:46:14 +00:00
|
|
|
// Get the Reply ID..
|
|
|
|
var uid int
|
2017-09-18 17:03:52 +00:00
|
|
|
err = getUserReplyUIDStmt.QueryRow(rid).Scan(&uid)
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-21 02:30:32 +00:00
|
|
|
if user.ID != uid && !user.Perms.EditReply {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
2016-12-07 13:46:14 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
content := html.EscapeString(preparseMessage(r.PostFormValue("edit_item")))
|
Added Quick Topic.
Added Attachments.
Added Attachment Media Embeds.
Renamed a load of *Store and *Cache methods to reduce the amount of unneccesary typing.
Added petabytes as a unit and cleaned up a few of the friendly units.
Refactored the username change logic to make it easier to maintain.
Refactored the avatar change logic to make it easier to maintain.
Shadow now uses CSS Variables for most of it's colours. We have plans to transpile this to support older browsers later on!
Snuck some CSS Variables into Tempra Conflux.
Added the GroupCache interface to MemoryGroupStore.
Added the Length method to MemoryGroupStore.
Added support for a site short name.
Added the UploadFiles permission.
Renamed more functions.
Fixed the background for the left gutter on the postbit for Tempra Simple and Shadow.
Added support for if statements operating on int8, int16, int32, int32, int64, uint, uint8, uint16, uint32, uint64, float32, and float64 for the template compiler.
Added support for if statements operating on slices and maps for the template compiler.
Fixed a security exploit in reply editing.
Fixed a bug in the URL detector in the parser where it couldn't find URLs with non-standard ports.
Fixed buttons having blue outlines on focus on Shadow.
Refactored the topic creation logic to make it easier to maintain.
Made a few responsive fixes, but there's still more to do in the following commits!
2017-10-05 10:20:28 +00:00
|
|
|
_, err = editProfileReplyStmt.Exec(content, parseMessage(content, 0, ""), rid)
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
if !isJs {
|
|
|
|
http.Redirect(w, r, "/user/"+strconv.Itoa(uid)+"#reply-"+strconv.Itoa(rid), http.StatusSeeOther)
|
2016-12-07 09:34:09 +00:00
|
|
|
} else {
|
2017-09-03 04:50:31 +00:00
|
|
|
w.Write(successJSONBytes)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeProfileReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-07 09:34:09 +00:00
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad Form", w, r, user)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
isJs := (r.PostFormValue("isJs") == "1")
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 09:34:09 +00:00
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/profile/reply/delete/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalErrorJSQ("The provided Reply ID is not a valid number.", w, r, user, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-07 09:34:09 +00:00
|
|
|
var uid int
|
2017-09-18 17:03:52 +00:00
|
|
|
err = getUserReplyUIDStmt.QueryRow(rid).Scan(&uid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalErrorJSQ("The reply you tried to delete doesn't exist.", w, r, user, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 13:46:14 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-21 02:30:32 +00:00
|
|
|
if user.ID != uid && !user.Perms.DeleteReply {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissionsJSQ(w, r, user, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-18 17:03:52 +00:00
|
|
|
_, err = deleteProfileReplyStmt.Exec(rid)
|
2016-12-07 09:34:09 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalErrorJSQ(err, w, r, isJs)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
//log.Printf("The profile post '%d' was deleted by User #%d", rid, user.ID)
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
if !isJs {
|
2016-12-07 09:34:09 +00:00
|
|
|
//http.Redirect(w,r, "/user/" + strconv.Itoa(uid), http.StatusSeeOther)
|
|
|
|
} else {
|
2017-09-03 04:50:31 +00:00
|
|
|
w.Write(successJSONBytes)
|
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeIps(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|
|
|
headerVars, ferr := UserCheck(w, r, &user)
|
|
|
|
if ferr != nil {
|
|
|
|
return ferr
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
if !user.Perms.ViewIPs {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
|
2017-10-12 03:24:14 +00:00
|
|
|
var ip = r.FormValue("ip")
|
2017-09-03 04:50:31 +00:00
|
|
|
var uid int
|
2017-09-10 16:57:22 +00:00
|
|
|
var reqUserList = make(map[int]bool)
|
2017-09-03 04:50:31 +00:00
|
|
|
|
2017-09-18 17:03:52 +00:00
|
|
|
rows, err := findUsersByIPUsersStmt.Query(ip)
|
2017-09-03 04:50:31 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
for rows.Next() {
|
|
|
|
err := rows.Scan(&uid)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
reqUserList[uid] = true
|
|
|
|
}
|
|
|
|
err = rows.Err()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
|
2017-09-18 17:03:52 +00:00
|
|
|
rows2, err := findUsersByIPTopicsStmt.Query(ip)
|
2017-09-03 04:50:31 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
defer rows2.Close()
|
|
|
|
|
|
|
|
for rows2.Next() {
|
|
|
|
err := rows2.Scan(&uid)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
reqUserList[uid] = true
|
|
|
|
}
|
|
|
|
err = rows2.Err()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
|
2017-09-18 17:03:52 +00:00
|
|
|
rows3, err := findUsersByIPRepliesStmt.Query(ip)
|
2017-09-03 04:50:31 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
defer rows3.Close()
|
|
|
|
|
|
|
|
for rows3.Next() {
|
|
|
|
err := rows3.Scan(&uid)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
reqUserList[uid] = true
|
|
|
|
}
|
|
|
|
err = rows3.Err()
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the user ID map to a slice, then bulk load the users
|
2017-09-10 16:57:22 +00:00
|
|
|
var idSlice = make([]int, len(reqUserList))
|
2017-09-03 04:50:31 +00:00
|
|
|
var i int
|
|
|
|
for userID := range reqUserList {
|
|
|
|
idSlice[i] = userID
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
2017-09-10 16:57:22 +00:00
|
|
|
// TODO: What if a user is deleted via the Control Panel?
|
2017-09-15 22:20:01 +00:00
|
|
|
userList, err := users.BulkGetMap(idSlice)
|
2017-09-03 04:50:31 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pi := IPSearchPage{"IP Search", user, headerVars, userList, ip}
|
|
|
|
if preRenderHooks["pre_render_ips"] != nil {
|
|
|
|
if runPreRenderHook("pre_render_ips", w, r, &user, &pi) {
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2017-09-03 04:50:31 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-23 20:46:28 +00:00
|
|
|
err = templates.ExecuteTemplate(w, "ip-search.html", pi)
|
2017-09-03 04:50:31 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-07 09:34:09 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeBanSubmit(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-21 02:30:32 +00:00
|
|
|
if !user.Perms.BanUsers {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
|
|
|
if r.FormValue("session") != user.Session {
|
2017-10-30 09:57:08 +00:00
|
|
|
return SecurityError(w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-08 14:11:18 +00:00
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/ban/submit/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The provided User ID is not a valid number.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-12 09:03:14 +00:00
|
|
|
/*if uid == -2 {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Stop trying to ban Merlin! Ban admin! Bad! No!",w,r,user)
|
2017-06-12 09:03:14 +00:00
|
|
|
}*/
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-15 22:20:01 +00:00
|
|
|
targetUser, err := users.Get(uid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to ban no longer exists.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-10-31 07:26:44 +00:00
|
|
|
// TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one?
|
2017-09-03 04:50:31 +00:00
|
|
|
if targetUser.IsSuperAdmin || targetUser.IsAdmin || targetUser.IsMod {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("You may not ban another staff member.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
|
|
|
if uid == user.ID {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Why are you trying to ban yourself? Stop that.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
if targetUser.IsBanned {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to unban is already banned.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
durationDays, err := strconv.Atoi(r.FormValue("ban-duration-days"))
|
2017-08-27 09:33:45 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("You can only use whole numbers for the number of days", w, r, user)
|
2017-08-27 09:33:45 +00:00
|
|
|
}
|
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
durationWeeks, err := strconv.Atoi(r.FormValue("ban-duration-weeks"))
|
2017-08-27 09:33:45 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("You can only use whole numbers for the number of weeks", w, r, user)
|
2017-08-27 09:33:45 +00:00
|
|
|
}
|
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
durationMonths, err := strconv.Atoi(r.FormValue("ban-duration-months"))
|
2016-12-08 14:11:18 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("You can only use whole numbers for the number of months", w, r, user)
|
2017-08-27 09:33:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var duration time.Duration
|
2017-09-03 04:50:31 +00:00
|
|
|
if durationDays > 1 && durationWeeks > 1 && durationMonths > 1 {
|
2017-08-27 09:33:45 +00:00
|
|
|
duration, _ = time.ParseDuration("0")
|
|
|
|
} else {
|
|
|
|
var seconds int
|
2017-09-03 04:50:31 +00:00
|
|
|
seconds += durationDays * day
|
|
|
|
seconds += durationWeeks * week
|
|
|
|
seconds += durationMonths * month
|
2017-08-27 09:33:45 +00:00
|
|
|
duration, _ = time.ParseDuration(strconv.Itoa(seconds) + "s")
|
|
|
|
}
|
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
err = targetUser.Ban(duration, user.ID)
|
2017-08-27 09:33:45 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to ban no longer exists.", w, r, user)
|
2017-08-27 09:33:45 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-04-06 17:37:32 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad IP", w, r, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
err = addModLog("ban", uid, "user", ipaddress, user.ID)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeUnban(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-21 02:30:32 +00:00
|
|
|
if !user.Perms.BanUsers {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2016-12-23 12:35:22 +00:00
|
|
|
if r.FormValue("session") != user.Session {
|
2017-10-30 09:57:08 +00:00
|
|
|
return SecurityError(w, r, user)
|
2016-12-23 12:35:22 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-08 14:11:18 +00:00
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/unban/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The provided User ID is not a valid number.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-15 22:20:01 +00:00
|
|
|
targetUser, err := users.Get(uid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to unban no longer exists.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
if !targetUser.IsBanned {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to unban isn't banned.", w, r, user)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-08-27 09:33:45 +00:00
|
|
|
err = targetUser.Unban()
|
2017-10-21 00:27:47 +00:00
|
|
|
if err == ErrNoTempGroup {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to unban is not banned", w, r, user)
|
2017-10-21 00:27:47 +00:00
|
|
|
} else if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The user you're trying to unban no longer exists.", w, r, user)
|
2017-08-27 09:33:45 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-04-06 17:37:32 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad IP", w, r, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-03 04:50:31 +00:00
|
|
|
err = addModLog("unban", uid, "user", ipaddress, user.ID)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-03 04:50:31 +00:00
|
|
|
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-08 14:11:18 +00:00
|
|
|
}
|
|
|
|
|
2017-10-30 09:57:08 +00:00
|
|
|
func routeActivate(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
2016-12-21 02:30:32 +00:00
|
|
|
if !user.Perms.ActivateUsers {
|
2017-10-30 09:57:08 +00:00
|
|
|
return NoPermissions(w, r, user)
|
2016-12-18 12:56:06 +00:00
|
|
|
}
|
2016-12-23 12:35:22 +00:00
|
|
|
if r.FormValue("session") != user.Session {
|
2017-10-30 09:57:08 +00:00
|
|
|
return SecurityError(w, r, user)
|
2016-12-23 12:35:22 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2016-12-18 12:56:06 +00:00
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/activate/"):])
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The provided User ID is not a valid number.", w, r, user)
|
2016-12-18 12:56:06 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
targetUser, err := users.Get(uid)
|
2017-06-28 12:05:26 +00:00
|
|
|
if err == ErrNoRows {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The account you're trying to activate no longer exists.", w, r, user)
|
2016-12-18 12:56:06 +00:00
|
|
|
} else if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-18 12:56:06 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-09-22 02:21:17 +00:00
|
|
|
if targetUser.Active {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("The account you're trying to activate has already been activated.", w, r, user)
|
2016-12-18 12:56:06 +00:00
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
err = targetUser.Activate()
|
2016-12-21 02:30:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2016-12-21 02:30:32 +00:00
|
|
|
}
|
2017-06-06 14:41:06 +00:00
|
|
|
|
2017-04-06 17:37:32 +00:00
|
|
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return LocalError("Bad IP", w, r, user)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
err = addModLog("activate", targetUser.ID, "user", ipaddress, user.ID)
|
2017-04-06 17:37:32 +00:00
|
|
|
if err != nil {
|
2017-10-30 09:57:08 +00:00
|
|
|
return InternalError(err, w, r)
|
2017-04-06 17:37:32 +00:00
|
|
|
}
|
2017-09-22 02:21:17 +00:00
|
|
|
http.Redirect(w, r, "/user/"+strconv.Itoa(targetUser.ID), http.StatusSeeOther)
|
2017-10-30 09:57:08 +00:00
|
|
|
return nil
|
2016-12-18 12:56:06 +00:00
|
|
|
}
|