From 771af02674a22c4ed6b90e318af18ca12fd4a2fd Mon Sep 17 00:00:00 2001 From: Azareal Date: Sat, 3 Dec 2016 13:45:08 +0000 Subject: [PATCH] Added the forums. Added the forum list. Added the forum view. Added a relative time utility function. --- README.md | 8 +- src/data.sql | 14 +++- src/forum.go | 12 +++ src/main.go | 9 +++ src/routes.go | 154 ++++++++++++++++++++++++++++++++++++-- src/run.bat | 2 +- src/templates/forum.html | 10 +++ src/templates/forums.html | 8 ++ src/templates/menu.html | 1 + src/user.go | 2 +- src/utils.go | 34 +++++++++ 11 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 src/forum.go create mode 100644 src/templates/forum.html create mode 100644 src/templates/forums.html diff --git a/README.md b/README.md index c887b5e5..291453aa 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Set the password column of your user account in the database to what you want yo # Run the program -go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go config.go +go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go forum.go config.go Alternatively, you could run the run.bat batch file on Windows. @@ -45,8 +45,6 @@ Oh my, you caught me right at the start of this project. There's nothing to see More moderation features. -Fix the bug where errors are sent off in raw HTML rather than formatted HTML. - Fix the custom pages. Add emails as a requirement for registration and add a simple anti-spam measure. @@ -64,3 +62,7 @@ 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. + +Add a group cache. diff --git a/src/data.sql b/src/data.sql index 98047405..76a8f5c0 100644 --- a/src/data.sql +++ b/src/data.sql @@ -24,6 +24,17 @@ CREATE TABLE `users_groups`( primary key(`gid`) ); +CREATE TABLE `forums`( + `fid` int not null AUTO_INCREMENT, + `name` varchar(100) not null, + `lastTopic` varchar(100) DEFAULT '' not null, + `lastTopicID` int DEFAULT 0 not null, + `lastReplyer` varchar(100) DEFAULT '' not null, + `lastReplyerID` int DEFAULT 0 not null, + `lastTopicTime` datetime not null, + primary key(`fid`) +); + CREATE TABLE `topics`( `tid` int not null AUTO_INCREMENT, `title` varchar(100) not null, @@ -34,7 +45,7 @@ CREATE TABLE `topics`( `createdBy` int not null, `is_closed` tinyint DEFAULT 0 not null, `sticky` tinyint DEFAULT 0 not null, - `parentID` int DEFAULT 0 not null, + `parentID` int DEFAULT 1 not null, primary key(`tid`) ); @@ -53,6 +64,7 @@ CREATE TABLE `replies`( 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 forums(`name`,`lastTopicTime`) VALUES ('General',NOW()); INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`) VALUES ('Test Topic','A topic automatically generated by the software.',NOW(),NOW(),1,1); diff --git a/src/forum.go b/src/forum.go new file mode 100644 index 00000000..00ca2bae --- /dev/null +++ b/src/forum.go @@ -0,0 +1,12 @@ +package main + +type Forum struct +{ + ID int + Name string + LastTopic string + LastTopicID int + LastReplyer string + LastReplyerID int + LastTopicTime string +} diff --git a/src/main.go b/src/main.go index 6cf2fb3a..23aef4b7 100644 --- a/src/main.go +++ b/src/main.go @@ -21,6 +21,7 @@ var db *sql.DB var get_session_stmt *sql.Stmt var create_topic_stmt *sql.Stmt var create_reply_stmt *sql.Stmt +var update_forum_cache_stmt *sql.Stmt var edit_topic_stmt *sql.Stmt var edit_reply_stmt *sql.Stmt var delete_reply_stmt *sql.Stmt @@ -72,6 +73,12 @@ func init_database(err error) { log.Fatal(err) } + log.Print("Preparing update_forum_cache statement.") + update_forum_cache_stmt, err = db.Prepare("UPDATE forums SET lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = NOW() WHERE fid = ?") + if err != nil { + log.Fatal(err) + } + log.Print("Preparing edit_topic statement.") edit_topic_stmt, err = db.Prepare("UPDATE topics SET title = ?, content = ?, parsed_content = ?, is_closed = ? WHERE tid = ?") if err != nil { @@ -185,6 +192,8 @@ func main(){ http.HandleFunc("/overview/", route_overview) http.HandleFunc("/topics/create/", route_topic_create) http.HandleFunc("/topics/", route_topics) + http.HandleFunc("/forums/", route_forums) + http.HandleFunc("/forum/", route_forum) http.HandleFunc("/topic/create/submit/", route_create_topic) //POST http.HandleFunc("/topic/", route_topic_id) http.HandleFunc("/reply/create/", route_create_reply) //POST diff --git a/src/routes.go b/src/routes.go index aeed503a..8303f69c 100644 --- a/src/routes.go +++ b/src/routes.go @@ -21,9 +21,6 @@ var tList map[int]interface{} // GET functions func route_overview(w http.ResponseWriter, r *http.Request){ user := SessionCheck(w,r) - NoPermissions(w, r, user) - return - pi := Page{"Overview","overview",user,tList,0} err := templates.ExecuteTemplate(w,"overview.html", pi) if err != nil { @@ -109,6 +106,129 @@ func route_topics(w http.ResponseWriter, r *http.Request){ InternalError(err, w, r, user) } } + +func route_forum(w http.ResponseWriter, r *http.Request){ + user := SessionCheck(w,r) + var( + topicList map[int]interface{} + currentID int + fname string + + tid int + title string + content string + createdBy int + is_closed bool + sticky bool + createdAt string + parentID int + status string + name string + avatar string + ) + topicList = make(map[int]interface{}) + currentID = 0 + + fid, err := strconv.Atoi(r.URL.Path[len("/forum/"):]) + if err != nil { + LocalError("The provided ForumID is not a valid number.",w,r,user) + return + } + + err = db.QueryRow("select name from forums where fid = ?", fid).Scan(&fname) + if err == sql.ErrNoRows { + pi := Page{"Error","error",user,tList,"The requested forum doesn't exist."} + + var b bytes.Buffer + templates.ExecuteTemplate(&b,"error.html", pi) + errpage := b.String() + w.WriteHeader(404) + fmt.Fprintln(w,errpage) + return + } else if err != nil { + InternalError(err,w,r,user) + return + } + + rows, err := db.Query("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid WHERE topics.parentID = ? order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC", fid) + if err != nil { + InternalError(err,w,r,user) + return + } + defer rows.Close() + + for rows.Next() { + err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar) + if err != nil { + InternalError(err,w,r,user) + return + } + + if is_closed { + status = "closed" + } else { + status = "open" + } + if avatar != "" && avatar[0] == '.' { + avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar + } + + topicList[currentID] = TopicUser{tid, title, content, createdBy, is_closed, sticky, createdAt,parentID, status, name, avatar} + currentID++ + } + err = rows.Err() + if err != nil { + InternalError(err,w,r,user) + return + } + pi := Page{fname,"forum",user,topicList,0} + err = templates.ExecuteTemplate(w,"forum.html", pi) + if err != nil { + InternalError(err, w, r, user) + } +} + +func route_forums(w http.ResponseWriter, r *http.Request){ + user := SessionCheck(w,r) + var forumList map[int]interface{} + forumList = make(map[int]interface{}) + currentID := 0 + + rows, err := db.Query("select fid, name, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime from forums") + if err != nil { + InternalError(err,w,r,user) + return + } + defer rows.Close() + + for rows.Next() { + forum := Forum{0,"","",0,"",0,""} + err := rows.Scan(&forum.ID, &forum.Name, &forum.LastTopic,&forum.LastTopicID,&forum.LastReplyer,&forum.LastReplyerID,&forum.LastTopicTime) + if err != nil { + InternalError(err,w,r,user) + return + } + + forum.LastTopicTime, err = relative_time(forum.LastTopicTime) + if err != nil { + InternalError(err,w,r,user) + return + } + + forumList[currentID] = forum + currentID++ + } + err = rows.Err() + if err != nil { + InternalError(err,w,r,user) + return + } + pi := Page{"Forum List","forums",user,forumList,0} + err = templates.ExecuteTemplate(w,"forums.html", pi) + if err != nil { + InternalError(err, w, r, user) + } +} func route_topic_id(w http.ResponseWriter, r *http.Request){ user := SessionCheck(w,r) @@ -221,8 +341,9 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) { return } success := 1 + topic_name := html.EscapeString(r.PostFormValue("topic-name")) - res, err := create_topic_stmt.Exec(html.EscapeString(r.PostFormValue("topic-name")),html.EscapeString(r.PostFormValue("topic-content")),parse_message(html.EscapeString(r.PostFormValue("topic-content"))),user.ID) + res, err := create_topic_stmt.Exec(topic_name,html.EscapeString(r.PostFormValue("topic-content")),parse_message(html.EscapeString(r.PostFormValue("topic-content"))),user.ID) if err != nil { log.Print(err) success = 0 @@ -234,6 +355,12 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) { success = 0 } + _, err = update_forum_cache_stmt.Exec(topic_name, lastId, user.Name, user.ID, 1) + if err != nil { + InternalError(err,w,r,user) + return + } + if success != 1 { errmsg := "Unable to create the topic" pi := Page{"Error","error",user,tList,errmsg} @@ -249,7 +376,6 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) { } func route_create_reply(w http.ResponseWriter, r *http.Request) { - var tid int user := SessionCheck(w,r) if !user.Loggedin { LoginRequired(w,r,user) @@ -263,7 +389,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) { } success := 1 - tid, err = strconv.Atoi(r.PostFormValue("tid")) + tid, err := strconv.Atoi(r.PostFormValue("tid")) if err != nil { log.Print(err) success = 0 @@ -285,6 +411,22 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) { success = 0 } + var topic_name string + err = db.QueryRow("select title from topics where tid = ?", tid).Scan(&topic_name) + if err == sql.ErrNoRows { + log.Print(err) + success = 0 + } else if err != nil { + InternalError(err,w,r,user) + return + } + + _, err = update_forum_cache_stmt.Exec(topic_name, tid, user.Name, user.ID, 1) + if err != nil { + InternalError(err,w,r,user) + return + } + if success != 1 { errmsg := "Unable to create the reply" pi := Page{"Error","error",user,tList,errmsg} diff --git a/src/run.bat b/src/run.bat index 675bbf78..667b228c 100644 --- a/src/run.bat +++ b/src/run.bat @@ -1,2 +1,2 @@ -go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go config.go +go run errors.go main.go pages.go reply.go routes.go topic.go user.go utils.go config.go forum.go pause \ No newline at end of file diff --git a/src/templates/forum.html b/src/templates/forum.html new file mode 100644 index 00000000..8365abad --- /dev/null +++ b/src/templates/forum.html @@ -0,0 +1,10 @@ +{{template "header.html" . }} +
+ +
+
+ {{range .ItemList}}
+ {{.Title}} {{if .Is_Closed}}closed{{else}}open{{end}} +
{{end}} +
+{{template "footer.html" . }} \ No newline at end of file diff --git a/src/templates/forums.html b/src/templates/forums.html new file mode 100644 index 00000000..695fbfaf --- /dev/null +++ b/src/templates/forums.html @@ -0,0 +1,8 @@ +{{template "header.html" . }} +
+ {{range .ItemList}}{{end}} +
+{{template "footer.html" . }} \ No newline at end of file diff --git a/src/templates/menu.html b/src/templates/menu.html index 76d4c7dd..4c8bb58e 100644 --- a/src/templates/menu.html +++ b/src/templates/menu.html @@ -1,6 +1,7 @@