diff --git a/.gitignore b/.gitignore index 1fb213a3..2b1307dd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ gosora.exe gosora.test.exe install.exe *.prof - +.DS_Store diff --git a/README.md b/README.md index ec4894fe..a2c3a265 100644 --- a/README.md +++ b/README.md @@ -104,42 +104,22 @@ We're looking for ways to clean-up the plugin system so that all of them (except ![Cosmo Theme](https://github.com/Azareal/Gosora/blob/master/images/cosmo.png) +# Dependencies -# TO-DO +Go 1.7 -Oh my, you caught me right at the start of this project. There's nothing to see here yet, asides from the absolute basics. You might want to look again later! +* MariaDB +* github.com/go-sql-driver/mysql -The various little features which somehow got stuck in the net. Don't worry, I'll get to them! +* golang.org/x/crypto/bcrypt -More moderation features. E.g. Move, Approval Queue (Posts made by users in certain usergroups will need to be approved by a moderator before they're publically visible), etc. +# Bundled Plugins -Add a simple anti-spam measure. I have quite a few ideas in mind, but it'll take a while to implement the more advanced ones, so I'd like to put off some of those to a later date and focus on the basics. E.g. CAPTCHAs, hidden fields, etc. +There are several plugins which are bundled with the software by default. These cover various common tasks which aren't common enough to clutter the core with or which have competing implementation methods (E.g. plugin_markdown vs plugin_bbcode for post mark-up). -Add a modern alert system. +* Hello World / Skeleton - Example plugins for helping you learn how to develop plugins. -Add per-forum permissions to finish up the foundations of the permissions system. +* BBCode - A plugin in early development for converting BBCode Tags into HTML. Don't use this in production yet. -Add a *better* plugin system. E.g. Allow for plugins written in Javascript and ones written in Go. Also, we need to add many, many, many more plugin hooks. - -I will need to ponder over implementing an even faster router. We don't need one immediately, although it would be nice if we could get one in the near future. It really depends. Ideally, it would be one which can easily integrate with the current structure without much work, although I'm not beyond making some alterations to faciliate it, assuming that we don't get too tightly bound to that specific router. - -Allow themes to define their own templates and to override core templates with their own. - -Add a friend system. - -Improve profile customisability. - -Implement all the common BBCode tags in plugin_bbcode - -Implement all the common Markdown codes in plugin_markdown - -Add more administration features. - -Add more features for improving user engagement. E.g. A like system. I have a few of these in mind, but I've been pre-occupied with implementing other features. - -Add a widget system. - -Add support for multi-factor authentication. - -Add support for secondary emails for users. +* Markdown - An extremely simple plugin for converting Markdown into HTML. diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..791693b0 --- /dev/null +++ b/TODO.md @@ -0,0 +1,40 @@ +# TO-DO + +Oh my, you caught me right at the start of this project. There's nothing to see here yet, asides from the absolute basics. You might want to look again later! + + +The various little features which somehow got stuck in the net. Don't worry, I'll get to them! + +More moderation features. E.g. Move, Approval Queue (Posts made by users in certain usergroups will need to be approved by a moderator before they're publically visible), etc. + +Add a simple anti-spam measure. I have quite a few ideas in mind, but it'll take a while to implement the more advanced ones, so I'd like to put off some of those to a later date and focus on the basics. E.g. CAPTCHAs, hidden fields, etc. + +Add a modern alert system. + +Add per-forum permissions to finish up the foundations of the permissions system. + +Add a *better* plugin system. E.g. Allow for plugins written in Javascript and ones written in Go. Also, we need to add many, many, many more plugin hooks. + +I will need to ponder over implementing an even faster router. We don't need one immediately, although it would be nice if we could get one in the near future. It really depends. Ideally, it would be one which can easily integrate with the current structure without much work, although I'm not beyond making some alterations to faciliate it, assuming that we don't get too tightly bound to that specific router. + +Allow themes to define their own templates and to override core templates with their own. + +Add a friend system. + +Improve profile customisability. + +Implement all the common BBCode tags in plugin_bbcode + +Implement all the common Markdown codes in plugin_markdown + +Add more administration features. + +Add more features for improving user engagement. E.g. A like system. I have a few of these in mind, but I've been pre-occupied with implementing other features. + +Add a widget system. + +Add support for multi-factor authentication. + +Add support for secondary emails for users. + +Improve the shell scripts and possibly add support for Make? diff --git a/config.go b/config.go index b60f9451..a9c51960 100644 --- a/config.go +++ b/config.go @@ -22,7 +22,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 = "localhost:8080" var server_port = "8080" diff --git a/data.sql b/data.sql index dd8a258b..d948784d 100644 --- a/data.sql +++ b/data.sql @@ -67,6 +67,7 @@ CREATE TABLE `topics`( `sticky` tinyint DEFAULT 0 not null, `parentID` int DEFAULT 1 not null, `ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null, + `postCount` int DEFAULT 1 not null, `data` varchar(200) DEFAULT '' not null, primary key(`tid`) ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; diff --git a/general_test.go b/general_test.go index 672d5a75..bd85ba06 100644 --- a/general_test.go +++ b/general_test.go @@ -54,8 +54,8 @@ func BenchmarkTopicTemplate(b *testing.B) { replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) - tpage := TopicPage{"Topic Blah",user,noticeList,replyList,topic,false} - tpage2 := TopicPage{"Topic Blah",admin,noticeList,replyList,topic,false} + tpage := TopicPage{"Topic Blah",user,noticeList,replyList,topic,1,1,false} + tpage2 := TopicPage{"Topic Blah",admin,noticeList,replyList,topic,1,1,false} w := ioutil.Discard b.Run("compiled_useradmin", func(b *testing.B) { diff --git a/images/pagination.PNG b/images/pagination.PNG new file mode 100644 index 00000000..fbb0f93f Binary files /dev/null and b/images/pagination.PNG differ diff --git a/images/pagination_test.png b/images/pagination_test.png new file mode 100644 index 00000000..88241033 Binary files /dev/null and b/images/pagination_test.png differ diff --git a/main.go b/main.go index 625ed635..721487d5 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ const saltLength int = 32 const sessionLength int = 80 var nogrouplog bool = false // This is mainly for benchmarks, as we don't want a lot of information getting in the way of the results -var templates = template.Must(template.ParseGlob("templates/*")) +var templates = template.New("") var no_css_tmpl = template.CSS("") var staff_css_tmpl = template.CSS(staff_css) var settings map[string]interface{} = make(map[string]interface{}) @@ -47,12 +47,12 @@ func compile_templates() { log.Print("Compiling the templates") - topic := TopicUser{1,"Blah",template.HTML("Hey there!"),0,false,false,"",0,"","","",no_css_tmpl,0,"","","","",58,"127.0.0.1"} + topic := TopicUser{1,"Blah",template.HTML("Hey there!"),0,false,false,"",0,"","127.0.0.1",0,"","",no_css_tmpl,0,"","","","",58} var replyList []Reply replyList = append(replyList, Reply{0,0,"",template.HTML("Yo!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1"}) var varList map[string]VarItem = make(map[string]VarItem) - tpage := TopicPage{"Title",user,noticeList,replyList,topic,false} + tpage := TopicPage{"Title",user,noticeList,replyList,topic,1,1,false} topic_id_tmpl := c.compile_template("topic.html","templates/","TopicPage", tpage, varList) topic_id_alt_tmpl := c.compile_template("topic_alt.html","templates/","TopicPage", tpage, varList) @@ -71,7 +71,7 @@ func compile_templates() { forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage", forums_page, varList) var topicList []TopicUser - topicList = append(topicList, TopicUser{1,"Topic Title","The topic content.",1,false,false,"",1,"open","Admin","","",0,"","","","",58,"127.0.0.1"}) + topicList = append(topicList, TopicUser{1,"Topic Title","The topic content.",1,false,false,"",1,"","127.0.0.1",0,"Admin","","",0,"","","","",58}) topics_page := TopicsPage{"Topic List",user,noticeList,topicList,""} topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) @@ -92,6 +92,30 @@ func write_template(name string, content string) { write_file("./template_" + name + ".go", content) } +func init_templates() { + compile_templates() + + // Filler functions for now... + fmap := make(map[string]interface{}) + fmap["add"] = func(in interface{}, in2 interface{})interface{} { + return 1 + } + fmap["subtract"] = func(in interface{}, in2 interface{})interface{} { + return 1 + } + fmap["multiply"] = func(in interface{}, in2 interface{})interface{} { + return 1 + } + fmap["divide"] = func(in interface{}, in2 interface{})interface{} { + return 1 + } + + // The interpreted templates... + templates.Funcs(fmap) + template.Must(templates.ParseGlob("templates/*")) + template.Must(templates.ParseGlob("pages/*")) +} + func main(){ //if profiling { // f, err := os.Create("startup_cpu.prof") @@ -104,7 +128,7 @@ func main(){ init_themes() var err error init_database(err) - compile_templates() + init_templates() db.SetMaxOpenConns(64) err = init_errors() @@ -136,7 +160,7 @@ func main(){ external_sites["YT"] = "https://www.youtube.com/" hooks["trow_assign"] = nil hooks["rrow_assign"] = nil - templates.ParseGlob("pages/*") + init_plugins() router := NewRouter() diff --git a/mod_routes.go b/mod_routes.go index d995500f..15de7eb6 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -250,6 +250,11 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request) { InternalError(err,w,r,user) return } + _, err = remove_replies_from_topic_stmt.Exec(1, tid) + if err != nil { + InternalError(err,w,r,user) + return + } } func route_profile_reply_edit_submit(w http.ResponseWriter, r *http.Request) { @@ -1265,7 +1270,7 @@ func route_panel_themes(w http.ResponseWriter, r *http.Request){ themeList = append(themeList, theme) } - pi := Page{"Theme Manager",user,noticeList,themeList,0} + pi := Page{"Theme Manager",user,noticeList,themeList,nil} err := templates.ExecuteTemplate(w,"panel-themes.html", pi) if err != nil { log.Print(err) @@ -1292,6 +1297,10 @@ func route_panel_themes_default(w http.ResponseWriter, r *http.Request){ LocalError("The theme isn't registered in the system",w,r,user) return } + if theme.Disabled { + LocalError("You must not enable this theme",w,r,user) + return + } var isDefault bool err := db.QueryRow("SELECT `default` from `themes` where `uname` = ?", uname).Scan(&isDefault) diff --git a/mysql.go b/mysql.go index dcd63fbe..b19c56ce 100644 --- a/mysql.go +++ b/mysql.go @@ -5,6 +5,7 @@ import "database/sql" import _ "github.com/go-sql-driver/mysql" import "log" import "fmt" +import "strconv" import "encoding/json" var db *sql.DB @@ -12,10 +13,13 @@ var get_session_stmt *sql.Stmt var get_topic_list_stmt *sql.Stmt 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 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 update_forum_cache_stmt *sql.Stmt var edit_topic_stmt *sql.Stmt var edit_reply_stmt *sql.Stmt @@ -86,13 +90,19 @@ func init_database(err error) { } log.Print("Preparing get_topic_user statement.") - get_topic_user_stmt, err = db.Prepare("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, users.name, users.avatar, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, topics.ipaddress from topics left join users ON topics.createdBy = users.uid where tid = ?") + get_topic_user_stmt, err = db.Prepare("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?") if err != nil { log.Fatal(err) } log.Print("Preparing get_topic_replies statement.") - get_topic_replies_stmt, err = db.Prepare("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?") + get_topic_replies_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 = ?") + if err != nil { + log.Fatal(err) + } + + 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)) if err != nil { log.Fatal(err) } @@ -121,6 +131,18 @@ func init_database(err error) { log.Fatal(err) } + log.Print("Preparing add_replies_to_topic statement.") + add_replies_to_topic_stmt, err = db.Prepare("UPDATE topics SET postCount = postCount + ? WHERE tid = ?") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing remove_replies_from_topic statement.") + remove_replies_from_topic_stmt, err = db.Prepare("UPDATE topics SET postCount = postCount - ? WHERE tid = ?") + 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 { diff --git a/pages.go b/pages.go index 8bda78c0..1b452ba3 100644 --- a/pages.go +++ b/pages.go @@ -18,6 +18,8 @@ type TopicPage struct NoticeList []string ItemList []Reply Topic TopicUser + Page int + LastPage int ExtData interface{} } diff --git a/plugin_bbcode.go b/plugin_bbcode.go index 0a1ab4a2..0b555200 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" @@ -112,7 +112,7 @@ func bbcode_parse_without_code(data interface{}) interface{} { has_i := false has_s := false complex_bbc := false - for i := 0; (i + 2) < len(msgbytes); i++ { + for i := 0; (i + 3) < len(msgbytes); i++ { if msgbytes[i] == '[' { if msgbytes[i + 2] != ']' { if msgbytes[i + 1] == '/' { @@ -170,6 +170,7 @@ func bbcode_parse_without_code(data interface{}) interface{} { msgbytes = append(msgbytes, closer...) } + // Copy the new complex parser over once the rough edges have been smoothed over if complex_bbc { msg = bbcode_url.ReplaceAllString(msg,"$1$2//$3") msg = bbcode_url_label.ReplaceAllString(msg,"$4") @@ -187,7 +188,8 @@ func bbcode_full_parse(data interface{}) interface{} { has_s := false has_c := false complex_bbc := false - for i := 0; (i + 2) < len(msgbytes); i++ { + msglen := len(msgbytes) + for i := 0; (i + 3) < msglen; i++ { if msgbytes[i] == '[' { if msgbytes[i + 2] != ']' { if msgbytes[i + 1] == '/' { @@ -213,17 +215,27 @@ func bbcode_full_parse(data interface{}) interface{} { i += 3 } } else { - if msgbytes[i + 2] == 'c' && msgbytes[i + 3] == 'o' && msgbytes[i + 4] == 'd' && msgbytes[i + 5] == 'e' { + if msglen >= (i+6) && msgbytes[i+2] == 'c' && msgbytes[i+3] == 'o' && msgbytes[i+4] == 'd' && msgbytes[i+5] == 'e' && msgbytes[i+6] == ']' { has_c = false - i += 6 + i += 7 } + //if msglen >= (i+6) { + // fmt.Println("boo") + // fmt.Println(msglen) + // fmt.Println(i+6) + // fmt.Println(string(msgbytes[i:i+6])) + //} complex_bbc = true } } else { - if msgbytes[i + 1] == 'c' && msgbytes[i + 2] == 'o' && msgbytes[i + 3] == 'd' && msgbytes[i + 4] == 'e' { + if msglen >= (i+5) && msgbytes[i+1] == 'c' && msgbytes[i+2] == 'o' && msgbytes[i+3] == 'd' && msgbytes[i+4] == 'e' && msgbytes[i+5] == ']' { has_c = true - i += 5 + i += 6 } + //if msglen >= (i+5) { + // fmt.Println("boo2") + // fmt.Println(string(msgbytes[i:i+5])) + //} complex_bbc = true } } else if !has_c { @@ -256,16 +268,17 @@ func bbcode_full_parse(data interface{}) interface{} { } if complex_bbc { + i := 0 var start int var lastTag int - outbytes := make([]byte, len(msgbytes)) - //fmt.Println(string(msgbytes)) - for i := 0; i < len(msgbytes); i++ { + outbytes := make([]byte, msglen) + fmt.Println(string(msgbytes)) + for ; (i+3) < msglen; i++ { MainLoop: if msgbytes[i] == '[' { OuterComplex: if msgbytes[i + 1] == 'u' { - if msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' { + if (msglen-1) >= (i+6) && msgbytes[i+2] == 'r' && msgbytes[i+3] == 'l' && msgbytes[i+4] == ']' { outbytes = append(outbytes, msgbytes[lastTag:i]...) start = i + 5 i = start @@ -287,7 +300,12 @@ func bbcode_full_parse(data interface{}) interface{} { } for ;; i++ { - if msgbytes[i] == '[' { + if msglen < (i + 6) { + //fmt.Println(msglen) + //fmt.Println(i+6) + outbytes = append(outbytes, bbcode_missing_tag...) + goto OuterComplex + } else if msgbytes[i] == '[' { if !bytes.Equal(msgbytes[i+1:i+6],[]byte("/url]")) { //log.Print("Not the URL closing tag!") //fmt.Println(msgbytes[i + 1:i + 6]) @@ -300,9 +318,6 @@ func bbcode_full_parse(data interface{}) interface{} { //log.Print("Weird character") //fmt.Println(msgbytes[i]) goto MainLoop - } else if (len(msgbytes) - 1) < (i + 6) { - outbytes = append(outbytes, bbcode_missing_tag...) - goto OuterComplex } } outbytes = append(outbytes, bbcode_url_open...) @@ -314,7 +329,7 @@ func bbcode_full_parse(data interface{}) interface{} { lastTag = i } } else if msgbytes[i + 1] == 'r' { - if bytes.Equal(msgbytes[i+2:i+6],[]byte("and]")) { + if msglen >= (i+6) && bytes.Equal(msgbytes[i+2:i+6],[]byte("and]")) { outbytes = append(outbytes, msgbytes[lastTag:i]...) start = i + 6 i = start @@ -340,7 +355,7 @@ func bbcode_full_parse(data interface{}) interface{} { dat := []byte(strconv.FormatInt((random.Int63n(number)),10)) outbytes = append(outbytes, dat...) //log.Print("Outputted the random number") - i += 6 + i += 7 lastTag = i } } @@ -348,6 +363,9 @@ func bbcode_full_parse(data interface{}) interface{} { } //fmt.Println(outbytes) //fmt.Println(string(outbytes)) + if lastTag != i { + outbytes = append(outbytes, msgbytes[lastTag:]...) + } if len(outbytes) != 0 { return string(outbytes) } diff --git a/routes.go b/routes.go index 13611e21..9aa57d43 100644 --- a/routes.go +++ b/routes.go @@ -244,11 +244,13 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ var( err error content string - is_super_admin bool group int + page int + offset int replyList []Reply ) + page, _ = strconv.Atoi(r.FormValue("page")) topic := TopicUser{Css: no_css_tmpl} topic.ID, err = strconv.Atoi(r.URL.Path[len("/topic/"):]) if err != nil { @@ -263,7 +265,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ } // Get the topic.. - err = get_topic_user_stmt.QueryRow(topic.ID).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.CreatedByName, &topic.Avatar, &is_super_admin, &group, &topic.URLPrefix, &topic.URLName, &topic.Level, &topic.IpAddress) + err = get_topic_user_stmt.QueryRow(topic.ID).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.CreatedByName, &topic.Avatar, &group, &topic.URLPrefix, &topic.URLName, &topic.Level) if err == sql.ErrNoRows { NotFound(w,r,user) return @@ -287,7 +289,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ } else { topic.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topic.CreatedBy),1) } - if is_super_admin || groups[group].Is_Mod || groups[group].Is_Admin { + if groups[group].Is_Mod || groups[group].Is_Admin { topic.Css = staff_css_tmpl topic.Level = -1 } @@ -305,16 +307,34 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ } } + // Calculate the offset + last_page := int(topic.PostCount / 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 + //fmt.Println(topic.PostCount) + //fmt.Println((topic.PostCount / items_per_page) + 1) + //fmt.Println(page) + //fmt.Println(offset) + } else { + page = 1 + } + // Get the replies.. - rows, err := get_topic_replies_stmt.Query(topic.ID) - if err != nil { + rows, err := get_topic_replies_offset_stmt.Query(topic.ID, offset) + if err == sql.ErrNoRows { + LocalError("Bad Page. Some of the posts may have been deleted or you got here by directly typing in the page number.",w,r,user) + return + } else if err != nil { InternalError(err,w,r,user) return } replyItem := Reply{Css: no_css_tmpl} for rows.Next() { - err := rows.Scan(&replyItem.ID, &replyItem.Content, &replyItem.CreatedBy, &replyItem.CreatedAt, &replyItem.LastEdit, &replyItem.LastEditBy, &replyItem.Avatar, &replyItem.CreatedByName, &is_super_admin, &group, &replyItem.URLPrefix, &replyItem.URLName, &replyItem.Level, &replyItem.IpAddress) + err := rows.Scan(&replyItem.ID, &replyItem.Content, &replyItem.CreatedBy, &replyItem.CreatedAt, &replyItem.LastEdit, &replyItem.LastEditBy, &replyItem.Avatar, &replyItem.CreatedByName, &group, &replyItem.URLPrefix, &replyItem.URLName, &replyItem.Level, &replyItem.IpAddress) if err != nil { InternalError(err,w,r,user) return @@ -323,7 +343,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ replyItem.ParentID = topic.ID replyItem.ContentHtml = template.HTML(parse_message(replyItem.Content)) replyItem.ContentLines = strings.Count(replyItem.Content,"\n") - if is_super_admin || groups[group].Is_Mod || groups[group].Is_Admin { + if groups[group].Is_Mod || groups[group].Is_Admin { replyItem.Css = staff_css_tmpl replyItem.Level = -1 } else { @@ -362,7 +382,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ } rows.Close() - tpage := TopicPage{topic.Title,user,noticeList,replyList,topic,nil} + tpage := TopicPage{topic.Title,user,noticeList,replyList,topic,page,last_page,nil} if template_topic_handle != nil { template_topic_handle(tpage,w) } else { @@ -392,7 +412,6 @@ func route_profile(w http.ResponseWriter, r *http.Request){ replyCss template.CSS replyLines int replyTag string - is_super_admin bool group int replyList []Reply @@ -439,7 +458,7 @@ func route_profile(w http.ResponseWriter, r *http.Request){ } // 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) + 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.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 @@ -447,14 +466,14 @@ func route_profile(w http.ResponseWriter, r *http.Request){ defer rows.Close() for rows.Next() { - err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &is_super_admin, &group) + err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &group) if err != nil { InternalError(err,w,r,user) return } replyLines = strings.Count(replyContent,"\n") - if is_super_admin || groups[group].Is_Mod || groups[group].Is_Admin { + if groups[group].Is_Mod || groups[group].Is_Admin { replyCss = staff_css_tmpl } else { replyCss = no_css_tmpl @@ -600,6 +619,11 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) { return } + _, err = add_replies_to_topic_stmt.Exec(1, tid) + 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) diff --git a/template_list.go b/template_list.go index 4ee2971e..c94ee179 100644 --- a/template_list.go +++ b/template_list.go @@ -50,100 +50,106 @@ var header_3 []byte = []byte(`
`) var header_4 []byte = []byte(`
`) var header_5 []byte = []byte(`
`) -var topic_0 []byte = []byte(` +var topic_0 []byte = []byte(`
`) +var topic_3 []byte = []byte(``) +var topic_6 []byte = []byte(`
+var topic_7 []byte = []byte(`' method="post">
+var topic_8 []byte = []byte(` style="background-color: #FFFFEA;"`) +var topic_9 []byte = []byte(` style="background-color: #eaeaea;"`) +var topic_10 []byte = []byte(`> `) -var topic_5 []byte = []byte(` +var topic_11 []byte = []byte(` `) -var topic_6 []byte = []byte(`🔒︎`) -var topic_7 []byte = []byte(` +var topic_12 []byte = []byte(`🔒︎`) +var topic_13 []byte = []byte(` Edit +var topic_14 []byte = []byte(`' class="username hide_on_edit open_edit" style="font-weight: normal;margin-left: 6px;">Edit Delete +var topic_15 []byte = []byte(`' class="username" style="font-weight: normal;">Delete `) -var topic_10 []byte = []byte(`Unpin`) -var topic_12 []byte = []byte(`Pin`) -var topic_14 []byte = []byte(` +var topic_16 []byte = []byte(`Unpin`) +var topic_18 []byte = []byte(`Pin`) +var topic_20 []byte = []byte(` +var topic_21 []byte = []byte(`' type="text" /> `) -var topic_16 []byte = []byte(` +var topic_22 []byte = []byte(` Report +var topic_23 []byte = []byte(`?session=`) +var topic_24 []byte = []byte(`&type=topic" class="username report_item" style="font-weight: normal;">Report
+var topic_25 []byte = []byte(`background-image: url(`) +var topic_26 []byte = []byte(`), url(/static/white-dot.jpg);background-position: 0px `) +var topic_27 []byte = []byte(`-1`) +var topic_28 []byte = []byte(`0px;background-repeat: no-repeat, repeat-y;background-size: 128px;padding-left: 136px;`) +var topic_29 []byte = []byte(`">

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

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



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

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

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

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



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



`) -var topic_39 []byte = []byte(` +var topic_44 []byte = []byte(`" class="username real_username">`) +var topic_45 []byte = []byte(` `) -var topic_40 []byte = []byte(` `) -var topic_42 []byte = []byte(` `) -var topic_44 []byte = []byte(` +var topic_46 []byte = []byte(` `) +var topic_48 []byte = []byte(` `) +var topic_50 []byte = []byte(` +var topic_51 []byte = []byte(`?session=`) +var topic_52 []byte = []byte(`&type=reply" class="mod_button"> `) -var topic_48 []byte = []byte(`style="color: #505050;float: right;">Level `) -var topic_49 []byte = []byte(` +var topic_53 []byte = []byte(`style="float: right;">`) +var topic_54 []byte = []byte(`style="color: #505050;float: right;">Level `) +var topic_55 []byte = []byte(`
`) -var topic_50 []byte = []byte(`
+var topic_56 []byte = []byte(`
`) -var topic_51 []byte = []byte(` +var topic_57 []byte = []byte(`
+var topic_58 []byte = []byte(`' type="hidden" />
@@ -157,42 +163,48 @@ var footer_0 []byte = []byte(`