diff --git a/.gitignore b/.gitignore index dbc49e4c..b515f1d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -uploads/* +uploads/avatar_* bin/* \ No newline at end of file diff --git a/README.md b/README.md index cd5df112..365cea0c 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/config.go b/config.go index 1f4f3ad6..edc05845 100644 --- a/config.go +++ b/config.go @@ -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 uncategorised_forum_visible = true \ No newline at end of file +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" \ No newline at end of file diff --git a/data.sql b/data.sql index daa44bc8..662e01f2 100644 --- a/data.sql +++ b/data.sql @@ -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`) diff --git a/forum.go b/forum.go index e100ca9b..0913b48d 100644 --- a/forum.go +++ b/forum.go @@ -16,4 +16,5 @@ type ForumSimple struct { ID int Name string + Active bool } \ No newline at end of file diff --git a/grosolo.exe b/grosolo.exe index 25dcd8a4..3739202c 100644 Binary files a/grosolo.exe and b/grosolo.exe differ diff --git a/grosolo.exe~ b/grosolo.exe~ index f9ba9b81..884a6c47 100644 Binary files a/grosolo.exe~ and b/grosolo.exe~ differ diff --git a/group.go b/group.go index 95a4a2c4..63ff708c 100644 --- a/group.go +++ b/group.go @@ -7,4 +7,5 @@ type Group struct Permissions string Is_Admin bool Is_Banned bool + Tag string } diff --git a/images/admin-tags.PNG b/images/admin-tags.PNG new file mode 100644 index 00000000..750a9f5f Binary files /dev/null and b/images/admin-tags.PNG differ diff --git a/images/profiles.PNG b/images/profiles.PNG new file mode 100644 index 00000000..1e63c3f8 Binary files /dev/null and b/images/profiles.PNG differ diff --git a/main.go b/main.go index f2b2bdad..7a7b726b 100644 --- a/main.go +++ b/main.go @@ -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) diff --git a/mod_routes.go b/mod_routes.go new file mode 100644 index 00000000..5a7c3753 --- /dev/null +++ b/mod_routes.go @@ -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) +} diff --git a/reply.go b/reply.go index 3362e407..ab54c285 100644 --- a/reply.go +++ b/reply.go @@ -14,4 +14,6 @@ type Reply struct LastEditBy int Avatar string Css template.CSS + ContentLines int + Tag string } diff --git a/routes.go b/routes.go index 8d794de8..3dc3a001 100644 --- a/routes.go +++ b/routes.go @@ -103,11 +103,15 @@ func route_topics(w http.ResponseWriter, r *http.Request){ } else { status = "open" } - if avatar != "" && avatar[0] == '.' { - avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar + 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] == '.' { - avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar + 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] == '.' { - topic.Avatar = "/uploads/avatar_" + strconv.Itoa(topic.CreatedBy) + topic.Avatar + 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] == '.' { - replyAvatar = "/uploads/avatar_" + strconv.Itoa(replyCreatedBy) + replyAvatar + 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() @@ -311,7 +342,109 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ InternalError(err, w, r, user) } } + +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 is_js == "0" { - //http.Redirect(w,r, "/topic/" + strconv.Itoa(tid), http.StatusSeeOther) + if success != 1 { + 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) } 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) -} \ No newline at end of file diff --git a/templates/menu.html b/templates/menu.html index 5364340a..93f47338 100644 --- a/templates/menu.html +++ b/templates/menu.html @@ -6,7 +6,7 @@ {{ if .CurrentUser.Loggedin }} - + {{ if .CurrentUser.Is_Admin}}{{end}} {{ else }} diff --git a/templates/profile.html b/templates/profile.html new file mode 100644 index 00000000..cc05f994 --- /dev/null +++ b/templates/profile.html @@ -0,0 +1,39 @@ +{{template "header.html" . }} +
+
+
{{.Something.Name}}
+
+ Add Friend + {{if .CurrentUser.Is_Admin}}Ban{{end}} + Report +
+
+
+
Comments
+
+
+ {{range $index, $element := .ItemList}} +
+ {{$element.ContentHtml}} +

+ {{$element.CreatedByName}} + {{if $.CurrentUser.Is_Admin}} + {{end}} + + {{ if $element.Tag }}{{$element.Tag}}{{end}} +
{{end}} +
+{{if not .CurrentUser.Is_Banned}} +
+
+ +
+
+
+
+
+
+ + +{{end}} +{{template "footer.html" . }} \ No newline at end of file diff --git a/templates/topic.html b/templates/topic.html index b7e88d75..85e88923 100644 --- a/templates/topic.html +++ b/templates/topic.html @@ -21,23 +21,25 @@
-
+
{{.Something.Content}}

- {{.Something.CreatedByName}} + {{.Something.CreatedByName}} + {{if .Something.Tag}}{{.Something.Tag}}{{end}}

{{range $index, $element := .ItemList}} -
+
{{$element.ContentHtml}}

- {{$element.CreatedByName}} + {{$element.CreatedByName}} {{if $.CurrentUser.Is_Admin}} {{end}} -
{{ end }} + {{if $element.Tag}}{{$element.Tag}}{{end}} +
{{end}}
{{if not .CurrentUser.Is_Banned}}
diff --git a/topic.go b/topic.go index cc8a5bba..a0279456 100644 --- a/topic.go +++ b/topic.go @@ -29,4 +29,6 @@ type TopicUser struct CreatedByName string Avatar string Css template.CSS + ContentLines int + Tag string } diff --git a/uploads/filler.txt b/uploads/filler.txt new file mode 100644 index 00000000..20e14b1e --- /dev/null +++ b/uploads/filler.txt @@ -0,0 +1 @@ +This file is here so that Git will include this folder in the repository. \ No newline at end of file diff --git a/user.go b/user.go index 033986f2..b22e6f5c 100644 --- a/user.go +++ b/user.go @@ -1,5 +1,6 @@ package main import "log" +import "strings" import "strconv" import "net/http" import "golang.org/x/crypto/bcrypt" @@ -72,8 +73,12 @@ 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] == '.' { - user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar + 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")