1356 lines
32 KiB
Go
1356 lines
32 KiB
Go
package main
|
|
|
|
import "log"
|
|
import "fmt"
|
|
import "strings"
|
|
import "strconv"
|
|
import "net/http"
|
|
import "html"
|
|
import "database/sql"
|
|
import _ "github.com/go-sql-driver/mysql"
|
|
|
|
func route_edit_topic(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
is_js := r.PostFormValue("js")
|
|
if is_js == "" {
|
|
is_js = "0"
|
|
}
|
|
if !user.Perms.ViewTopic || !user.Perms.EditTopic {
|
|
NoPermissionsJSQ(w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
var tid int
|
|
tid, err = strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
|
|
if err != nil {
|
|
LocalErrorJSQ("The provided TopicID is not a valid number.",w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
topic_name := r.PostFormValue("topic_name")
|
|
topic_status := r.PostFormValue("topic_status")
|
|
is_closed := (topic_status == "closed")
|
|
|
|
topic_content := html.EscapeString(r.PostFormValue("topic_content"))
|
|
_, err = edit_topic_stmt.Exec(topic_name, preparse_message(topic_content), parse_message(html.EscapeString(preparse_message(topic_content))), is_closed, tid)
|
|
if err != nil {
|
|
InternalErrorJSQ(err,w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
if is_js == "0" {
|
|
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
|
|
} else {
|
|
fmt.Fprintf(w,"{'success': '1'}")
|
|
}
|
|
}
|
|
|
|
func route_delete_topic(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Perms.ViewTopic || !user.Perms.DeleteTopic {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided TopicID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
var content string
|
|
var createdBy int
|
|
err = db.QueryRow("select tid, content, createdBy from topics where tid = ?", tid).Scan(&tid, &content, &createdBy)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The topic you tried to delete doesn't exist.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = delete_topic_stmt.Exec(tid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
log.Print("The topic '" + strconv.Itoa(tid) + "' was deleted by User ID #" + strconv.Itoa(user.ID) + ".")
|
|
http.Redirect(w,r,"/",http.StatusSeeOther)
|
|
|
|
wcount := word_count(content)
|
|
err = decrease_post_user_stats(wcount, createdBy, true, user)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
}
|
|
|
|
func route_stick_topic(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Perms.ViewTopic || !user.Perms.PinTopic {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/stick/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided TopicID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = stick_topic_stmt.Exec(tid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
|
|
}
|
|
|
|
func route_unstick_topic(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Perms.ViewTopic || !user.Perms.PinTopic {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/unstick/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided TopicID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = unstick_topic_stmt.Exec(tid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
|
|
}
|
|
|
|
func route_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
is_js := r.PostFormValue("js")
|
|
if is_js == "" {
|
|
is_js = "0"
|
|
}
|
|
if !user.Perms.ViewTopic || !user.Perms.EditReply {
|
|
NoPermissionsJSQ(w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/reply/edit/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided Reply ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
content := html.EscapeString(preparse_message(r.PostFormValue("edit_item")))
|
|
_, err = edit_reply_stmt.Exec(content, parse_message(content), rid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
// Get the Reply ID..
|
|
var tid int
|
|
err = db.QueryRow("select tid from replies where rid = ?", rid).Scan(&tid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if is_js == "0" {
|
|
http.Redirect(w,r, "/topic/" + strconv.Itoa(tid) + "#reply-" + strconv.Itoa(rid), http.StatusSeeOther)
|
|
} else {
|
|
fmt.Fprintf(w,"{'success': '1'}")
|
|
}
|
|
}
|
|
|
|
func route_reply_delete_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
is_js := r.PostFormValue("is_js")
|
|
if is_js == "" {
|
|
is_js = "0"
|
|
}
|
|
|
|
if !user.Perms.ViewTopic || !user.Perms.DeleteReply {
|
|
NoPermissionsJSQ(w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/reply/delete/submit/"):])
|
|
if err != nil {
|
|
LocalErrorJSQ("The provided Reply ID is not a valid number.",w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
var tid int
|
|
var content string
|
|
var createdBy int
|
|
err = db.QueryRow("SELECT tid, content, createdBy from replies where rid = ?", rid).Scan(&tid, &content, &createdBy)
|
|
if err == sql.ErrNoRows {
|
|
LocalErrorJSQ("The reply you tried to delete doesn't exist.",w,r,user,is_js)
|
|
return
|
|
} else if err != nil {
|
|
InternalErrorJSQ(err,w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
_, err = delete_reply_stmt.Exec(rid)
|
|
if err != nil {
|
|
InternalErrorJSQ(err,w,r,user,is_js)
|
|
return
|
|
}
|
|
log.Print("The reply '" + strconv.Itoa(rid) + "' was deleted by User ID #" + strconv.Itoa(user.ID) + ".")
|
|
if is_js == "0" {
|
|
//http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
|
|
} else {
|
|
fmt.Fprintf(w,"{'success': '1'}")
|
|
}
|
|
|
|
wcount := word_count(content)
|
|
err = decrease_post_user_stats(wcount, createdBy, false, user)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
_, err = remove_replies_from_topic_stmt.Exec(1, tid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
}
|
|
|
|
func route_profile_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
is_js := r.PostFormValue("js")
|
|
if is_js == "" {
|
|
is_js = "0"
|
|
}
|
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/profile/reply/edit/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided Reply ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
// Get the Reply ID..
|
|
var uid int
|
|
err = db.QueryRow("select uid from users_replies where rid = ?", rid).Scan(&uid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if user.ID != uid && !user.Perms.EditReply {
|
|
NoPermissionsJSQ(w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
content := html.EscapeString(preparse_message(r.PostFormValue("edit_item")))
|
|
_, err = edit_profile_reply_stmt.Exec(content, parse_message(content), rid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if is_js == "0" {
|
|
http.Redirect(w,r, "/user/" + strconv.Itoa(uid) + "#reply-" + strconv.Itoa(rid), http.StatusSeeOther)
|
|
} else {
|
|
fmt.Fprintf(w,"{'success': '1'}")
|
|
}
|
|
}
|
|
|
|
func route_profile_reply_delete_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
|
|
is_js := r.PostFormValue("is_js")
|
|
if is_js == "" {
|
|
is_js = "0"
|
|
}
|
|
|
|
rid, err := strconv.Atoi(r.URL.Path[len("/profile/reply/delete/submit/"):])
|
|
if err != nil {
|
|
LocalErrorJSQ("The provided Reply ID is not a valid number.",w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
var uid int
|
|
err = db.QueryRow("SELECT uid from users_replies where rid = ?", rid).Scan(&uid)
|
|
if err == sql.ErrNoRows {
|
|
LocalErrorJSQ("The reply you tried to delete doesn't exist.",w,r,user,is_js)
|
|
return
|
|
} else if err != nil {
|
|
InternalErrorJSQ(err,w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
if user.ID != uid && !user.Perms.DeleteReply {
|
|
NoPermissionsJSQ(w,r,user,is_js)
|
|
return
|
|
}
|
|
|
|
_, err = delete_profile_reply_stmt.Exec(rid)
|
|
if err != nil {
|
|
InternalErrorJSQ(err,w,r,user,is_js)
|
|
return
|
|
}
|
|
log.Print("The reply '" + strconv.Itoa(rid) + "' was deleted by User ID #" + strconv.Itoa(user.ID) + ".")
|
|
|
|
if is_js == "0" {
|
|
//http.Redirect(w,r, "/user/" + strconv.Itoa(uid), http.StatusSeeOther)
|
|
} else {
|
|
fmt.Fprintf(w,"{'success': '1'}")
|
|
}
|
|
}
|
|
|
|
func route_ban(w http.ResponseWriter, r *http.Request) {
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if !user.Perms.BanUsers {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/ban/"):])
|
|
if err != nil {
|
|
LocalError("The provided User ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
var uname string
|
|
err = db.QueryRow("SELECT name from users where uid = ?", uid).Scan(&uname)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The user you're trying to ban no longer exists.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
confirm_msg := "Are you sure you want to ban '" + uname + "'?"
|
|
yousure := AreYouSure{"/users/ban/submit/" + strconv.Itoa(uid),confirm_msg}
|
|
|
|
pi := Page{"Ban User",user,noticeList,tList,yousure}
|
|
templates.ExecuteTemplate(w,"areyousure.html", pi)
|
|
}
|
|
|
|
func route_ban_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
if !user.Perms.BanUsers {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/ban/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided User ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
var group int
|
|
var is_super_admin bool
|
|
err = db.QueryRow("SELECT `group`, `is_super_admin` from `users` where `uid` = ?", uid).Scan(&group, &is_super_admin)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The user you're trying to ban no longer exists.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if is_super_admin || groups[group].Is_Admin || groups[group].Is_Mod {
|
|
LocalError("You may not ban another staff member.",w,r,user)
|
|
return
|
|
}
|
|
if uid == user.ID {
|
|
LocalError("You may not ban yourself.",w,r,user)
|
|
return
|
|
}
|
|
if uid == -2 {
|
|
LocalError("You may not ban me. Fine, I will offer up some guidance unto thee. Come to my lair, young one. /arcane-tower/",w,r,user)
|
|
return
|
|
}
|
|
|
|
if groups[group].Is_Banned {
|
|
LocalError("The user you're trying to unban is already banned.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = change_group_stmt.Exec(4, uid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
|
|
}
|
|
|
|
func route_unban(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Perms.BanUsers {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/unban/"):])
|
|
if err != nil {
|
|
LocalError("The provided User ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
var uname string
|
|
var group int
|
|
err = db.QueryRow("SELECT `name`, `group` from users where `uid` = ?", uid).Scan(&uname, &group)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The user you're trying to unban no longer exists.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if !groups[group].Is_Banned {
|
|
LocalError("The user you're trying to unban isn't banned.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = change_group_stmt.Exec(default_group, uid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
|
|
}
|
|
|
|
func route_activate(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Perms.ActivateUsers {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
uid, err := strconv.Atoi(r.URL.Path[len("/users/activate/"):])
|
|
if err != nil {
|
|
LocalError("The provided User ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
var uname string
|
|
var active bool
|
|
err = db.QueryRow("SELECT `name`, `active` from users where `uid` = ?", uid).Scan(&uname, &active)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The account you're trying to activate no longer exists.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if active {
|
|
LocalError("The account you're trying to activate has already been activated.",w,r,user)
|
|
return
|
|
}
|
|
_, err = activate_user_stmt.Exec(uid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = change_group_stmt.Exec(default_group, uid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
http.Redirect(w,r,"/users/" + strconv.Itoa(uid),http.StatusSeeOther)
|
|
}
|
|
|
|
/* Control Panel*/
|
|
func route_panel(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
pi := Page{"Control Panel Dashboard",user,noticeList,tList,0}
|
|
templates.ExecuteTemplate(w,"panel-dashboard.html", pi)
|
|
}
|
|
|
|
func route_panel_forums(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageForums {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var forumList []interface{}
|
|
for _, forum := range forums {
|
|
if forum.ID > -1 {
|
|
forumList = append(forumList, forum)
|
|
}
|
|
}
|
|
|
|
pi := Page{"Forum Manager",user,noticeList,forumList,0}
|
|
templates.ExecuteTemplate(w,"panel-forums.html", pi)
|
|
}
|
|
|
|
func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request){
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageForums {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
fname := r.PostFormValue("forum-name")
|
|
res, err := create_forum_stmt.Exec(fname)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
lastId, err := res.LastInsertId()
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
forums[int(lastId)] = Forum{int(lastId),fname,true,"",0,"",0,""}
|
|
http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_forums_delete(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageForums {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
fid, err := strconv.Atoi(r.URL.Path[len("/panel/forums/delete/"):])
|
|
if err != nil {
|
|
LocalError("The provided Forum ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, ok = forums[fid];
|
|
if !ok {
|
|
LocalError("The forum you're trying to delete doesn't exist.",w,r,user)
|
|
return
|
|
}
|
|
|
|
confirm_msg := "Are you sure you want to delete the '" + forums[fid].Name + "' forum?"
|
|
yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid),confirm_msg}
|
|
|
|
pi := Page{"Delete Forum",user,noticeList,tList,yousure}
|
|
templates.ExecuteTemplate(w,"areyousure.html", pi)
|
|
}
|
|
|
|
func route_panel_forums_delete_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageForums {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
fid, err := strconv.Atoi(r.URL.Path[len("/panel/forums/delete/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided Forum ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, ok = forums[fid];
|
|
if !ok {
|
|
LocalError("The forum you're trying to delete doesn't exist.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = delete_forum_stmt.Exec(fid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
// Remove this forum from the forum cache
|
|
delete(forums,fid);
|
|
http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageForums {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
fid, err := strconv.Atoi(r.URL.Path[len("/panel/forums/edit/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided Forum ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
forum_name := r.PostFormValue("edit_item")
|
|
forum, ok := forums[fid];
|
|
if !ok {
|
|
LocalError("The forum you're trying to edit doesn't exist.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = update_forum_stmt.Exec(forum_name, fid)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
forum.Name = forum_name
|
|
forums[fid] = forum
|
|
|
|
http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_settings(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.EditSettings {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var settingList map[string]interface{} = make(map[string]interface{})
|
|
rows, err := db.Query("SELECT name, content, type FROM settings")
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
var sname string
|
|
var scontent string
|
|
var stype string
|
|
for rows.Next() {
|
|
err := rows.Scan(&sname, &scontent, &stype)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if stype == "list" {
|
|
llist := settingLabels[sname]
|
|
labels := strings.Split(llist,",")
|
|
conv, err := strconv.Atoi(scontent)
|
|
if err != nil {
|
|
LocalError("The setting '" + sname + "' can't be converted to an integer",w,r,user)
|
|
return
|
|
}
|
|
scontent = labels[conv - 1]
|
|
} else if stype == "bool" {
|
|
if scontent == "1" {
|
|
scontent = "Yes"
|
|
} else {
|
|
scontent = "No"
|
|
}
|
|
}
|
|
settingList[sname] = scontent
|
|
}
|
|
err = rows.Err()
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
pi := Page{"Setting Manager",user, noticeList,tList,settingList}
|
|
templates.ExecuteTemplate(w,"panel-settings.html", pi)
|
|
}
|
|
|
|
func route_panel_setting(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.EditSettings {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
setting := Setting{"","","",""}
|
|
setting.Name = r.URL.Path[len("/panel/settings/edit/"):]
|
|
|
|
err := db.QueryRow("SELECT content, type from settings where name = ?", setting.Name).Scan(&setting.Content, &setting.Type)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The setting you want to edit doesn't exist.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
var itemList []interface{}
|
|
if setting.Type == "list" {
|
|
llist, ok := settingLabels[setting.Name]
|
|
if !ok {
|
|
LocalError("The labels for this setting don't exist",w,r,user)
|
|
return
|
|
}
|
|
|
|
conv, err := strconv.Atoi(setting.Content)
|
|
if err != nil {
|
|
LocalError("The value of this setting couldn't be converted to an integer",w,r,user)
|
|
return
|
|
}
|
|
|
|
labels := strings.Split(llist,",")
|
|
for index, label := range labels {
|
|
itemList = append(itemList, OptionLabel{
|
|
Label: label,
|
|
Value: index + 1,
|
|
Selected: conv == (index + 1),
|
|
})
|
|
}
|
|
}
|
|
|
|
pi := Page{"Edit Setting",user,noticeList,itemList,setting}
|
|
templates.ExecuteTemplate(w,"panel-setting.html", pi)
|
|
}
|
|
|
|
func route_panel_setting_edit(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.EditSettings {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
LocalError("Bad Form", w, r, user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
var stype string
|
|
var sconstraints string
|
|
sname := r.URL.Path[len("/panel/settings/edit/submit/"):]
|
|
scontent := r.PostFormValue("setting-value")
|
|
|
|
err = db.QueryRow("SELECT name, type, constraints from settings where name = ?", sname).Scan(&sname, &stype, &sconstraints)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The setting you want to edit doesn't exist.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if stype == "bool" {
|
|
if scontent == "on" || scontent == "1" {
|
|
scontent = "1"
|
|
} else {
|
|
scontent = "0"
|
|
}
|
|
}
|
|
|
|
_, err = update_setting_stmt.Exec(scontent, sname)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
errmsg := parseSetting(sname, scontent, stype, sconstraints)
|
|
if errmsg != "" {
|
|
LocalError(errmsg,w,r,user)
|
|
return
|
|
}
|
|
http.Redirect(w,r,"/panel/settings/",http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_plugins(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManagePlugins {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var pluginList []interface{}
|
|
for _, plugin := range plugins {
|
|
pluginList = append(pluginList, plugin)
|
|
}
|
|
|
|
pi := Page{"Plugin Manager",user,noticeList,pluginList,0}
|
|
templates.ExecuteTemplate(w,"panel-plugins.html", pi)
|
|
}
|
|
|
|
func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request){
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManagePlugins {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
uname := r.URL.Path[len("/panel/plugins/activate/"):]
|
|
plugin, ok := plugins[uname]
|
|
if !ok {
|
|
LocalError("The plugin isn't registered in the system",w,r,user)
|
|
return
|
|
}
|
|
|
|
var active bool
|
|
err := db.QueryRow("SELECT active from plugins where uname = ?", uname).Scan(&active)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if plugins[uname].Activate != nil {
|
|
err = plugins[uname].Activate()
|
|
if err != nil {
|
|
LocalError(err.Error(),w,r,user)
|
|
return
|
|
}
|
|
}
|
|
|
|
has_plugin := err != sql.ErrNoRows
|
|
if has_plugin {
|
|
if active {
|
|
LocalError("The plugin is already active",w,r,user)
|
|
return
|
|
}
|
|
_, err = update_plugin_stmt.Exec(1, uname)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
} else {
|
|
_, err := add_plugin_stmt.Exec(uname,1)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Print("Activating plugin '" + plugin.Name + "'")
|
|
plugin.Active = true
|
|
plugins[uname] = plugin
|
|
plugins[uname].Init()
|
|
http.Redirect(w,r,"/panel/plugins/",http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_plugins_deactivate(w http.ResponseWriter, r *http.Request){
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManagePlugins {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
uname := r.URL.Path[len("/panel/plugins/deactivate/"):]
|
|
plugin, ok := plugins[uname]
|
|
if !ok {
|
|
LocalError("The plugin isn't registered in the system",w,r,user)
|
|
return
|
|
}
|
|
|
|
var active bool
|
|
err := db.QueryRow("SELECT active from plugins where uname = ?", uname).Scan(&active)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The plugin you're trying to deactivate isn't active",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if !active {
|
|
LocalError("The plugin you're trying to deactivate isn't active",w,r,user)
|
|
return
|
|
}
|
|
_, err = update_plugin_stmt.Exec(0, uname)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
plugin.Active = false
|
|
plugins[uname] = plugin
|
|
plugins[uname].Deactivate()
|
|
|
|
http.Redirect(w,r,"/panel/plugins/",http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_users(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var userList []interface{}
|
|
rows, err := db.Query("SELECT `uid`,`name`,`group`,`active`,`is_super_admin`,`avatar` FROM users")
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
puser := User{ID: 0,}
|
|
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.Is_Super_Admin, &puser.Avatar)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
puser.Is_Admin = puser.Is_Super_Admin || groups[puser.Group].Is_Admin
|
|
puser.Is_Super_Mod = puser.Is_Admin || groups[puser.Group].Is_Mod
|
|
puser.Is_Mod = puser.Is_Super_Mod
|
|
puser.Is_Banned = groups[puser.Group].Is_Banned
|
|
if puser.Is_Banned && puser.Is_Super_Mod {
|
|
puser.Is_Banned = false
|
|
}
|
|
|
|
if puser.Avatar != "" {
|
|
if puser.Avatar[0] == '.' {
|
|
puser.Avatar = "/uploads/avatar_" + strconv.Itoa(puser.ID) + puser.Avatar
|
|
}
|
|
} else {
|
|
puser.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(puser.ID),1)
|
|
}
|
|
|
|
if groups[puser.Group].Tag != "" {
|
|
puser.Tag = groups[puser.Group].Tag
|
|
} else {
|
|
puser.Tag = ""
|
|
}
|
|
|
|
userList = append(userList, puser)
|
|
}
|
|
err = rows.Err()
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
pi := Page{"User Manager",user,noticeList,userList,0}
|
|
err = templates.ExecuteTemplate(w,"panel-users.html", pi)
|
|
if err != nil {
|
|
InternalError(err, w, r, user)
|
|
}
|
|
}
|
|
|
|
func route_panel_users_edit(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
|
if !user.Is_Super_Mod || !user.Perms.EditUser {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var err error
|
|
targetUser := User{ID: 0,}
|
|
targetUser.ID, err = strconv.Atoi(r.URL.Path[len("/panel/users/edit/"):])
|
|
if err != nil {
|
|
LocalError("The provided User ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
err = db.QueryRow("SELECT `name`, `email`, `group` from `users` where `uid` = ?", targetUser.ID).Scan(&targetUser.Name, &targetUser.Email, &targetUser.Group)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The user you're trying to edit doesn't exist.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
targetUser.Is_Admin = targetUser.Is_Super_Admin || groups[targetUser.Group].Is_Admin
|
|
targetUser.Is_Super_Mod = groups[targetUser.Group].Is_Mod || targetUser.Is_Admin
|
|
if targetUser.Is_Admin && !user.Is_Admin {
|
|
LocalError("Only administrators can edit the account of an administrator.",w,r,user)
|
|
return
|
|
}
|
|
|
|
var groupList []interface{}
|
|
for _, group := range groups {
|
|
if !user.Perms.EditUserGroupAdmin && group.Is_Admin {
|
|
continue
|
|
}
|
|
if !user.Perms.EditUserGroupSuperMod && group.Is_Mod {
|
|
continue
|
|
}
|
|
groupList = append(groupList, group)
|
|
}
|
|
|
|
pi := Page{"User Editor",user,noticeList,groupList,targetUser}
|
|
err = templates.ExecuteTemplate(w,"panel-user-edit.html", pi)
|
|
if err != nil {
|
|
InternalError(err, w, r, user)
|
|
}
|
|
}
|
|
|
|
func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request){
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.EditUser {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
var err error
|
|
targetUser := User{ID: 0,}
|
|
targetUser.ID, err = strconv.Atoi(r.URL.Path[len("/panel/users/edit/submit/"):])
|
|
if err != nil {
|
|
LocalError("The provided User ID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
err = db.QueryRow("SELECT `name`, `email`, `group` from `users` where `uid` = ?", targetUser.ID).Scan(&targetUser.Name, &targetUser.Email, &targetUser.Group)
|
|
if err == sql.ErrNoRows {
|
|
LocalError("The user you're trying to edit doesn't exist.",w,r,user)
|
|
return
|
|
} else if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
targetUser.Is_Admin = targetUser.Is_Super_Admin || groups[targetUser.Group].Is_Admin
|
|
targetUser.Is_Super_Mod = groups[targetUser.Group].Is_Mod || targetUser.Is_Admin
|
|
if targetUser.Is_Admin && !user.Is_Admin {
|
|
LocalError("Only administrators can edit the account of an administrator.",w,r,user)
|
|
return
|
|
}
|
|
|
|
newname := html.EscapeString(r.PostFormValue("user-name"))
|
|
if newname == "" {
|
|
LocalError("You didn't put in a username.", w, r, user)
|
|
return
|
|
}
|
|
|
|
newemail := html.EscapeString(r.PostFormValue("user-email"))
|
|
if newemail == "" {
|
|
LocalError("You didn't put in an email address.", w, r, user)
|
|
return
|
|
}
|
|
if (newemail != targetUser.Email) && !user.Perms.EditUserEmail {
|
|
LocalError("You need the EditUserEmail permission to edit the email address of a user.", w, r, user)
|
|
return
|
|
}
|
|
|
|
newpassword := r.PostFormValue("user-password")
|
|
if newpassword != "" && !user.Perms.EditUserPassword {
|
|
LocalError("You need the EditUserPassword permission to edit the password of a user.", w, r, user)
|
|
return
|
|
}
|
|
|
|
newgroup, err := strconv.Atoi(r.PostFormValue("user-group"))
|
|
if err != nil {
|
|
LocalError("The provided GroupID is not a valid number.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, ok = groups[newgroup]
|
|
if !ok {
|
|
LocalError("The group you're trying to place this user in doesn't exist.",w,r,user)
|
|
return
|
|
}
|
|
|
|
if !user.Perms.EditUserGroupAdmin && groups[newgroup].Is_Admin {
|
|
LocalError("You need the EditUserGroupAdmin permission to assign someone to an administrator group.",w,r,user)
|
|
return
|
|
}
|
|
if !user.Perms.EditUserGroupSuperMod && groups[newgroup].Is_Mod {
|
|
LocalError("You need the EditUserGroupAdmin permission to assign someone to a super mod group.",w,r,user)
|
|
return
|
|
}
|
|
|
|
_, err = update_user_stmt.Exec(newname,newemail,newgroup,targetUser.ID)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
if newpassword != "" {
|
|
SetPassword(targetUser.ID, newpassword)
|
|
}
|
|
|
|
http.Redirect(w,r,"/panel/users/edit/" + strconv.Itoa(targetUser.ID),http.StatusSeeOther)
|
|
}
|
|
|
|
func route_panel_groups(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var groupList []interface{}
|
|
for _, group := range groups {
|
|
groupList = append(groupList, group)
|
|
}
|
|
|
|
pi := Page{"Group Manager",user,noticeList,groupList,0}
|
|
templates.ExecuteTemplate(w,"panel-groups.html", pi)
|
|
}
|
|
|
|
func route_panel_themes(w http.ResponseWriter, r *http.Request){
|
|
user, noticeList, ok := SessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageThemes {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
|
|
var themeList []interface{}
|
|
for _, theme := range themes {
|
|
themeList = append(themeList, theme)
|
|
}
|
|
|
|
pi := Page{"Theme Manager",user,noticeList,themeList,nil}
|
|
err := templates.ExecuteTemplate(w,"panel-themes.html", pi)
|
|
if err != nil {
|
|
log.Print(err)
|
|
}
|
|
}
|
|
|
|
func route_panel_themes_default(w http.ResponseWriter, r *http.Request){
|
|
user, ok := SimpleSessionCheck(w,r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if !user.Is_Super_Mod || !user.Perms.ManageThemes {
|
|
NoPermissions(w,r,user)
|
|
return
|
|
}
|
|
if r.FormValue("session") != user.Session {
|
|
SecurityError(w,r,user)
|
|
return
|
|
}
|
|
|
|
uname := r.URL.Path[len("/panel/themes/default/"):]
|
|
theme, ok := themes[uname]
|
|
if !ok {
|
|
LocalError("The theme isn't registered in the system",w,r,user)
|
|
return
|
|
}
|
|
if theme.Disabled {
|
|
LocalError("You must not enable this theme",w,r,user)
|
|
return
|
|
}
|
|
|
|
var isDefault bool
|
|
err := db.QueryRow("SELECT `default` from `themes` where `uname` = ?", uname).Scan(&isDefault)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
has_theme := err != sql.ErrNoRows
|
|
if has_theme {
|
|
if isDefault {
|
|
LocalError("The theme is already active",w,r,user)
|
|
return
|
|
}
|
|
_, err = update_theme_stmt.Exec(1, uname)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
} else {
|
|
_, err := add_theme_stmt.Exec(uname,1)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
}
|
|
|
|
_, err = update_theme_stmt.Exec(0, defaultTheme)
|
|
if err != nil {
|
|
InternalError(err,w,r,user)
|
|
return
|
|
}
|
|
|
|
log.Print("Setting theme '" + theme.Name + "' as the default theme")
|
|
theme.Active = true
|
|
themes[uname] = theme
|
|
|
|
dTheme, ok := themes[defaultTheme]
|
|
if !ok {
|
|
log.Fatal("The default theme is missing")
|
|
return
|
|
}
|
|
dTheme.Active = false
|
|
themes[defaultTheme] = dTheme
|
|
|
|
defaultTheme = uname
|
|
reset_template_overrides()
|
|
add_theme_static_files(uname)
|
|
map_theme_templates(theme)
|
|
|
|
http.Redirect(w,r,"/panel/themes/",http.StatusSeeOther)
|
|
}
|