diff --git a/README.md b/README.md index a2c3a265..51f30cee 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A super fast forum software written in Go. -The initial code-base was forked from one of my side projects, but has now gone far beyond that. +The initial code-base was forked from one of my side projects, but has now gone far beyond that. We're still fairly early in development, so the code-base might change at an incredible rate. We plan to start stabilising it somewhat once we enter alpha. Azareal's Discord Chat: https://discord.gg/eyYvtTf @@ -106,7 +106,7 @@ We're looking for ways to clean-up the plugin system so that all of them (except # Dependencies -Go 1.7 +* Go 1.7 * MariaDB @@ -120,6 +120,6 @@ There are several plugins which are bundled with the software by default. These * Hello World / Skeleton - Example plugins for helping you learn how to develop plugins. -* BBCode - A plugin in early development for converting BBCode Tags into HTML. Don't use this in production yet. +* BBCode - A plugin in early development for converting BBCode Tags into HTML. * Markdown - An extremely simple plugin for converting Markdown into HTML. diff --git a/TODO.md b/TODO.md index 791693b0..2a863243 100644 --- a/TODO.md +++ b/TODO.md @@ -37,4 +37,4 @@ Add support for multi-factor authentication. Add support for secondary emails for users. -Improve the shell scripts and possibly add support for Make? +Improve the shell scripts and possibly add support for Make? A make.go might be a good solution? diff --git a/build-gosora-linux b/build-gosora-linux index e99c2676..29b369fd 100644 --- a/build-gosora-linux +++ b/build-gosora-linux @@ -1,2 +1,4 @@ -go build -go build ./install \ No newline at end of file +echo "Building Gosora" +go build -o Gosora +echo "Building the installer" +go build ./install diff --git a/build.bat b/build.bat index 4fe85921..27d28e3f 100644 --- a/build.bat +++ b/build.bat @@ -7,7 +7,7 @@ if %errorlevel% neq 0 ( ) echo Building the executable -go build +go build -o gosora.exe if %errorlevel% neq 0 ( pause exit /b %errorlevel% diff --git a/config.go b/config.go index a9c51960..26cf01d6 100644 --- a/config.go +++ b/config.go @@ -30,6 +30,7 @@ var enable_ssl = false var ssl_privkey = "" var ssl_fullchain = "" -// Developer flag +// Developer flags var debug = false var profiling = false + diff --git a/data.sql b/data.sql index d948784d..1306fcd5 100644 --- a/data.sql +++ b/data.sql @@ -47,6 +47,7 @@ CREATE TABLE `forums`( `fid` int not null AUTO_INCREMENT, `name` varchar(100) not null, `active` tinyint DEFAULT 1 not null, + `topicCount` int DEFAULT 0 not null, `lastTopic` varchar(100) DEFAULT '' not null, `lastTopicID` int DEFAULT 0 not null, `lastReplyer` varchar(100) DEFAULT '' not null, @@ -169,6 +170,7 @@ INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{"BanUsers":fal INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Banned','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManageThemes":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":false,"EditTopic":false,"DeleteTopic":false,"CreateReply":false,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}',1); INSERT INTO users_groups(`name`,`permissions`) VALUES ('Awaiting Activation','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManageThemes":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":false,"EditTopic":false,"DeleteTopic":false,"CreateReply":false,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}'); +INSERT INTO forums(`name`,`active`) VALUES ('Reports',0); 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/forum.go b/forum.go index 45d56670..3b712aa0 100644 --- a/forum.go +++ b/forum.go @@ -1,10 +1,13 @@ package main +import "database/sql" +import _ "github.com/go-sql-driver/mysql" type Forum struct { ID int Name string Active bool + TopicCount int LastTopic string LastTopicID int LastReplyer string @@ -18,3 +21,42 @@ type ForumSimple struct Name string Active bool } + +func create_forum(forum_name string, active bool) (int, error) { + var fid int + err := forum_entry_exists_stmt.QueryRow().Scan(&fid) + if err != nil && err != sql.ErrNoRows { + return 0, err + } + if err != sql.ErrNoRows { + _, err = update_forum_stmt.Exec(forum_name, active, fid) + if err != nil { + return fid, err + } + forums[fid].Name = forum_name + forums[fid].Active = active + return fid, nil + } + + res, err := create_forum_stmt.Exec(forum_name, active) + if err != nil { + return 0, err + } + + fid64, err := res.LastInsertId() + if err != nil { + return 0, err + } + + forums = append(forums, Forum{int(fid64),forum_name,active,0,"",0,"",0,""}) + return fid, nil +} + +func delete_forum(fid int) error { + _, err := delete_forum_stmt.Exec(fid) + if err != nil { + return err + } + forums[fid].Name = "" + return nil +} diff --git a/images/forum_manager.PNG b/images/forum_manager.PNG new file mode 100644 index 00000000..3bb160c0 Binary files /dev/null and b/images/forum_manager.PNG differ diff --git a/install-gosora-linux b/install-gosora-linux index 11b4883b..19372ad5 100644 --- a/install-gosora-linux +++ b/install-gosora-linux @@ -1,5 +1,10 @@ +echo "Installing the MySQL Driver" go get -u github.com/go-sql-driver/mysql +echo "Installing bcrypt" go get -u golang.org/x/crypto/bcrypt -go build + +echo "Preparing the installer" +go generate +go build -o Gosora go build ./install ./Install \ No newline at end of file diff --git a/install.bat b/install.bat index ccd38783..be08c151 100644 --- a/install.bat +++ b/install.bat @@ -12,6 +12,11 @@ if %errorlevel% neq 0 ( ) echo Preparing the installer +go generate +if %errorlevel% neq 0 ( + pause + exit /b %errorlevel% +) go build if %errorlevel% neq 0 ( pause diff --git a/install/install.go b/install/install.go index 32bd80e8..4ef0bba2 100644 --- a/install/install.go +++ b/install/install.go @@ -160,7 +160,7 @@ var site_email = "" // Should be a setting var smtp_server = "" //var noavatar = "https://api.adorable.io/avatars/{width}/{id}@{site_url}.png" var noavatar = "https://api.adorable.io/avatars/285/{id}@" + site_url + ".png" -var items_per_page = 40 // Should be a setting +var items_per_page = 25 var site_url = "` + site_url + `" var server_port = "` + server_port + `" @@ -170,6 +170,7 @@ var ssl_fullchain = "" // Developer flag var debug = false +var profiling = false `) fmt.Println("Opening the configuration file") diff --git a/main.go b/main.go index 721487d5..c4e567e4 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,9 @@ var staff_css_tmpl = template.CSS(staff_css) var settings map[string]interface{} = make(map[string]interface{}) var external_sites map[string]string = make(map[string]string) var groups map[int]Group = make(map[int]Group) -var forums map[int]Forum = make(map[int]Forum) +var forums []Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency +var groupCapCount int +var forumCapCount int var static_files map[string]SFile = make(map[string]SFile) var template_topic_handle func(TopicPage,io.Writer) = nil @@ -75,7 +77,8 @@ func compile_templates() { topics_page := TopicsPage{"Topic List",user,noticeList,topicList,""} topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) - forum_page := ForumPage{"General Forum",user,noticeList,topicList,"There aren't any topics in this forum yet."} + forum_item := Forum{1,"General Forum",true,0,"",0,"",0,""} + forum_page := ForumPage{"General Forum",user,noticeList,topicList,forum_item,1,1,nil} forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList) log.Print("Writing the templates") diff --git a/mod_routes.go b/mod_routes.go index 15de7eb6..f156e624 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -72,7 +72,8 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) { var content string var createdBy int - err = db.QueryRow("select tid, content, createdBy from topics where tid = ?", tid).Scan(&tid, &content, &createdBy) + var fid int + err = db.QueryRow("select tid, content, createdBy, parentID from topics where tid = ?", tid).Scan(&tid, &content, &createdBy, &fid) if err == sql.ErrNoRows { LocalError("The topic you tried to delete doesn't exist.",w,r,user) return @@ -95,6 +96,18 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r,user) return } + + if (fid > forumCapCount) || (fid < 0) || forums[fid].Name=="" { + LocalError("The topic's parent forum doesn't exist.",w,r,user) + return + } + _, err = remove_topics_from_forum_stmt.Exec(1, fid) + if err != nil { + InternalError(err,w,r,user) + return + } + + forums[fid].TopicCount -= 1 } func route_stick_topic(w http.ResponseWriter, r *http.Request) { @@ -571,12 +584,12 @@ func route_panel_forums(w http.ResponseWriter, r *http.Request){ var forumList []interface{} for _, forum := range forums { - if forum.ID > -1 { + if forum.Name != "" { forumList = append(forumList, forum) } } - pi := Page{"Forum Manager",user,noticeList,forumList,0} + pi := Page{"Forum Manager",user,noticeList,forumList,nil} templates.ExecuteTemplate(w,"panel-forums.html", pi) } @@ -600,20 +613,20 @@ func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request){ return } + var active bool fname := r.PostFormValue("forum-name") - res, err := create_forum_stmt.Exec(fname) + factive := r.PostFormValue("forum-name") + if factive == "on" || factive == "1" { + active = true + } else { + active = false + } + + _, err = create_forum(fname, active) 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) } @@ -637,8 +650,7 @@ func route_panel_forums_delete(w http.ResponseWriter, r *http.Request){ return } - _, ok = forums[fid]; - if !ok { + if (fid > forumCapCount) || (fid < 0) || forums[fid].Name=="" { LocalError("The forum you're trying to delete doesn't exist.",w,r,user) return } @@ -671,20 +683,17 @@ func route_panel_forums_delete_submit(w http.ResponseWriter, r *http.Request) { return } - _, ok = forums[fid]; - if !ok { + if (fid > forumCapCount) || (fid < 0) || forums[fid].Name=="" { LocalError("The forum you're trying to delete doesn't exist.",w,r,user) return } - _, err = delete_forum_stmt.Exec(fid) + err = delete_forum(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) } @@ -715,8 +724,7 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request) { } forum_name := r.PostFormValue("edit_item") - forum, ok := forums[fid]; - if !ok { + if (fid > forumCapCount) || (fid < 0) || forums[fid].Name=="" { LocalError("The forum you're trying to edit doesn't exist.",w,r,user) return } @@ -726,8 +734,7 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r,user) return } - forum.Name = forum_name - forums[fid] = forum + forums[fid].Name = forum_name http.Redirect(w,r,"/panel/forums/",http.StatusSeeOther) } diff --git a/mysql.go b/mysql.go index b19c56ce..dd63dd2c 100644 --- a/mysql.go +++ b/mysql.go @@ -15,11 +15,14 @@ var get_topic_user_stmt *sql.Stmt var get_topic_replies_stmt *sql.Stmt var get_topic_replies_offset_stmt *sql.Stmt var get_forum_topics_stmt *sql.Stmt +var get_forum_topics_offset_stmt *sql.Stmt var create_topic_stmt *sql.Stmt var create_report_stmt *sql.Stmt var create_reply_stmt *sql.Stmt var add_replies_to_topic_stmt *sql.Stmt var remove_replies_from_topic_stmt *sql.Stmt +var add_topics_to_forum_stmt *sql.Stmt +var remove_topics_from_forum_stmt *sql.Stmt var update_forum_cache_stmt *sql.Stmt var edit_topic_stmt *sql.Stmt var edit_reply_stmt *sql.Stmt @@ -55,6 +58,7 @@ var delete_profile_reply_stmt *sql.Stmt var create_forum_stmt *sql.Stmt var delete_forum_stmt *sql.Stmt var update_forum_stmt *sql.Stmt +var forum_entry_exists_stmt *sql.Stmt var update_setting_stmt *sql.Stmt var add_plugin_stmt *sql.Stmt var update_plugin_stmt *sql.Stmt @@ -102,13 +106,19 @@ func init_database(err error) { } log.Print("Preparing get_topic_replies_offset statement.") - get_topic_replies_offset_stmt, err = db.Prepare("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ? limit ? , " + strconv.Itoa(items_per_page)) + get_topic_replies_offset_stmt, err = db.Prepare("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users on replies.createdBy = users.uid where tid = ? limit ?, " + strconv.Itoa(items_per_page)) if err != nil { log.Fatal(err) } log.Print("Preparing get_forum_topics statement.") - get_forum_topics_stmt, err = db.Prepare("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") + get_forum_topics_stmt, err = db.Prepare("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") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing get_forum_topics_offset statement.") + get_forum_topics_offset_stmt, err = db.Prepare("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 limit ?, " + strconv.Itoa(items_per_page)) if err != nil { log.Fatal(err) } @@ -120,7 +130,7 @@ func init_database(err error) { } log.Print("Preparing create_report statement.") - create_report_stmt, err = db.Prepare("INSERT INTO topics(title,content,parsed_content,createdAt,createdBy,data,parentID) VALUES(?,?,?,NOW(),?,?,-1)") + create_report_stmt, err = db.Prepare("INSERT INTO topics(title,content,parsed_content,createdAt,createdBy,data,parentID) VALUES(?,?,?,NOW(),?,?,1)") if err != nil { log.Fatal(err) } @@ -143,6 +153,18 @@ func init_database(err error) { log.Fatal(err) } + log.Print("Preparing add_topics_to_forum statement.") + add_topics_to_forum_stmt, err = db.Prepare("UPDATE forums SET topicCount = topicCount + ? WHERE fid = ?") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing remove_topics_from_forum statement.") + remove_topics_from_forum_stmt, err = db.Prepare("UPDATE forums SET topicCount = topicCount - ? WHERE fid = ?") + if err != nil { + 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 { @@ -249,7 +271,7 @@ func init_database(err error) { } log.Print("Preparing change_group statement.") - change_group_stmt, err = db.Prepare("UPDATE `users` SET `group` = ? WHERE `uid` = ?") + change_group_stmt, err = db.Prepare("update `users` set `group` = ? where `uid` = ?") if err != nil { log.Fatal(err) } @@ -333,19 +355,26 @@ func init_database(err error) { } log.Print("Preparing create_forum statement.") - create_forum_stmt, err = db.Prepare("INSERT INTO forums(name) VALUES(?)") + create_forum_stmt, err = db.Prepare("INSERT INTO forums(name,active) VALUES(?,?)") if err != nil { log.Fatal(err) } log.Print("Preparing delete_forum statement.") - delete_forum_stmt, err = db.Prepare("DELETE FROM forums WHERE fid = ?") + //delete_forum_stmt, err = db.Prepare("DELETE FROM forums WHERE fid = ?") + delete_forum_stmt, err = db.Prepare("update forums set name= '', active = 0 where fid = ?") if err != nil { log.Fatal(err) } log.Print("Preparing update_forum statement.") - update_forum_stmt, err = db.Prepare("UPDATE forums SET name = ? WHERE fid = ?") + update_forum_stmt, err = db.Prepare("update forums set name = ?, active = ? where fid = ?") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing forum_entry_exists statement.") + forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' order by fid asc limit 1") if err != nil { log.Fatal(err) } @@ -418,19 +447,30 @@ func init_database(err error) { } log.Print("Loading the forums.") - rows, err = db.Query("SELECT fid, name, active, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime FROM forums") + log.Print("Adding the uncategorised forum") + forums = append(forums, Forum{0,"Uncategorised",uncategorised_forum_visible,0,"",0,"",0,""}) + + //rows, err = db.Query("SELECT fid, name, active, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime FROM forums") + rows, err = db.Query("SELECT fid, name, active, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime FROM forums ORDER BY fid ASC") if err != nil { log.Fatal(err) } defer rows.Close() - for rows.Next() { - forum := Forum{0,"",true,"",0,"",0,""} - err := rows.Scan(&forum.ID, &forum.Name, &forum.Active, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + i := 1 + for ;rows.Next();i++ { + forum := Forum{0,"",true,0,"",0,"",0,""} + err := rows.Scan(&forum.ID, &forum.Name, &forum.Active, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) if err != nil { log.Fatal(err) } + // Ugh, you really shouldn't physically delete these items, it makes a big mess of things + if forum.ID != i { + fmt.Println("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forums() instead x.x") + fill_forum_id_gap(i, forum.ID) + } + if forum.LastTopicID != 0 { forum.LastTopicTime, err = relative_time(forum.LastTopicTime) if err != nil { @@ -440,17 +480,18 @@ func init_database(err error) { forum.LastTopic = "None" forum.LastTopicTime = "" } - forums[forum.ID] = forum + + log.Print("Adding the " + forum.Name + " forum") + forums = append(forums,forum) } err = rows.Err() if err != nil { log.Fatal(err) } + forumCapCount = i - log.Print("Adding the uncategorised forum") - forums[0] = Forum{0,"Uncategorised",uncategorised_forum_visible,"",0,"",0,""} - log.Print("Adding the reports forum") - forums[-1] = Forum{-1,"Reports",false,"",0,"",0,""} + //log.Print("Adding the reports forum") + //forums[-1] = Forum{-1,"Reports",false,0,"",0,"",0,""} log.Print("Loading the settings.") rows, err = db.Query("SELECT name, content, type, constraints FROM settings") @@ -541,6 +582,4 @@ func init_database(err error) { if err != nil { log.Fatal(err) } - - } diff --git a/pages.go b/pages.go index 1b452ba3..4ced1fec 100644 --- a/pages.go +++ b/pages.go @@ -38,6 +38,9 @@ type ForumPage struct CurrentUser User NoticeList []string ItemList []TopicUser + Forum Forum + Page int + LastPage int ExtData interface{} } diff --git a/plugin_bbcode.go b/plugin_bbcode.go index 0b555200..4ab800a3 100644 --- a/plugin_bbcode.go +++ b/plugin_bbcode.go @@ -1,6 +1,6 @@ package main //import "log" -import "fmt" +//import "fmt" import "bytes" //import "strings" import "strconv" @@ -15,6 +15,8 @@ var bbcode_missing_tag []byte var bbcode_url_open []byte var bbcode_url_open2 []byte var bbcode_url_close []byte +var bbcode_space_gap []byte + var bbcode_bold *regexp.Regexp var bbcode_italic *regexp.Regexp var bbcode_underline *regexp.Regexp @@ -36,6 +38,7 @@ func init_bbcode() { bbcode_url_open = []byte("") bbcode_url_close = []byte("") + bbcode_space_gap = []byte(" ") bbcode_bold = regexp.MustCompile(`(?s)\[b\](.*)\[/b\]`) bbcode_italic = regexp.MustCompile(`(?s)\[i\](.*)\[/i\]`) @@ -188,8 +191,8 @@ func bbcode_full_parse(data interface{}) interface{} { has_s := false has_c := false complex_bbc := false - msglen := len(msgbytes) - for i := 0; (i + 3) < msglen; i++ { + msgbytes = append(msgbytes,bbcode_space_gap...) + for i := 0; i < len(msgbytes); i++ { if msgbytes[i] == '[' { if msgbytes[i + 2] != ']' { if msgbytes[i + 1] == '/' { @@ -215,7 +218,7 @@ func bbcode_full_parse(data interface{}) interface{} { i += 3 } } else { - if msglen >= (i+6) && msgbytes[i+2] == 'c' && msgbytes[i+3] == 'o' && msgbytes[i+4] == 'd' && msgbytes[i+5] == 'e' && msgbytes[i+6] == ']' { + if msgbytes[i+2] == 'c' && msgbytes[i+3] == 'o' && msgbytes[i+4] == 'd' && msgbytes[i+5] == 'e' && msgbytes[i+6] == ']' { has_c = false i += 7 } @@ -228,7 +231,7 @@ func bbcode_full_parse(data interface{}) interface{} { complex_bbc = true } } else { - if msglen >= (i+5) && msgbytes[i+1] == 'c' && msgbytes[i+2] == 'o' && msgbytes[i+3] == 'd' && msgbytes[i+4] == 'e' && msgbytes[i+5] == ']' { + if msgbytes[i+1] == 'c' && msgbytes[i+2] == 'o' && msgbytes[i+3] == 'd' && msgbytes[i+4] == 'e' && msgbytes[i+5] == ']' { has_c = true i += 6 } @@ -271,14 +274,14 @@ func bbcode_full_parse(data interface{}) interface{} { i := 0 var start int var lastTag int - outbytes := make([]byte, msglen) - fmt.Println(string(msgbytes)) - for ; (i+3) < msglen; i++ { + outbytes := make([]byte, len(msgbytes)) + //fmt.Println(string(msgbytes)) + for ; i < len(msgbytes); i++ { MainLoop: if msgbytes[i] == '[' { OuterComplex: if msgbytes[i + 1] == 'u' { - if (msglen-1) >= (i+6) && msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' { + if msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' { outbytes = append(outbytes, msgbytes[lastTag:i]...) start = i + 5 i = start @@ -300,7 +303,7 @@ func bbcode_full_parse(data interface{}) interface{} { } for ;; i++ { - if msglen < (i + 6) { + if len(msgbytes) < (i + 10) { //fmt.Println(msglen) //fmt.Println(i+6) outbytes = append(outbytes, bbcode_missing_tag...) @@ -329,7 +332,7 @@ func bbcode_full_parse(data interface{}) interface{} { lastTag = i } } else if msgbytes[i + 1] == 'r' { - if msglen >= (i+6) && bytes.Equal(msgbytes[i+2:i+6],[]byte("and]")) { + if bytes.Equal(msgbytes[i+2:i+6],[]byte("and]")) { outbytes = append(outbytes, msgbytes[lastTag:i]...) start = i + 6 i = start @@ -340,7 +343,7 @@ func bbcode_full_parse(data interface{}) interface{} { goto OuterComplex } break - } else if (len(msgbytes) - 1) < (i + 7) { + } else if (len(msgbytes) - 1) < (i + 10) { outbytes = append(outbytes, bbcode_missing_tag...) goto OuterComplex } @@ -364,7 +367,7 @@ func bbcode_full_parse(data interface{}) interface{} { //fmt.Println(outbytes) //fmt.Println(string(outbytes)) if lastTag != i { - outbytes = append(outbytes, msgbytes[lastTag:]...) + outbytes = append(outbytes, msgbytes[lastTag:len(msgbytes) - 10]...) } if len(outbytes) != 0 { return string(outbytes) diff --git a/public/global.js b/public/global.js index 923087c9..49e904d5 100644 --- a/public/global.js +++ b/public/global.js @@ -121,4 +121,13 @@ $(document).ready(function(){ }); } }); + + $(this).keyup(function(event){ + if(event.which == 37) { + $("#prevFloat a")[0].click(); + } + if(event.which == 39) { + $("#nextFloat a")[0].click(); + } + }); }); \ No newline at end of file diff --git a/routes.go b/routes.go index 9aa57d43..56782e2d 100644 --- a/routes.go +++ b/routes.go @@ -2,7 +2,7 @@ package main import "log" -import "fmt" +//import "fmt" import "strconv" import "bytes" import "regexp" @@ -150,15 +150,14 @@ func route_forum(w http.ResponseWriter, r *http.Request){ return } - var topicList []TopicUser + page, _ := strconv.Atoi(r.FormValue("page")) 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 } - _, ok = forums[fid] - if !ok { + if (fid > forumCapCount) || (fid < 0) || forums[fid].Name=="" { NotFound(w,r,user) return } @@ -167,12 +166,24 @@ func route_forum(w http.ResponseWriter, r *http.Request){ return } - rows, err := get_forum_topics_stmt.Query(fid) + // Calculate the offset + var offset int + last_page := int(forums[fid].TopicCount / items_per_page) + 1 + if page > 1 { + offset = (items_per_page * page) - items_per_page + } else if page == -1 { + page = last_page + offset = (items_per_page * page) - items_per_page + } else { + page = 1 + } + rows, err := get_forum_topics_offset_stmt.Query(fid, offset) if err != nil { InternalError(err,w,r,user) return } + var topicList []TopicUser topicItem := TopicUser{ID: 0} for rows.Next() { err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.ParentID, &topicItem.CreatedByName, &topicItem.Avatar) @@ -201,7 +212,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){ } rows.Close() - pi := ForumPage{forums[fid].Name,user,noticeList,topicList,nil} + pi := ForumPage{forums[fid].Name,user,noticeList,topicList,forums[fid],page,last_page,nil} if template_forum_handle != nil { template_forum_handle(pi,w) } else { @@ -541,6 +552,8 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) { LocalError("Bad Form", w, r, user) return } + + fid := 2 topic_name := html.EscapeString(r.PostFormValue("topic-name")) content := html.EscapeString(preparse_message(r.PostFormValue("topic-content"))) ipaddress, _, err := net.SplitHostPort(r.RemoteAddr) @@ -549,6 +562,11 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) { return } + if (fid > forumCapCount) || (fid < 0) || forums[fid].Name=="" { + LocalError("The topic's parent forum doesn't exist.",w,r,user) + return + } + res, err := create_topic_stmt.Exec(topic_name,content,parse_message(content),ipaddress,user.ID) if err != nil { InternalError(err,w,r,user) @@ -560,7 +578,15 @@ func route_create_topic(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r,user) return } - _, err = update_forum_cache_stmt.Exec(topic_name, lastId, user.Name, user.ID, 1) + + _, err = add_topics_to_forum_stmt.Exec(1, fid) + if err != nil { + InternalError(err,w,r,user) + return + } + forums[fid].TopicCount -= 1 + + _, err = update_forum_cache_stmt.Exec(topic_name, lastId, user.Name, user.ID, fid) if err != nil { InternalError(err,w,r,user) return @@ -710,8 +736,8 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { } item_type := r.FormValue("type") - success := 1 + fid := 1 var tid int var title string var content string @@ -719,16 +745,16 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { if item_type == "reply" { err = db.QueryRow("select tid, content from replies where rid = ?", item_id).Scan(&tid, &content) if err == sql.ErrNoRows { - LocalError("We were unable to find the reported post", w, r, user) + LocalError("We were unable to find the reported post",w,r,user) return } else if err != nil { InternalError(err,w,r,user) return } - err = db.QueryRow("select title, data from topics where tid = ?", tid).Scan(&title,&data) + err = db.QueryRow("select title, data from topics where tid = ?",tid).Scan(&title,&data) if err == sql.ErrNoRows { - LocalError("We were unable to find the topic which the reported post is supposed to be in", w, r, user) + LocalError("We were unable to find the topic which the reported post is supposed to be in",w,r,user) return } else if err != nil { InternalError(err,w,r,user) @@ -738,7 +764,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { } else if item_type == "user-reply" { err = db.QueryRow("select uid, content from users_replies where rid = ?", item_id).Scan(&tid, &content) if err == sql.ErrNoRows { - LocalError("We were unable to find the reported post", w, r, user) + LocalError("We were unable to find the reported post",w,r,user) return } else if err != nil { InternalError(err,w,r,user) @@ -747,7 +773,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { err = db.QueryRow("select name from users where uid = ?", tid).Scan(&title) if err == sql.ErrNoRows { - LocalError("We were unable to find the profile which the reported post is supposed to be on", w, r, user) + LocalError("We were unable to find the profile which the reported post is supposed to be on",w,r,user) return } else if err != nil { InternalError(err,w,r,user) @@ -769,14 +795,13 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { run_vhook_noreturn("report_preassign", &item_id, &item_type) return } - // Don't try to guess the type - LocalError("Unknown type", w, r, user) + LocalError("Unknown type",w,r,user) return } var count int - rows, err := db.Query("select count(*) as count from topics where data = ? and data != '' and parentID = -1", item_type + "_" + strconv.Itoa(item_id)) + rows, err := db.Query("select count(*) as count from topics where data = ? and data != '' and parentID = 1", item_type + "_" + strconv.Itoa(item_id)) if err != nil && err != sql.ErrNoRows { InternalError(err,w,r,user) return @@ -797,34 +822,28 @@ func route_report_submit(w http.ResponseWriter, r *http.Request) { title = "Report: " + title res, err := create_report_stmt.Exec(title,content,content,user.ID,item_type + "_" + strconv.Itoa(item_id)) if err != nil { - log.Print(err) - success = 0 + InternalError(err,w,r,user) + return } lastId, err := res.LastInsertId() - if err != nil { - log.Print(err) - success = 0 - } - - _, err = update_forum_cache_stmt.Exec(title, lastId, user.Name, user.ID, 1) if err != nil { InternalError(err,w,r,user) return } - if success != 1 { - errmsg := "Unable to create the report" - pi := Page{"Error",user,nList,tList,errmsg} - - var b bytes.Buffer - templates.ExecuteTemplate(&b,"error.html", pi) - errpage := b.String() - w.WriteHeader(500) - fmt.Fprintln(w,errpage) - } else { - http.Redirect(w, r, "/topic/" + strconv.FormatInt(lastId, 10), http.StatusSeeOther) + _, err = add_topics_to_forum_stmt.Exec(1, fid) + if err != nil { + InternalError(err,w,r,user) + return } + _, err = update_forum_cache_stmt.Exec(title, lastId, user.Name, user.ID, fid) + if err != nil { + InternalError(err,w,r,user) + return + } + + http.Redirect(w, r, "/topic/" + strconv.FormatInt(lastId, 10), http.StatusSeeOther) } func route_account_own_edit_critical(w http.ResponseWriter, r *http.Request) { diff --git a/run-gosora-linux b/run-gosora-linux index 89bde232..aa81c8ba 100644 --- a/run-gosora-linux +++ b/run-gosora-linux @@ -1,3 +1,6 @@ -go build -go build ./install +echo "Generating the dynamic code" +go generate +echo "Building Gosora" +go build -o Gosora +echo "Running Gosora" ./Gosora \ No newline at end of file diff --git a/run.bat b/run.bat index 90be3557..17fb6881 100644 --- a/run.bat +++ b/run.bat @@ -7,7 +7,7 @@ if %errorlevel% neq 0 ( ) echo Building the executable -go build +go build -o gosora.exe if %errorlevel% neq 0 ( pause exit /b %errorlevel% diff --git a/template_forum.go b/template_forum.go index 089a934f..be038155 100644 --- a/template_forum.go +++ b/template_forum.go @@ -40,37 +40,55 @@ w.Write([]byte(item)) w.Write(header_5) } } +if tmpl_forum_vars.Page > 1 { w.Write(forum_0) -w.Write([]byte(tmpl_forum_vars.Title)) +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID))) w.Write(forum_1) +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page - 1))) +w.Write(forum_2) +} +if tmpl_forum_vars.LastPage != tmpl_forum_vars.Page { +w.Write(forum_3) +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID))) +w.Write(forum_4) +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1))) +w.Write(forum_5) +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID))) +w.Write(forum_6) +w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Page + 1))) +w.Write(forum_7) +} +w.Write(forum_8) +w.Write([]byte(tmpl_forum_vars.Title)) +w.Write(forum_9) if len(tmpl_forum_vars.ItemList) != 0 { for _, item := range tmpl_forum_vars.ItemList { -w.Write(forum_2) -if item.Avatar != "" { -w.Write(forum_3) -w.Write([]byte(item.Avatar)) -w.Write(forum_4) -} -if item.Sticky { -w.Write(forum_5) -} else { -if item.Is_Closed { -w.Write(forum_6) -} -} -w.Write(forum_7) -w.Write([]byte(strconv.Itoa(item.ID))) -w.Write(forum_8) -w.Write([]byte(item.Title)) -w.Write(forum_9) -if item.Is_Closed { w.Write(forum_10) -} +if item.Avatar != "" { w.Write(forum_11) -} -} else { +w.Write([]byte(item.Avatar)) w.Write(forum_12) } +if item.Sticky { w.Write(forum_13) +} else { +if item.Is_Closed { +w.Write(forum_14) +} +} +w.Write(forum_15) +w.Write([]byte(strconv.Itoa(item.ID))) +w.Write(forum_16) +w.Write([]byte(item.Title)) +w.Write(forum_17) +if item.Is_Closed { +w.Write(forum_18) +} +w.Write(forum_19) +} +} else { +w.Write(forum_20) +} +w.Write(forum_21) w.Write(footer_0) } diff --git a/template_list.go b/template_list.go index c94ee179..1855f388 100644 --- a/template_list.go +++ b/template_list.go @@ -50,106 +50,109 @@ var header_3 []byte = []byte(`
`) var header_4 []byte = []byte(`
`) var header_5 []byte = []byte(`
`) -var topic_0 []byte = []byte(``) -var topic_3 []byte = []byte(``) -var topic_6 []byte = []byte(` +var topic_5 []byte = []byte(`" /> +`) +var topic_8 []byte = []byte(`
+var topic_9 []byte = []byte(`' method="post">
+var topic_10 []byte = []byte(` style="background-color: #FFFFEA;"`) +var topic_11 []byte = []byte(` style="background-color: #eaeaea;"`) +var topic_12 []byte = []byte(`> `) -var topic_11 []byte = []byte(` +var topic_13 []byte = []byte(` `) -var topic_12 []byte = []byte(`🔒︎`) -var topic_13 []byte = []byte(` +var topic_14 []byte = []byte(`🔒︎`) +var topic_15 []byte = []byte(` Edit +var topic_16 []byte = []byte(`' class="username hide_on_edit open_edit" style="font-weight: normal;margin-left: 6px;">Edit Delete +var topic_17 []byte = []byte(`' class="username" style="font-weight: normal;">Delete `) -var topic_16 []byte = []byte(`Unpin`) -var topic_18 []byte = []byte(`Pin`) -var topic_20 []byte = []byte(` +var topic_18 []byte = []byte(`Unpin`) +var topic_20 []byte = []byte(`Pin`) +var topic_22 []byte = []byte(` +var topic_23 []byte = []byte(`' type="text" /> `) -var topic_22 []byte = []byte(` +var topic_24 []byte = []byte(` Report +var topic_25 []byte = []byte(`?session=`) +var topic_26 []byte = []byte(`&type=topic" class="username report_item" style="font-weight: normal;">Report
+var topic_27 []byte = []byte(`background-image: url(`) +var topic_28 []byte = []byte(`), url(/static/white-dot.jpg);background-position: 0px `) +var topic_29 []byte = []byte(`-1`) +var topic_30 []byte = []byte(`0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;`) +var topic_31 []byte = []byte(`">

`) -var topic_30 []byte = []byte(`

+var topic_32 []byte = []byte(`



+var topic_33 []byte = []byte(`

`) -var topic_33 []byte = []byte(` +var topic_34 []byte = []byte(`" class="username real_username">`) +var topic_35 []byte = []byte(` `) -var topic_35 []byte = []byte(`style="color: #505050;float: right;">Level `) -var topic_36 []byte = []byte(` +var topic_36 []byte = []byte(`style="float: right;">`) +var topic_37 []byte = []byte(`style="color: #505050;float: right;">Level `) +var topic_38 []byte = []byte(`

`) -var topic_37 []byte = []byte(` +var topic_39 []byte = []byte(`
+var topic_40 []byte = []byte(`background-image: url(`) +var topic_41 []byte = []byte(`), url(/static/white-dot.jpg);background-position: 0px `) +var topic_42 []byte = []byte(`-1`) +var topic_43 []byte = []byte(`0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;`) +var topic_44 []byte = []byte(`">

`) -var topic_43 []byte = []byte(`



+var topic_45 []byte = []byte(`



`) -var topic_45 []byte = []byte(` +var topic_46 []byte = []byte(`" class="username real_username">`) +var topic_47 []byte = []byte(` `) -var topic_46 []byte = []byte(` `) -var topic_48 []byte = []byte(` `) -var topic_50 []byte = []byte(` +var topic_48 []byte = []byte(` `) +var topic_50 []byte = []byte(` `) +var topic_52 []byte = []byte(` +var topic_53 []byte = []byte(`?session=`) +var topic_54 []byte = []byte(`&type=reply" class="mod_button"> `) -var topic_54 []byte = []byte(`style="color: #505050;float: right;">Level `) -var topic_55 []byte = []byte(` +var topic_55 []byte = []byte(`style="float: right;">`) +var topic_56 []byte = []byte(`style="color: #505050;float: right;">Level `) +var topic_57 []byte = []byte(`
`) -var topic_56 []byte = []byte(`
+var topic_58 []byte = []byte(`
`) -var topic_57 []byte = []byte(` +var topic_59 []byte = []byte(`
+var topic_60 []byte = []byte(`' type="hidden" />
@@ -163,48 +166,51 @@ var footer_0 []byte = []byte(`