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_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(`