Added the profile system.

Added the profile comment system.
Added group tags.
Improved the avatar placement logic.
Users without avatars will now get noavatars. Customisable in config.go
Only admins can see the Control Panel link now.
Fixed a problem with Git not including /uploads/ which can crash the program.

Database changes:
Added active column to forums.
Added tag column to users_groups.
Added the users_replies table.
Commented out the replies_reports table.
This commit is contained in:
Azareal 2016-12-07 09:34:09 +00:00
parent 30ecdf8d93
commit 72d7beefe9
20 changed files with 781 additions and 396 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
uploads/*
uploads/avatar_*
bin/*

View File

@ -61,8 +61,8 @@ Add a settings system.
Add a plugin system.
Revamp the system for serving static files to make it much faster.
Tweak the CSS to make it responsive.
Add a forum cache.
Nest the moderation routes to possibly speed routing up a little...?
Add a friend system.

View File

@ -12,5 +12,7 @@ var max_request_size = 5 * megabyte
// Misc
var default_route = route_topics
var staff_css = "background-color: #ffeaff;background-position: left;"
var staff_css = "background-color: #ffeaff;"
var uncategorised_forum_visible = true
var siteurl = "localhost:8080"
var noavatar = "https://api.adorable.io/avatars/285/{id}@" + siteurl + ".png"

View File

@ -21,12 +21,14 @@ CREATE TABLE `users_groups`(
`permissions` text not null,
`is_admin` tinyint DEFAULT 0 not null,
`is_banned` tinyint DEFAULT 0 not null,
`tag` varchar(50) DEFAULT '' not null,
primary key(`gid`)
);
CREATE TABLE `forums`(
`fid` int not null AUTO_INCREMENT,
`name` varchar(100) not null,
`active` tinyint DEFAULT 1 not null,
`lastTopic` varchar(100) DEFAULT '' not null,
`lastTopicID` int DEFAULT 0 not null,
`lastReplyer` varchar(100) DEFAULT '' not null,
@ -61,17 +63,29 @@ CREATE TABLE `replies`(
primary key(`rid`)
);
CREATE TABLE `replies_reports` (
CREATE TABLE `users_replies`(
`rid` int not null AUTO_INCREMENT,
`uid` int not null,
`content` text not null,
`parsed_content` text not null,
`createdAt` datetime not null,
`createdBy` int not null,
`lastEdit` int not null,
`lastEditBy` int not null,
primary key(`rid`)
);
/*CREATE TABLE `replies_reports` (
`rid` int not null AUTO_INCREMENT,
`reportedBy` int not null,
`reportedContent` text not null,
`resolved` tinyint DEFAULT 0 not null,
primary key(`rid`)
);
);*/
INSERT INTO users(`name`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`)
VALUES ('Admin',1,1,NOW(),NOW());
INSERT INTO users_groups(`name`,`permissions`,`is_admin`) VALUES ('Administrator','{}',1);
INSERT INTO users_groups(`name`,`permissions`,`is_admin`,`tag`) VALUES ('Admin','{}',1,"Admin");
INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{}');
INSERT INTO forums(`name`,`lastTopicTime`) VALUES ('General',NOW());
INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`)

View File

@ -16,4 +16,5 @@ type ForumSimple struct
{
ID int
Name string
Active bool
}

Binary file not shown.

Binary file not shown.

View File

@ -7,4 +7,5 @@ type Group struct
Permissions string
Is_Admin bool
Is_Banned bool
Tag string
}

BIN
images/admin-tags.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

BIN
images/profiles.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

35
main.go
View File

@ -40,6 +40,9 @@ var set_avatar_stmt *sql.Stmt
var set_username_stmt *sql.Stmt
var register_stmt *sql.Stmt
var username_exists_stmt *sql.Stmt
var create_profile_reply_stmt *sql.Stmt
var edit_profile_reply_stmt *sql.Stmt
var delete_profile_reply_stmt *sql.Stmt
var create_forum_stmt *sql.Stmt
var delete_forum_stmt *sql.Stmt
@ -185,6 +188,24 @@ func init_database(err error) {
log.Fatal(err)
}
log.Print("Preparing create_profile_reply statement.")
create_profile_reply_stmt, err = db.Prepare("INSERT INTO users_replies(uid,content,parsed_content,createdAt,createdBy) VALUES(?,?,?,NOW(),?)")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing edit_profile_reply statement.")
edit_profile_reply_stmt, err = db.Prepare("UPDATE users_replies SET content = ?, parsed_content = ? WHERE rid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing delete_profile_reply statement.")
delete_profile_reply_stmt, err = db.Prepare("DELETE FROM users_replies WHERE rid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing create_forum statement.")
create_forum_stmt, err = db.Prepare("INSERT INTO forums(name) VALUES(?)")
if err != nil {
@ -204,15 +225,15 @@ func init_database(err error) {
}
log.Print("Loading the usergroups.")
rows, err := db.Query("SELECT gid,name,permissions,is_admin,is_banned FROM users_groups")
rows, err := db.Query("SELECT gid,name,permissions,is_admin,is_banned,tag FROM users_groups")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
group := Group{0,"","",false,false}
err := rows.Scan(&group.ID, &group.Name, &group.Permissions, &group.Is_Admin, &group.Is_Banned)
group := Group{0,"","",false,false,""}
err := rows.Scan(&group.ID, &group.Name, &group.Permissions, &group.Is_Admin, &group.Is_Banned, &group.Tag)
if err != nil {
log.Fatal(err)
}
@ -224,7 +245,7 @@ func init_database(err error) {
}
log.Print("Loading the forums.")
rows, err = db.Query("SELECT fid, name, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime FROM forums")
rows, err = db.Query("SELECT fid, name, active, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime FROM forums")
if err != nil {
log.Fatal(err)
}
@ -232,7 +253,7 @@ func init_database(err error) {
for rows.Next() {
forum := Forum{0,"",true,"",0,"",0,""}
err := rows.Scan(&forum.ID, &forum.Name, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
err := rows.Scan(&forum.ID, &forum.Name, &forum.Active, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
if err != nil {
log.Fatal(err)
}
@ -333,6 +354,10 @@ func main(){
http.HandleFunc("/user/edit/avatar/submit/", route_account_own_edit_avatar_submit)
http.HandleFunc("/user/edit/username/", route_account_own_edit_username)
http.HandleFunc("/user/edit/username/submit/", route_account_own_edit_username_submit)
http.HandleFunc("/user/", route_profile)
http.HandleFunc("/profile/reply/create/", route_profile_reply_create)
http.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit)
http.HandleFunc("/profile/reply/delete/submit/", route_profile_reply_delete_submit)
//http.HandleFunc("/user/:id/edit/", route_logout)
//http.HandleFunc("/user/:id/ban/", route_logout)

485
mod_routes.go Normal file
View File

@ -0,0 +1,485 @@
package main
import "log"
import "fmt"
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 := SessionCheck(w,r)
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.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
if user.Is_Banned {
BannedJSQ(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")
var is_closed bool
if topic_status == "closed" {
is_closed = true
} else {
is_closed = false
}
topic_content := html.EscapeString(r.PostFormValue("topic_content"))
_, err = edit_topic_stmt.Exec(topic_name, topic_content, parse_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 := SessionCheck(w,r)
if !user.Is_Admin {
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
}
err = db.QueryRow("SELECT tid from topics where tid = ?", tid).Scan(&tid)
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)
}
func route_stick_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
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.Is_Admin {
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(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 := SessionCheck(w,r)
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.Is_Admin {
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
err = db.QueryRow("SELECT tid from replies where rid = ?", rid).Scan(&tid)
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'}")
}
}
func route_profile_reply_edit_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
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.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
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
}
content := html.EscapeString(r.PostFormValue("edit_item"))
_, err = edit_profile_reply_stmt.Exec(content, parse_message(content), rid)
if err != nil {
InternalError(err,w,r,user)
return
}
// Get the Reply ID..
var uid int
err = db.QueryRow("select tid from users_replies where rid = ?", rid).Scan(&uid)
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 := SessionCheck(w,r)
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.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
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
}
_, 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_panel_forums(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
var forumList map[int]interface{} = make(map[int]interface{})
currentID := 0
for _, forum := range forums {
if forum.ID > -1 {
forumList[currentID] = forum
currentID++
}
}
pi := Page{"Forum Manager","panel-forums",user,forumList,0}
templates.ExecuteTemplate(w,"panel-forums.html", pi)
}
func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
if !user.Is_Admin {
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","panel-forums-delete",user,tList,yousure}
templates.ExecuteTemplate(w,"areyousure.html", pi)
}
func route_panel_forums_delete_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
if !user.Is_Admin {
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)
}

View File

@ -14,4 +14,6 @@ type Reply struct
LastEditBy int
Avatar string
Css template.CSS
ContentLines int
Tag string
}

544
routes.go
View File

@ -103,11 +103,15 @@ func route_topics(w http.ResponseWriter, r *http.Request){
} else {
status = "open"
}
if avatar != "" && avatar[0] == '.' {
if avatar != "" {
if avatar[0] == '.' {
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
}
} else {
avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1)
}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar, ""}
topicList[currentID] = TopicUser{tid,title,content,createdBy,is_closed,sticky, createdAt,parentID,status,name,avatar,"",0,""}
currentID++
}
err = rows.Err()
@ -174,11 +178,15 @@ func route_forum(w http.ResponseWriter, r *http.Request){
} else {
status = "open"
}
if avatar != "" && avatar[0] == '.' {
if avatar != "" {
if avatar[0] == '.' {
avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar
}
} else {
avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1)
}
topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar, ""}
topicList[currentID] = TopicUser{tid,title,content,createdBy,is_closed,sticky, createdAt,parentID,status,name,avatar,"",0,""}
currentID++
}
err = rows.Err()
@ -231,6 +239,8 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
replyLastEditBy int
replyAvatar string
replyCss template.CSS
replyLines int
replyTag string
is_super_admin bool
group int
@ -239,7 +249,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
)
replyList = make(map[int]interface{})
currentID = 0
topic := TopicUser{0,"","",0,false,false,"",0,"","","",no_css_tmpl}
topic := TopicUser{0,"","",0,false,false,"",0,"","","",no_css_tmpl,0,""}
topic.ID, err = strconv.Atoi(r.URL.Path[len("/topic/"):])
if err != nil {
@ -259,17 +269,28 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
}
topic.Content = template.HTML(parse_message(content))
topic.ContentLines = strings.Count(content,"\n")
if topic.Is_Closed {
topic.Status = "closed"
} else {
topic.Status = "open"
}
if topic.Avatar != "" && topic.Avatar[0] == '.' {
if topic.Avatar != "" {
if topic.Avatar[0] == '.' {
topic.Avatar = "/uploads/avatar_" + strconv.Itoa(topic.CreatedBy) + topic.Avatar
}
} else {
topic.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topic.CreatedBy),1)
}
if is_super_admin || groups[group].Is_Admin {
topic.Css = staff_css_tmpl
}
if groups[group].Tag != "" {
topic.Tag = groups[group].Tag
} else {
topic.Tag = ""
}
// Get the replies..
//rows, err := db.Query("select rid, content, createdBy, createdAt from replies where tid = ?", tid)
@ -287,16 +308,26 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
return
}
replyLines = strings.Count(replyContent,"\n")
if is_super_admin || groups[group].Is_Admin {
replyCss = staff_css_tmpl
} else {
replyCss = no_css_tmpl
}
if replyAvatar != "" && replyAvatar[0] == '.' {
if replyAvatar != "" {
if replyAvatar[0] == '.' {
replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar
}
} else {
replyAvatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyCreatedBy),1)
}
if groups[group].Tag != "" {
replyTag = groups[group].Tag
} else {
replyTag = ""
}
replyList[currentID] = Reply{rid,topic.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss}
replyList[currentID] = Reply{rid,topic.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss,replyLines,replyTag}
currentID++
}
err = rows.Err()
@ -312,6 +343,108 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
}
}
func route_profile(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
var(
err error
rid int
replyContent string
replyCreatedBy int
replyCreatedByName string
replyCreatedAt string
replyLastEdit int
replyLastEditBy int
replyAvatar string
replyCss template.CSS
replyLines int
replyTag string
is_super_admin bool
group int
currentID int
replyList map[int]interface{}
)
replyList = make(map[int]interface{})
currentID = 0
puser := User{0,"",0,false,false,false,"",false,""}
puser.ID, err = strconv.Atoi(r.URL.Path[len("/user/"):])
if err != nil {
LocalError("The provided TopicID is not a valid number.",w,r,user)
return
}
// Fetch the user data
err = db.QueryRow("SELECT `name`, `group`, `is_super_admin`, `avatar` FROM `users` WHERE `uid` = ?", puser.ID).Scan(&puser.Name, &puser.Group, &puser.Is_Super_Admin, &puser.Avatar)
if err == sql.ErrNoRows {
NotFound(w,r,user)
return
} else if err != nil {
InternalError(err,w,r,user)
return
}
puser.Is_Admin = puser.Is_Super_Admin
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)
}
// Get the replies..
rows, err := db.Query("select users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group from users_replies left join users ON users_replies.createdBy = users.uid where users_replies.uid = ?", puser.ID)
if err != nil {
InternalError(err,w,r,user)
return
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &is_super_admin, &group)
if err != nil {
InternalError(err,w,r,user)
return
}
replyLines = strings.Count(replyContent,"\n")
if is_super_admin || groups[group].Is_Admin {
replyCss = staff_css_tmpl
} else {
replyCss = no_css_tmpl
}
if replyAvatar != "" {
if replyAvatar[0] == '.' {
replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar
}
} else {
replyAvatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyCreatedBy),1)
}
if groups[group].Tag != "" {
replyTag = groups[group].Tag
} else if puser.ID == replyCreatedBy {
replyTag = "Profile Owner"
} else {
replyTag = ""
}
replyList[currentID] = Reply{rid,puser.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss,replyLines,replyTag}
currentID++
}
err = rows.Err()
if err != nil {
InternalError(err,w,r,user)
return
}
pi := Page{puser.Name + "'s Profile","profile",user,replyList,puser}
err = templates.ExecuteTemplate(w,"profile.html", pi)
if err != nil {
InternalError(err, w, r, user)
}
}
func route_topic_create(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if user.Is_Banned {
@ -445,226 +578,67 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
}
}
func route_edit_topic(w http.ResponseWriter, r *http.Request) {
func route_profile_reply_create(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
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.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
if !user.Loggedin {
LoginRequired(w,r,user)
return
}
if user.Is_Banned {
BannedJSQ(w,r,user,is_js)
Banned(w,r,user)
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")
//log.Print(topic_name)
//log.Print(topic_status)
var is_closed bool
if topic_status == "closed" {
is_closed = true
} else {
is_closed = false
}
topic_content := html.EscapeString(r.PostFormValue("topic_content"))
//log.Print(topic_content)
_, err = edit_topic_stmt.Exec(topic_name, topic_content, parse_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 := SessionCheck(w,r)
if !user.Is_Admin {
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
}
err = db.QueryRow("SELECT tid from topics where tid = ?", tid).Scan(&tid)
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)
}
func route_stick_topic(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
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.Is_Admin {
NoPermissionsJSQ(w,r,user,is_js)
return
}
rid, err := strconv.Atoi(r.URL.Path[len("/reply/edit/submit/"):])
success := 1
uid, err := strconv.Atoi(r.PostFormValue("uid"))
if err != nil {
LocalError("The provided Reply ID is not a valid number.",w,r,user)
log.Print(err)
success = 0
errmsg := "Unable to create the reply"
pi := Page{"Error","error",user,tList,errmsg}
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
return
}
content := html.EscapeString(r.PostFormValue("edit_item"))
_, err = edit_reply_stmt.Exec(content, parse_message(content), rid)
_, err = create_profile_reply_stmt.Exec(uid,html.EscapeString(r.PostFormValue("reply-content")),parse_message(html.EscapeString(r.PostFormValue("reply-content"))),user.ID)
if err != nil {
InternalError(err,w,r,user)
return
log.Print(err)
success = 0
}
// 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 := SessionCheck(w,r)
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.Is_Admin {
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
err = db.QueryRow("SELECT tid from replies where rid = ?", rid).Scan(&tid)
var user_name string
err = db.QueryRow("select name from users where uid = ?", uid).Scan(&user_name)
if err == sql.ErrNoRows {
LocalErrorJSQ("The reply you tried to delete doesn't exist.",w,r,user,is_js)
return
log.Print(err)
success = 0
} else if err != nil {
InternalErrorJSQ(err,w,r,user,is_js)
InternalError(err,w,r,user)
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 success != 1 {
errmsg := "Unable to create the reply"
pi := Page{"Error","error",user,tList,errmsg}
if is_js == "0" {
//http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther)
var b bytes.Buffer
templates.ExecuteTemplate(&b,"error.html", pi)
errpage := b.String()
w.WriteHeader(500)
fmt.Fprintln(w,errpage)
} else {
fmt.Fprintf(w,"{'success': '1'}")
http.Redirect(w, r, "/user/" + strconv.Itoa(uid), http.StatusSeeOther)
}
}
@ -990,9 +964,7 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
var salt string
var session string
username := html.EscapeString(r.PostFormValue("username"))
//log.Print("Username: " + username)
password := r.PostFormValue("password")
//log.Print("Password: " + password)
err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
if err == sql.ErrNoRows {
@ -1028,15 +1000,11 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
SetPassword(uid, password)
} else { // Normal login..
password = password + salt
//hashed_password, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
InternalError(err,w,r,user)
return
}
//log.Print("Hashed: " + string(hashed_password))
//log.Print("Real: " + real_password)
//if string(hashed_password) != real_password {
err := bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password))
if err == bcrypt.ErrMismatchedHashAndPassword {
errmsg := "That's not the correct password."
@ -1066,8 +1034,6 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
return
}
//log.Print("Successful Login")
//log.Print("Session: " + session)
cookie := http.Cookie{Name: "uid",Value: strconv.Itoa(uid),Path: "/",MaxAge: year}
http.SetCookie(w,&cookie)
cookie = http.Cookie{Name: "session",Value: session,Path: "/",MaxAge: year}
@ -1173,163 +1139,3 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w,&cookie)
http.Redirect(w,r, "/", http.StatusSeeOther)
}
func route_panel_forums(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
NoPermissions(w,r,user)
return
}
var forumList map[int]interface{} = make(map[int]interface{})
currentID := 0
for _, forum := range forums {
if forum.ID > -1 {
forumList[currentID] = forum
currentID++
}
}
pi := Page{"Forum Manager","panel-forums",user,forumList,0}
templates.ExecuteTemplate(w,"panel-forums.html", pi)
}
func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request){
user := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
if !user.Is_Admin {
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","panel-forums-delete",user,tList,yousure}
templates.ExecuteTemplate(w,"areyousure.html", pi)
}
func route_panel_forums_delete_submit(w http.ResponseWriter, r *http.Request) {
user := SessionCheck(w,r)
if !user.Is_Admin {
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 := SessionCheck(w,r)
if !user.Is_Admin {
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)
}

View File

@ -6,7 +6,7 @@
<li class="menu_create_topic"><a href="/topics/create/">Create Topic</a></li>
{{ if .CurrentUser.Loggedin }}
<li class="menu_account"><a href="/user/edit/critical/">My Account</a></li>
<li class="menu_account"><a href="/panel/forums/">Panel</a></li>
{{ if .CurrentUser.Is_Admin}}<li class="menu_account"><a href="/panel/forums/">Panel</a></li>{{end}}
<li class="menu_logout"><a href="/accounts/logout?session={{.CurrentUser.Session}}">Logout</a></li>
{{ else }}
<li class="menu_register"><a href="/accounts/create/">Register</a></li>

39
templates/profile.html Normal file
View File

@ -0,0 +1,39 @@
{{template "header.html" . }}
<div class="colblock_left" style="max-width: 220px;margin-right: 8px;">
<div class="rowitem" style="padding: 0;"><img src="{{.Something.Avatar}}" style="max-width: 100%;margin: 0;"/></div>
<div class="rowitem">{{.Something.Name}}</div>
<div class="rowitem passive">
<a class="username">Add Friend</a>
{{if .CurrentUser.Is_Admin}}<a class="username">Ban</a>{{end}}
<a class="username">Report</a>
</div>
</div>
<div class="colblock_right">
<div class="rowitem"><a>Comments</a></div>
</div>
<div class="colblock_right" style="overflow: hidden;">
{{range $index, $element := .ItemList}}
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le $element.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;background-attachment: scroll;padding-left: 136px;{{$element.Css}}{{end}}">
<span class="editable_block">{{$element.ContentHtml}}</span>
<br /><br />
<a href="/user/{{$element.CreatedBy}}" class="username">{{$element.CreatedByName}}</a>
{{if $.CurrentUser.Is_Admin}}<a href="/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a>
<a href="/profile/reply/delete/submit/{{$element.ID}}"><button class="username delete_item">Delete</button></a>{{end}}
<a href="/profile/reply/report/submit/{{$element.ID}}"><button class="username report_item">Report</button></a>
{{ if $element.Tag }}<a class="username" style="float: right;">{{$element.Tag}}</a>{{end}}
</div>{{end}}
</div>
{{if not .CurrentUser.Is_Banned}}
<div class="colblock_right">
<form action="/profile/reply/create/" method="post">
<input name="uid" value='{{.Something.ID}}' type="hidden" />
<div class="formrow">
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div>
</div>
<div class="formrow">
<div class="formitem"><button name="reply-button" class="formbutton">Create Reply</div></div>
</div>
</form>
</div>
{{end}}
{{template "footer.html" . }}

View File

@ -21,23 +21,25 @@
</form>
</div>
<div class="rowblock">
<div class="rowitem passive editable_parent" style="border-bottom: none;{{ if .Something.Avatar }}background-image: url({{ .Something.Avatar }}), url(/static/white-dot.jpg);background-position: top left;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{.Something.Css}}{{end}}">
<div class="rowitem passive editable_parent" style="border-bottom: none;{{ if .Something.Avatar }}background-image: url({{ .Something.Avatar }}), url(/static/white-dot.jpg);background-position: 0px {{if le .Something.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;background-attachment: scroll;padding-left: 136px;{{.Something.Css}}{{end}}">
<span class="hide_on_edit topic_content">{{.Something.Content}}</span>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Something.Content}}</textarea>
<br /><br />
<a class="username">{{.Something.CreatedByName}}<a/>
<a href="/user/{{.Something.CreatedBy}}" class="username">{{.Something.CreatedByName}}</a>
{{if .Something.Tag}}<a class="username" style="float: right;">{{.Something.Tag}}</a>{{end}}
</div>
</div><br />
<div class="rowblock" style="overflow: hidden;">
{{range $index, $element := .ItemList}}
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: top left;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;{{$element.Css}}{{end}}">
<div class="rowitem passive deletable_block editable_parent" style="{{ if $element.Avatar }}background-image: url({{$element.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le $element.ContentLines 5}}-1{{end}}0px;background-repeat: no-repeat, repeat-y;background-size: 128px;background-attachment: scroll;padding-left: 136px;{{$element.Css}}{{end}}">
<span class="editable_block">{{$element.ContentHtml}}</span>
<br /><br />
<a class="username">{{$element.CreatedByName}}</a>
<a href="/user/{{$element.CreatedBy}}" class="username">{{$element.CreatedByName}}</a>
{{if $.CurrentUser.Is_Admin}}<a href="/reply/edit/submit/{{$element.ID}}"><button class="username edit_item">Edit</button></a>
<a href="/reply/delete/submit/{{$element.ID}}"><button class="username delete_item">Delete</button></a>{{end}}
<a href="/reply/report/submit/{{$element.ID}}"><button class="username report_item">Report</button></a>
</div>{{ end }}
{{if $element.Tag}}<a class="username" style="float: right;">{{$element.Tag}}</a>{{end}}
</div>{{end}}
</div>
{{if not .CurrentUser.Is_Banned}}
<div class="rowblock">

View File

@ -29,4 +29,6 @@ type TopicUser struct
CreatedByName string
Avatar string
Css template.CSS
ContentLines int
Tag string
}

1
uploads/filler.txt Normal file
View File

@ -0,0 +1 @@
This file is here so that Git will include this folder in the repository.

View File

@ -1,5 +1,6 @@
package main
import "log"
import "strings"
import "strconv"
import "net/http"
import "golang.org/x/crypto/bcrypt"
@ -72,9 +73,13 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (User) {
}
user.Is_Admin = (user.Is_Super_Admin || groups[user.Group].Is_Admin)
user.Is_Banned = groups[user.Group].Is_Banned
if user.Avatar != "" && user.Avatar[0] == '.' {
if user.Avatar != "" {
if user.Avatar[0] == '.' {
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
}
} else {
user.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(user.ID),1)
}
user.Loggedin = true
/*log.Print("Logged in")
log.Print("ID: " + strconv.Itoa(user.ID))