diff --git a/README.md b/README.md index bca745cf..2c1da133 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Add a plugin system. Tweak the CSS to make it responsive. -Nest the moderation routes to possibly speed routing up a little...? +Implement a faster router. Add a friend system. diff --git a/data.sql b/data.sql index ef692c39..577cef3f 100644 --- a/data.sql +++ b/data.sql @@ -102,14 +102,20 @@ CREATE TABLE `plugins`( INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool'); INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3',); -INSERT INTO users(`name`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`,`message`) -VALUES ('Admin',1,1,NOW(),NOW(),''); +INSERT INTO users(`name`,`email`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`,`message`) +VALUES ('Admin','admin@localhost',1,1,NOW(),NOW(),''); /* The Permissions: BanUsers ActivateUsers +EditUser +EditUserEmail +EditUserPassword +EditUserGroup +EditUserGroupSuperMod +EditUserGroupAdmin ManageForums EditSettings ManagePlugins @@ -126,11 +132,11 @@ PinTopic CloseTopic */ -INSERT INTO users_groups(`name`,`permissions`,`active`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"ManageForums":true,"EditSettings":true,"ManagePlugins":true,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,1,1,"Admin"); -INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`tag`) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,"Mod"); -INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{"BanUsers":false,"ActivateUsers":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":true,"EditTopic":false,"DeleteTopic":false,"CreateReply":true,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}'); -INSERT INTO users_groups(`name`,`permissions`,`is_banned`) VALUES ('Banned','{"BanUsers":false,"ActivateUsers":false,"ManageForums":false,"EditSettings":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`,`is_banned`) VALUES ('Awaiting Activation','{"BanUsers":false,"ActivateUsers":false,"ManageForums":false,"EditSettings":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 users_groups(`name`,`permissions`,`active`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManagePlugins":true,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,1,1,"Admin"); +INSERT INTO users_groups(`name`,`permissions`,`is_mod`,`tag`) VALUES ('Moderator','{"BanUsers":true,"ActivateUsers":false,"EditUser":true,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":true,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":true,"ViewTopic":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}',1,"Mod"); +INSERT INTO users_groups(`name`,`permissions`) VALUES ('Member','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":false,"ManagePlugins":false,"ViewIPs":false,"ViewTopic":true,"CreateTopic":true,"EditTopic":false,"DeleteTopic":false,"CreateReply":true,"EditReply":false,"DeleteReply":false,"PinTopic":false,"CloseTopic":false}'); +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,"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`,`is_banned`) VALUES ('Awaiting Activation','{"BanUsers":false,"ActivateUsers":false,"EditUser":false,"EditUserEmail":false,"EditUserPassword":false,"EditUserGroup":false,"EditUserGroupSuperMod":false,"EditUserGroupAdmin":false,"ManageForums":false,"EditSettings":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`,`lastTopicTime`) VALUES ('General',NOW()); INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`) diff --git a/general_test.go b/general_test.go index bef30b46..b4fa2eb9 100644 --- a/general_test.go +++ b/general_test.go @@ -1,17 +1,24 @@ package main +//import "fmt" +import "log" +import "bytes" import "testing" +import "net/http" +import "net/http/httptest" import "io/ioutil" import "html/template" -func BenchmarkTemplates(b *testing.B) { - user := User{0,"Bob",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} - admin := User{1,"Admin",0,true,true,true,true,true,false,AllPerms,"",false,"","","","",""} +func BenchmarkTopicTemplate(b *testing.B) { + b.ReportAllocs() + + user := User{0,"Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} + admin := User{1,"Admin","admin@localhost",0,true,true,true,true,true,false,AllPerms,"",false,"","","","",""} var noticeList map[int]string = make(map[int]string) noticeList[0] = "test" topic := TopicUser{0,"Lol",template.HTML("Hey everyone!"),0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""} - var replyList []interface{} + var replyList []Reply replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) @@ -23,65 +30,203 @@ func BenchmarkTemplates(b *testing.B) { replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - - var replyList2 []Reply - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - replyList2 = append(replyList2, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - - pi := Page{"Topic Blah","topic",user,noticeList,replyList,topic} - pi2 := Page{"Topic Blah","topic",admin,noticeList,replyList,topic} - tpage := TopicPage{"Topic Blah","topic",user,noticeList,replyList2,topic,false} - tpage2 := TopicPage{"Topic Blah","topic",admin,noticeList,replyList2,topic,false} + tpage := TopicPage{"Topic Blah","topic",user,noticeList,replyList,topic,false} + tpage2 := TopicPage{"Topic Blah","topic",admin,noticeList,replyList,topic,false} w := ioutil.Discard b.Run("compiled_useradmin", func(b *testing.B) { - for i := 0; i < b.N; i++ { - template_topic_2(pi2,w) - } - }) - /*b.Run("interpreted_useradmin", func(b *testing.B) { - for i := 0; i < b.N; i++ { - templates.ExecuteTemplate(w,"topic.html", pi2) - } - })*/ - b.Run("compiled_userguest", func(b *testing.B) { - for i := 0; i < b.N; i++ { - template_topic_2(pi,w) - } - }) - /*b.Run("interpreted_userguest", func(b *testing.B) { - for i := 0; i < b.N; i++ { - templates.ExecuteTemplate(w,"topic.html", pi) - } - })*/ - - - b.Run("compiled_customstruct_useradmin", func(b *testing.B) { for i := 0; i < b.N; i++ { template_topic(tpage2,w) } }) - b.Run("interpreted_customstruct_useradmin", func(b *testing.B) { + b.Run("interpreted_useradmin", func(b *testing.B) { for i := 0; i < b.N; i++ { templates.ExecuteTemplate(w,"topic.html", tpage2) } }) - b.Run("compiled_customstruct_userguest", func(b *testing.B) { + b.Run("compiled_userguest", func(b *testing.B) { for i := 0; i < b.N; i++ { template_topic(tpage,w) } }) - b.Run("interpreted_customstruct_userguest", func(b *testing.B) { + b.Run("interpreted_userguest", func(b *testing.B) { for i := 0; i < b.N; i++ { templates.ExecuteTemplate(w,"topic.html", tpage) } }) } + +func BenchmarkTopicsTemplate(b *testing.B) { + b.ReportAllocs() + + user := User{0,"Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} + admin := User{1,"Admin","admin@localhost",0,true,true,true,true,true,false,AllPerms,"",false,"","","","",""} + var noticeList map[int]string = make(map[int]string) + noticeList[0] = "test" + + var topicList []interface{} + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + topicList = append(topicList, TopicUser{0,"Hey everyone!",template.HTML("Hey everyone!"),0,false,false,"0000-00-00 00:00:00",1,"open","Admin","",no_css_tmpl,0,"Admin","","",""}) + + tpage := Page{"Topic Blah","topic",user,noticeList,topicList,0} + tpage2 := Page{"Topic Blah","topic",admin,noticeList,topicList,0} + w := ioutil.Discard + + b.Run("compiled_useradmin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + template_topics(tpage2,w) + } + }) + b.Run("interpreted_useradmin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + templates.ExecuteTemplate(w,"topics.html", tpage2) + } + }) + b.Run("compiled_userguest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + template_topics(tpage,w) + } + }) + b.Run("interpreted_userguest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + templates.ExecuteTemplate(w,"topics.html", tpage) + } + }) +} + +func BenchmarkRoute(b *testing.B) { + b.ReportAllocs() + + admin_uid_cookie := http.Cookie{Name: "uid",Value: "1",Path: "/",MaxAge: year} + // TO-DO: Stop hard-coding this value + admin_session_cookie := http.Cookie{Name: "session",Value: "TKBh5Z-qEQhWDBnV6_XVmOhKAowMYPhHeRlrQjjbNc0QRrRiglvWOYFDc1AaMXQIywvEsyA2AOBRYUrZ5kvnGhThY1GhOW6FSJADnRWm_bI=",Path: "/",MaxAge: year} + + topic_w := httptest.NewRecorder() + topic_req := httptest.NewRequest("get","/topic/1",bytes.NewReader(nil)) + topic_req_admin := topic_req + topic_req_admin.AddCookie(&admin_uid_cookie) + topic_req_admin.AddCookie(&admin_session_cookie) + topic_handler := http.HandlerFunc(route_topic_id) + + topics_w := httptest.NewRecorder() + topics_req := httptest.NewRequest("get","/topics/",bytes.NewReader(nil)) + topics_req_admin := topics_req + topics_req_admin.AddCookie(&admin_uid_cookie) + topics_req_admin.AddCookie(&admin_session_cookie) + topics_handler := http.HandlerFunc(route_topics) + + forum_w := httptest.NewRecorder() + forum_req := httptest.NewRequest("get","/forum/1",bytes.NewReader(nil)) + forum_req_admin := forum_req + forum_req_admin.AddCookie(&admin_uid_cookie) + forum_req_admin.AddCookie(&admin_session_cookie) + forum_handler := http.HandlerFunc(route_forum) + + forums_w := httptest.NewRecorder() + forums_req := httptest.NewRequest("get","/forums/",bytes.NewReader(nil)) + forums_req_admin := forums_req + forums_req_admin.AddCookie(&admin_uid_cookie) + forums_req_admin.AddCookie(&admin_session_cookie) + forums_handler := http.HandlerFunc(route_forums) + + debug = false + nogrouplog = true + + // init_database is a little noisy for a benchmark + discard := ioutil.Discard + log.SetOutput(discard) + + var err error + init_database(err); + external_sites["YT"] = "https://www.youtube.com/" + hooks["trow_assign"] = nil + hooks["rrow_assign"] = nil + + for name, body := range plugins { + if body.Active { + plugins[name].Init() + } + } + + b.Run("topic_admin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //topic_w.Code = 200 + topic_w.Body.Reset() + topic_handler.ServeHTTP(topic_w,topic_req_admin) + //if topic_w.Code != 200 { + // fmt.Println(topic_w.Body) + // panic("HTTP Error!") + //} + } + }) + b.Run("topic_guest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //topic_w.Code = 200 + topic_w.Body.Reset() + topic_handler.ServeHTTP(topic_w,topic_req) + } + }) + b.Run("topics_admin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //topics_w.Code = 200 + topics_w.Body.Reset() + topics_handler.ServeHTTP(topics_w,topics_req_admin) + } + }) + b.Run("topics_guest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //topics_w.Code = 200 + topics_w.Body.Reset() + topics_handler.ServeHTTP(topics_w,topics_req) + } + }) + b.Run("forum_admin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //forum_w.Code = 200 + forum_w.Body.Reset() + forum_handler.ServeHTTP(forum_w,forum_req_admin) + } + }) + b.Run("forum_guest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //forum_w.Code = 200 + forum_w.Body.Reset() + forum_handler.ServeHTTP(forum_w,forum_req) + } + }) + b.Run("forums_admin", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //forums_w.Code = 200 + forums_w.Body.Reset() + forums_handler.ServeHTTP(forums_w,forums_req_admin) + } + }) + b.Run("forums_guest", func(b *testing.B) { + for i := 0; i < b.N; i++ { + //forums_w.Code = 200 + forums_w.Body.Reset() + forums_handler.ServeHTTP(forums_w,forums_req) + } + }) + + serveMux := http.NewServeMux() + serveMux.HandleFunc("/topics/", route_topics) + b.Run("topics_guest_plus_router", func(b *testing.B) { + for i := 0; i < b.N; i++ { + topics_w.Body.Reset() + serveMux.ServeHTTP(topics_w,topics_req) + } + }) +} + +/*func TestRoute(b *testing.T) { + +}*/ \ No newline at end of file diff --git a/gosora.exe b/gosora.exe index 1388b30e..21ab5452 100644 Binary files a/gosora.exe and b/gosora.exe differ diff --git a/group.go b/group.go index d7a9d5d8..155f39e7 100644 --- a/group.go +++ b/group.go @@ -16,11 +16,18 @@ type Group struct Tag string } +// Permission Structure: ActionComponent[Subcomponent]Flag type Perms struct { // Global Permissions BanUsers bool ActivateUsers bool + EditUser bool + EditUserEmail bool + EditUserPassword bool + EditUserGroup bool + EditUserGroupSuperMod bool + EditUserGroupAdmin bool ManageForums bool // This could be local, albeit limited for per-forum managers EditSettings bool ManagePlugins bool @@ -71,6 +78,12 @@ func init() { AllPerms = Perms{ BanUsers: true, ActivateUsers: true, + EditUser: true, + EditUserEmail: true, + EditUserPassword: true, + EditUserGroup: true, + EditUserGroupSuperMod: true, + EditUserGroupAdmin: true, ManageForums: true, EditSettings: true, ManagePlugins: true, diff --git a/images/bench_round1.PNG b/images/bench_round1.PNG new file mode 100644 index 00000000..f7e4c0a8 Binary files /dev/null and b/images/bench_round1.PNG differ diff --git a/images/bench_round1_with_memory_allocs.PNG b/images/bench_round1_with_memory_allocs.PNG new file mode 100644 index 00000000..be57685d Binary files /dev/null and b/images/bench_round1_with_memory_allocs.PNG differ diff --git a/images/bench_round1_with_router_bench.PNG b/images/bench_round1_with_router_bench.PNG new file mode 100644 index 00000000..89661c04 Binary files /dev/null and b/images/bench_round1_with_router_bench.PNG differ diff --git a/images/bench_topic_route.PNG b/images/bench_topic_route.PNG new file mode 100644 index 00000000..7aa8851c Binary files /dev/null and b/images/bench_topic_route.PNG differ diff --git a/main.go b/main.go index 7968224a..e4d1e7c7 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ const kilobyte int = 1024 const megabyte int = 1024 * 1024 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 no_css_tmpl = template.CSS("") @@ -39,7 +40,7 @@ var template_profile_handle func(Page,io.Writer) = nil func compile_templates() { var c CTemplateSet - user := User{0,"",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} + user := User{0,"","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} var noticeList map[int]string = make(map[int]string) noticeList[0] = "test" @@ -206,6 +207,7 @@ func main(){ http.HandleFunc("/panel/plugins/deactivate/", route_panel_plugins_deactivate) http.HandleFunc("/panel/users/", route_panel_users) http.HandleFunc("/panel/users/edit/", route_panel_users_edit) + http.HandleFunc("/panel/users/edit/submit/", route_panel_users_edit_submit) http.HandleFunc("/", default_route) diff --git a/mod_routes.go b/mod_routes.go index adabbacb..09a5bf21 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -446,6 +446,11 @@ func route_unban(w http.ResponseWriter, r *http.Request) { return } + if r.FormValue("session") != user.Session { + SecurityError(w,r,user) + return + } + uid, err := strconv.Atoi(r.URL.Path[len("/users/unban/"):]) if err != nil { LocalError("The provided User ID is not a valid number.",w,r,user) @@ -486,6 +491,11 @@ func route_activate(w http.ResponseWriter, r *http.Request) { return } + if r.FormValue("session") != user.Session { + SecurityError(w,r,user) + return + } + uid, err := strconv.Atoi(r.URL.Path[len("/users/activate/"):]) if err != nil { LocalError("The provided User ID is not a valid number.",w,r,user) @@ -678,7 +688,6 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request) { } forum_name := r.PostFormValue("edit_item") - forum, ok := forums[fid]; if !ok { LocalError("The forum you're trying to edit doesn't exist.",w,r,user) @@ -790,7 +799,7 @@ func route_panel_setting(w http.ResponseWriter, r *http.Request){ labels := strings.Split(llist,",") for index, label := range labels { - itemList = append(itemList, SettingLabel{ + itemList = append(itemList, OptionLabel{ Label: label, Value: index + 1, Selected: conv == (index + 1), @@ -887,6 +896,11 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request){ return } + if r.FormValue("session") != user.Session { + SecurityError(w,r,user) + return + } + uname := r.URL.Path[len("/panel/plugins/activate/"):] plugin, ok := plugins[uname] if !ok { @@ -945,6 +959,11 @@ func route_panel_plugins_deactivate(w http.ResponseWriter, r *http.Request){ return } + if r.FormValue("session") != user.Session { + SecurityError(w,r,user) + return + } + uname := r.URL.Path[len("/panel/plugins/deactivate/"):] plugin, ok := plugins[uname] if !ok { @@ -990,7 +1009,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request){ } var userList []interface{} - rows, err := db.Query("SELECT `uid`, `name`, `group`, `active`, `is_super_admin`, `avatar` FROM users") + rows, err := db.Query("SELECT `uid`,`name`,`group`,`active`,`is_super_admin`,`avatar` FROM users") if err != nil { InternalError(err,w,r,user) return @@ -1043,5 +1062,150 @@ func route_panel_users(w http.ResponseWriter, r *http.Request){ } func route_panel_users_edit(w http.ResponseWriter, r *http.Request){ + user, noticeList, ok := SessionCheck(w,r) + if !ok { + return + } + // Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with + if !user.Is_Super_Mod || !user.Perms.EditUser { + NoPermissions(w,r,user) + return + } + + var err error + targetUser := User{ID: 0,} + targetUser.ID, err = strconv.Atoi(r.URL.Path[len("/panel/users/edit/"):]) + if err != nil { + LocalError("The provided User ID is not a valid number.",w,r,user) + return + } + + err = db.QueryRow("SELECT `name`, `email`, `group` from `users` where `uid` = ?", targetUser.ID).Scan(&targetUser.Name, &targetUser.Email, &targetUser.Group) + if err == sql.ErrNoRows { + LocalError("The user you're trying to edit doesn't exist.",w,r,user) + return + } else if err != nil { + InternalError(err,w,r,user) + return + } + + targetUser.Is_Admin = targetUser.Is_Super_Admin || groups[targetUser.Group].Is_Admin + targetUser.Is_Super_Mod = groups[targetUser.Group].Is_Mod || targetUser.Is_Admin + if targetUser.Is_Admin && !user.Is_Admin { + LocalError("Only administrators can edit the account of an administrator.",w,r,user) + return + } + + var groupList []interface{} + for _, group := range groups { + if !user.Perms.EditUserGroupAdmin && group.Is_Admin { + continue + } + if !user.Perms.EditUserGroupSuperMod && group.Is_Mod { + continue + } + groupList = append(groupList, group) + } + + pi := Page{"User Editor","panel-user-edit",user,noticeList,groupList,targetUser} + err = templates.ExecuteTemplate(w,"panel-user-edit.html", pi) + if err != nil { + InternalError(err, w, r, user) + } } + +func route_panel_users_edit_submit(w http.ResponseWriter, r *http.Request){ + user, ok := SimpleSessionCheck(w,r) + if !ok { + return + } + if !user.Is_Super_Mod || !user.Perms.EditUser { + NoPermissions(w,r,user) + return + } + + if r.FormValue("session") != user.Session { + SecurityError(w,r,user) + return + } + + var err error + targetUser := User{ID: 0,} + targetUser.ID, err = strconv.Atoi(r.URL.Path[len("/panel/users/edit/submit/"):]) + if err != nil { + LocalError("The provided User ID is not a valid number.",w,r,user) + return + } + + err = db.QueryRow("SELECT `name`, `email`, `group` from `users` where `uid` = ?", targetUser.ID).Scan(&targetUser.Name, &targetUser.Email, &targetUser.Group) + if err == sql.ErrNoRows { + LocalError("The user you're trying to edit doesn't exist.",w,r,user) + return + } else if err != nil { + InternalError(err,w,r,user) + return + } + + targetUser.Is_Admin = targetUser.Is_Super_Admin || groups[targetUser.Group].Is_Admin + targetUser.Is_Super_Mod = groups[targetUser.Group].Is_Mod || targetUser.Is_Admin + if targetUser.Is_Admin && !user.Is_Admin { + LocalError("Only administrators can edit the account of an administrator.",w,r,user) + return + } + + newname := html.EscapeString(r.PostFormValue("user-name")) + if newname == "" { + LocalError("You didn't put in a username.", w, r, user) + return + } + + newemail := html.EscapeString(r.PostFormValue("user-email")) + if newemail == "" { + LocalError("You didn't put in an email address.", w, r, user) + return + } + if (newemail != targetUser.Email) && !user.Perms.EditUserEmail { + LocalError("You need the EditUserEmail permission to edit the email address of a user.", w, r, user) + return + } + + newpassword := r.PostFormValue("user-password") + if newpassword != "" && !user.Perms.EditUserPassword { + LocalError("You need the EditUserPassword permission to edit the password of a user.", w, r, user) + return + } + + newgroup, err := strconv.Atoi(r.PostFormValue("user-group")) + if err != nil { + LocalError("The provided GroupID is not a valid number.",w,r,user) + return + } + + _, ok = groups[newgroup] + if !ok { + LocalError("The group you're trying to place this user in doesn't exist.",w,r,user) + return + } + + if !user.Perms.EditUserGroupAdmin && groups[newgroup].Is_Admin { + LocalError("You need the EditUserGroupAdmin permission to assign someone to an administrator group.",w,r,user) + return + } + if !user.Perms.EditUserGroupSuperMod && groups[newgroup].Is_Mod { + LocalError("You need the EditUserGroupAdmin permission to assign someone to a super mod group.",w,r,user) + return + } + + _, err = update_user_stmt.Exec(newname,newemail,newgroup,targetUser.ID) + if err != nil { + InternalError(err,w,r,user) + return + } + + if newpassword != "" { + SetPassword(targetUser.ID, newpassword) + } + + http.Redirect(w,r,"/panel/users/edit/" + strconv.Itoa(targetUser.ID),http.StatusSeeOther) +} \ No newline at end of file diff --git a/mysql.go b/mysql.go index f821537b..b0e98394 100644 --- a/mysql.go +++ b/mysql.go @@ -9,6 +9,7 @@ import "encoding/json" var db *sql.DB var get_session_stmt *sql.Stmt +var get_topic_list_stmt *sql.Stmt var create_topic_stmt *sql.Stmt var create_report_stmt *sql.Stmt var create_reply_stmt *sql.Stmt @@ -40,6 +41,7 @@ var update_forum_stmt *sql.Stmt var update_setting_stmt *sql.Stmt var add_plugin_stmt *sql.Stmt var update_plugin_stmt *sql.Stmt +var update_user_stmt *sql.Stmt func init_database(err error) { if(dbpassword != ""){ @@ -57,13 +59,19 @@ func init_database(err error) { } log.Print("Preparing get_session statement.") - get_session_stmt, err = db.Prepare("SELECT `uid`, `name`, `group`, `is_super_admin`, `session`, `avatar`, `message`, `url_prefix`, `url_name` FROM `users` WHERE `uid` = ? AND `session` = ? AND `session` <> ''") + get_session_stmt, err = db.Prepare("select `uid`, `name`, `group`, `is_super_admin`, `session`, `avatar`, `message`, `url_prefix`, `url_name` FROM `users` WHERE `uid` = ? AND `session` = ? AND `session` <> ''") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing get_topic_list statement.") + get_topic_list_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 order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC") if err != nil { log.Fatal(err) } log.Print("Preparing create_topic statement.") - create_topic_stmt, err = db.Prepare("INSERT INTO topics(title,content,parsed_content,createdAt,createdBy) VALUES(?,?,?,NOW(),?)") + create_topic_stmt, err = db.Prepare("insert into topics(title,content,parsed_content,createdAt,createdBy) VALUES(?,?,?,NOW(),?)") if err != nil { log.Fatal(err) } @@ -245,6 +253,12 @@ func init_database(err error) { log.Fatal(err) } + log.Print("Preparing update_user statement.") + update_user_stmt, err = db.Prepare("UPDATE `users` SET `name` = ?, `email` = ?, `group` = ? WHERE `uid` = ?") + if err != nil { + log.Fatal(err) + } + log.Print("Loading the usergroups.") rows, err := db.Query("SELECT gid,name,permissions,is_mod,is_admin,is_banned,tag FROM users_groups") if err != nil { @@ -264,8 +278,10 @@ func init_database(err error) { log.Fatal(err) } - fmt.Println(group.Name + ": ") - fmt.Printf("%+v\n", group.Perms) + if !nogrouplog { + fmt.Println(group.Name + ": ") + fmt.Printf("%+v\n", group.Perms) + } group.Perms.ExtData = make(map[string]bool) groups[group.ID] = group diff --git a/images/test_compiled_vs_interpreted.PNG b/old-images/test_compiled_vs_interpreted.PNG similarity index 100% rename from images/test_compiled_vs_interpreted.PNG rename to old-images/test_compiled_vs_interpreted.PNG diff --git a/images/test_maps_vs_slices.PNG b/old-images/test_maps_vs_slices.PNG similarity index 100% rename from images/test_maps_vs_slices.PNG rename to old-images/test_maps_vs_slices.PNG diff --git a/images/test_typedstruct_vs_interfacestruct.PNG b/old-images/test_typedstruct_vs_interfacestruct.PNG similarity index 100% rename from images/test_typedstruct_vs_interfacestruct.PNG rename to old-images/test_typedstruct_vs_interfacestruct.PNG diff --git a/router.go b/router.go new file mode 100644 index 00000000..36d956bb --- /dev/null +++ b/router.go @@ -0,0 +1,31 @@ +package main +/*import "sync" +import "net/http" + +type Router struct { + mu sync.RWMutex + routes map[string]http.Handler +} + +func (route *Router) ServeHTTP() { + route.mu.RLock() + defer route.mu.RUnlock() + + if path[0] != "/" { + return route.routes["/"] + } + + // Do something on the path to turn slashes facing the wrong way "\" into "/" slashes. If it's bytes, then alter the bytes in place for the maximum speed + + handle := route.routes[path] + if !ok { + if path[-1] != "/" { + handle = route.routes[path + "/"] + if !ok { + return route.routes["/"] + } + return handle + } + } + return handle +}*/ \ No newline at end of file diff --git a/routes.go b/routes.go index 0b34c298..8168190e 100644 --- a/routes.go +++ b/routes.go @@ -98,7 +98,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){ avatar string ) - rows, err := db.Query("select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC") + rows, err := get_topic_list_stmt.Query() if err != nil { InternalError(err,w,r,user) return @@ -287,14 +287,16 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ replyList []Reply ) - topic := TopicUser{0,"","",0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""} + topic := TopicUser{0,"","",0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""} topic.ID, err = strconv.Atoi(r.URL.Path[len("/topic/"):]) if err != nil { LocalError("The provided TopicID is not a valid number.",w,r,user) return } if !user.Perms.ViewTopic { + //fmt.Printf("%+v\n", user) + //fmt.Printf("%+v\n", user.Perms) NoPermissions(w,r,user) return } diff --git a/setting.go b/setting.go index 63fa461b..7ce7211a 100644 --- a/setting.go +++ b/setting.go @@ -4,7 +4,7 @@ import "strings" var settingLabels map[string]string -type SettingLabel struct +type OptionLabel struct { Label string Value int diff --git a/template_topic_old.go b/template_topic_old.go deleted file mode 100644 index affc9d1c..00000000 --- a/template_topic_old.go +++ /dev/null @@ -1,186 +0,0 @@ -package main -import "io" -import "strconv" -import "html/template" - -/*func init() { -ctemplates["topic"] = template_topic -}*/ - -func template_topic_2(tmpl_topic_vars Page, w io.Writer) { -var extra_data TopicUser = tmpl_topic_vars.Something.(TopicUser) -w.Write([]byte(` - - - ` + tmpl_topic_vars.Title + ` - - - - - - -
- -`)) -if len(tmpl_topic_vars.NoticeList) != 0 { -for _, item := range tmpl_topic_vars.NoticeList { -w.Write([]byte(`
` + item + `
`)) -} -} -w.Write([]byte(` -
-
-
- ` + extra_data.Title + ` - ` + extra_data.Status + ` - Status - `)) -if tmpl_topic_vars.CurrentUser.Is_Mod { -w.Write([]byte(` - Edit - Delete - `)) -if extra_data.Sticky { -w.Write([]byte(`Unpin`)) -} else { -w.Write([]byte(`Pin`)) -} -w.Write([]byte(` - - - - - `)) -} -w.Write([]byte(` - Report -
-
-
-
-
- ` + string(extra_data.Content.(template.HTML)) + ` - -

- ` + extra_data.CreatedByName + ` - `)) -if extra_data.Tag != "" { -w.Write([]byte(`` + extra_data.Tag + ``)) -} else { -if extra_data.URLName != "" { -w.Write([]byte(`` + extra_data.URLName + ` - ` + extra_data.URLPrefix + ``)) -} -} -w.Write([]byte(` -
-

-
- `)) -if len(tmpl_topic_vars.ItemList) != 0 { -for _, item := range tmpl_topic_vars.ItemList { -w.Write([]byte(` -
- ` + string(item.(Reply).ContentHtml) + ` -

- ` + item.(Reply).CreatedByName + ` - `)) -if tmpl_topic_vars.CurrentUser.Is_Mod { -w.Write([]byte(` - `)) -} -w.Write([]byte(` - - `)) -if item.(Reply).Tag != "" { -w.Write([]byte(`` + item.(Reply).Tag + ``)) -} else { -if item.(Reply).URLName != "" { -w.Write([]byte(`` + item.(Reply).URLName + ` - ` + item.(Reply).URLPrefix + ``)) -} -} -w.Write([]byte(` -
`)) -} -} -w.Write([]byte(` -
-`)) -if !tmpl_topic_vars.CurrentUser.Is_Banned && tmpl_topic_vars.CurrentUser.Loggedin { -w.Write([]byte(` -
-
- -
-
-
-
-
-
-
-
-`)) -} -w.Write([]byte(` - -
- -`)) -} diff --git a/templates/panel-user-edit.html b/templates/panel-user-edit.html new file mode 100644 index 00000000..f67f4488 --- /dev/null +++ b/templates/panel-user-edit.html @@ -0,0 +1,45 @@ +{{template "header.html" . }} +
+
Control Panel
+
Forums
+
Settings
+
Plugins
+
Users
+
Groups
+
Coming Soon
+
Coming Soon
+
Coming Soon
+
Reports
+
+
+
User Editor
+
+
+
+
+ +
+
+ {{if .CurrentUser.Perms.EditUserPassword}}
+ +
+
{{end}} + {{if .CurrentUser.Perms.EditUserEmail}}
+ +
+
{{end}} + {{if .CurrentUser.Perms.EditUserGroup}} +
+ +
+ +
+
{{end}} +
+
+
+ + +{{template "footer.html" . }} \ No newline at end of file diff --git a/templates/panel-users.html b/templates/panel-users.html index 93581a01..d6063437 100644 --- a/templates/panel-users.html +++ b/templates/panel-users.html @@ -17,7 +17,7 @@
{{range .ItemList}}
- {{.Name}} + {{.Name}} Profile {{if .Tag}}{{.Tag}}{{end}} diff --git a/templates/profile.html b/templates/profile.html index eb2093c2..c18e6d60 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -7,7 +7,7 @@
Add Friend {{if (.CurrentUser.Is_Super_Mod) and not (.Something.Is_Super_Mod) }} - {{if .Something.Is_Banned }}Unban{{else}}Ban{{end}} + {{if .Something.Is_Banned }}Unban{{else}}Ban{{end}} {{end}} Report
diff --git a/templates_old.go b/templates_old.go new file mode 100644 index 00000000..50bdbd0a --- /dev/null +++ b/templates_old.go @@ -0,0 +1,657 @@ +package main +import "log" +import "fmt" +import "strings" +import "reflect" +import "path/filepath" +import "io/ioutil" +import "text/template/parse" + +/* Keeping this so I can benchmark against it */ + +type CTemplateSet2 struct +{ + tlist map[string]*parse.Tree + dir string + funcMap map[string]interface{} + importMap map[string]string + varList map[string]VarItem + localVars map[string]map[string]VarItemReflect + //tempVars map[string]string + doImports bool + expectsInt interface{} +} + +func (c *CTemplateSet2) compile_template_2(name string, dir string, expects string, expectsInt interface{}, varList map[string]VarItem) (out string) { + c.dir = dir + c.doImports = true + c.funcMap = make(map[string]interface{}) + c.funcMap["and"] = "&&" + c.funcMap["not"] = "!" + c.funcMap["or"] = "||" + c.funcMap["eq"] = true + c.funcMap["ge"] = true + c.funcMap["gt"] = true + c.funcMap["le"] = true + c.funcMap["lt"] = true + c.funcMap["ne"] = true + c.importMap = make(map[string]string) + c.importMap["strconv"] = "strconv" + c.varList = varList + c.expectsInt = expectsInt + holdreflect := reflect.ValueOf(expectsInt) + + res, err := ioutil.ReadFile(dir + name) + if err != nil { + log.Fatal(err) + } + content := string(res) + + tree := parse.New(name, c.funcMap) + var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree) + tree, err = tree.Parse(content,"{{","}}", treeSet, c.funcMap) + if err != nil { + log.Fatal(err) + } + + if debug { + fmt.Println(name) + } + + out = "" + fname := strings.TrimSuffix(name, filepath.Ext(name)) + c.tlist = make(map[string]*parse.Tree) + c.tlist[fname] = tree + varholder := "tmpl_" + fname + "_vars" + + if debug { + fmt.Println(c.tlist) + } + + c.localVars = make(map[string]map[string]VarItemReflect) + c.localVars[fname] = make(map[string]VarItemReflect) + c.localVars[fname]["."] = VarItemReflect{".",varholder,holdreflect} + + subtree := c.tlist[fname] + if debug { + fmt.Println(subtree.Root) + } + + for _, node := range subtree.Root.Nodes { + if debug { + fmt.Println("Node: " + node.String()) + } + out += c.compile_switch(varholder, holdreflect, fname, node) + } + + var importList string + if c.doImports { + for _, item := range c.importMap { + importList += "import \"" + item + "\"\n" + } + } + + var varString string + for _, varItem := range c.varList { + varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n" + } + + out = "package main\n" + importList + "\nfunc init() {\nctemplates[\"" + fname + "\"] = template_" + fname + "\n}\n\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ") (tmpl string) {\n" + varString + out + "return tmpl\n}\n" + if debug { + fmt.Println("Output!") + fmt.Println(out) + } + return out +} + +func (c *CTemplateSet2) compile_switch(varholder string, holdreflect reflect.Value, template_name string, node interface{}) (out string) { + switch node := node.(type) { + case *parse.ActionNode: + if debug { + fmt.Println("Action Node") + } + + if node.Pipe == nil { + break + } + for _, cmd := range node.Pipe.Cmds { + out += c.compile_subswitch(varholder, holdreflect, template_name, cmd) + } + return out + case *parse.IfNode: + if debug { + fmt.Println("If Node: ") + fmt.Println(node.Pipe) + } + + var expr string + for _, cmd := range node.Pipe.Cmds { + if debug { + fmt.Println("If Node Bit: ") + fmt.Println(cmd) + fmt.Println(reflect.ValueOf(cmd).Type().Name()) + } + expr += c.compile_varswitch(varholder, holdreflect, template_name, cmd) + } + + if node.ElseList == nil { + if debug { + fmt.Println("Branch 1") + } + return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "}\n" + } else { + if debug { + fmt.Println("Branch 2") + } + return "if " + expr + " {\n" + c.compile_switch(varholder, holdreflect, template_name, node.List) + "} else {\n" + c.compile_switch(varholder, holdreflect, template_name, node.ElseList) + "}\n" + } + case *parse.ListNode: + if debug { + fmt.Println("List Node") + } + for _, subnode := range node.Nodes { + out += c.compile_switch(varholder, holdreflect, template_name, subnode) + } + return out + case *parse.RangeNode: + if debug { + fmt.Println("Range Node!") + fmt.Println(node.Pipe) + } + + var outVal reflect.Value + for _, cmd := range node.Pipe.Cmds { + if debug { + fmt.Println("Range Bit: ") + fmt.Println(cmd) + } + out, outVal = c.compile_reflectswitch(varholder, holdreflect, template_name, cmd) + } + + if debug { + fmt.Println("Returned: ") + fmt.Println(out) + fmt.Println("Range Kind Switch!") + } + + switch outVal.Kind() { + case reflect.Map: + var item reflect.Value + for _, key := range outVal.MapKeys() { + item = outVal.MapIndex(key) + } + + out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compile_switch("item", item, template_name, node.List) + "}\n}" + case reflect.Invalid: + return "" + } + + if node.ElseList != nil { + out += " else {\n" + c.compile_switch(varholder, holdreflect, template_name, node.ElseList) + "}\n" + } else { + out += "\n" + } + return out + case *parse.TemplateNode: + if debug { + fmt.Println("Template Node") + } + return c.compile_subtemplate(varholder, holdreflect, node) + case *parse.TextNode: + return "tmpl += `" + string(node.Text) + "`\n" + default: + panic("Unknown Node in main switch") + } + return "" +} + +func (c *CTemplateSet2) compile_subswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string) { + firstWord := node.Args[0] + switch n := firstWord.(type) { + case *parse.FieldNode: + if debug { + fmt.Println("Field Node: ") + fmt.Println(n.Ident) + } + + /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ + cur := holdreflect + + var varbit string + if cur.Kind() == reflect.Interface { + cur = cur.Elem() + varbit += ".(" + cur.Type().Name() + ")" + } + + for _, id := range n.Ident { + if debug { + fmt.Println("Data Kind: ") + fmt.Println(cur.Kind().String()) + fmt.Println("Field Bit: ") + fmt.Println(id) + } + + cur = cur.FieldByName(id) + if cur.Kind() == reflect.Interface { + cur = cur.Elem() + /*if cur.Kind() == reflect.String && cur.Type().Name() != "string" { + varbit = "string(" + varbit + "." + id + ")"*/ + //if cur.Kind() == reflect.String && cur.Type().Name() != "string" { + if cur.Type().PkgPath() != "main" { + c.importMap["html/template"] = "html/template" + varbit += "." + id + ".(" + strings.TrimPrefix(cur.Type().PkgPath(),"html/") + "." + cur.Type().Name() + ")" + } else { + varbit += "." + id + ".(" + cur.Type().Name() + ")" + } + } else { + varbit += "." + id + } + + if debug { + fmt.Println("End Cycle") + } + } + out = c.compile_varsub(varholder + varbit, cur) + + for _, varItem := range c.varList { + if strings.HasPrefix(out, varItem.Destination) { + out = strings.Replace(out, varItem.Destination, varItem.Name, 1) + } + } + return out + case *parse.DotNode: + if debug { + fmt.Println("Dot Node") + fmt.Println(node.String()) + } + return c.compile_varsub(varholder, holdreflect) + case *parse.NilNode: + panic("Nil is not a command x.x") + case *parse.VariableNode: + if debug { + fmt.Println("Variable Node") + fmt.Println(n.String()) + fmt.Println(n.Ident) + } + + out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) + return "tmpl += " + out + "\n" + case *parse.StringNode: + return n.Quoted + default: + fmt.Println("Unknown Kind: ") + fmt.Println(reflect.ValueOf(firstWord).Elem().Kind()) + fmt.Println("Unknown Type: ") + fmt.Println(reflect.ValueOf(firstWord).Elem().Type().Name()) + panic("I don't know what node this is") + } + return "" +} + +func (c *CTemplateSet2) compile_varswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string) { + firstWord := node.Args[0] + switch n := firstWord.(type) { + case *parse.FieldNode: + if debug { + fmt.Println("Field Node: ") + fmt.Println(n.Ident) + + for _, id := range n.Ident { + fmt.Println("Field Bit: ") + fmt.Println(id) + } + } + + /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ + return c.compile_boolsub(n.String(), varholder, template_name, holdreflect) + case *parse.ChainNode: + if debug { + fmt.Println("Chain Node: ") + fmt.Println(n.Node) + fmt.Println(node.Args) + } + break + case *parse.IdentifierNode: + if debug { + fmt.Println("Identifier Node: ") + fmt.Println(node) + fmt.Println(node.Args) + } + return c.compile_identswitch(varholder, holdreflect, template_name, node) + case *parse.DotNode: + return varholder + case *parse.VariableNode: + if debug { + fmt.Println("Variable Node") + fmt.Println(n.String()) + fmt.Println(n.Ident) + } + + out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) + return out + case *parse.NilNode: + panic("Nil is not a command x.x") + case *parse.PipeNode: + if debug { + fmt.Println("Pipe Node!") + fmt.Println(n) + } + + /*for _, cmd := range n.Cmds { + if debug { + fmt.Println("Pipe Bit: ") + fmt.Println(cmd) + } + out += c.compile_if_varsub_n(n.String(), varholder, template_name, holdreflect) + }*/ + + if debug { + fmt.Println("Args: ") + fmt.Println(node.Args) + } + + /*argcopy := node.Args[1:] + for _, arg := range argcopy { + if debug { + fmt.Println("Pipe Arg: ") + fmt.Println(arg) + fmt.Println(reflect.ValueOf(arg).Elem().Type().Name()) + fmt.Println(reflect.ValueOf(arg).Kind()) + } + + switch arg.(type) { + case *parse.IdentifierNode: + out += c.compile_identswitch(varholder, holdreflect, template_name, node) + break + case *parse.PipeNode: + break + //out += c.compile_if_varsub_n(a.String(), varholder, template_name, holdreflect) + default: + panic("Unknown Pipe Arg type! Did Mario get stuck in the pipes again?") + } + //out += c.compile_varswitch(arg.String(), holdreflect, template_name, arg) + }*/ + out += c.compile_identswitch(varholder, holdreflect, template_name, node) + + if debug { + fmt.Println("Out: ") + fmt.Println(out) + } + return out + default: + fmt.Println("Unknown Kind: ") + fmt.Println(reflect.ValueOf(firstWord).Elem().Kind()) + fmt.Println("Unknown Type: ") + fmt.Println(reflect.ValueOf(firstWord).Elem().Type().Name()) + panic("I don't know what node this is! Grr...") + } + return "" +} + +func (c *CTemplateSet2) compile_identswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string) { + ArgLoop: + for pos, id := range node.Args { + if debug { + fmt.Println(id) + } + + switch id.String() { + case "not": + out += "!" + case "or": + out += " || " + case "and": + out += " && " + case "le": + out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, template_name, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, template_name, holdreflect) + break ArgLoop + default: + if debug { + fmt.Println("Variable!") + } + out += c.compile_if_varsub_n(id.String(), varholder, template_name, holdreflect) + } + } + return out +} + +func (c *CTemplateSet2) compile_reflectswitch(varholder string, holdreflect reflect.Value, template_name string, node *parse.CommandNode) (out string, outVal reflect.Value) { + firstWord := node.Args[0] + switch n := firstWord.(type) { + case *parse.FieldNode: + if debug { + fmt.Println("Field Node: ") + fmt.Println(n.Ident) + + for _, id := range n.Ident { + fmt.Println("Field Bit: ") + fmt.Println(id) + } + } + /* Use reflect to determine if the field is for a method, otherwise assume it's a variable. Coming Soon. */ + return c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) + case *parse.ChainNode: + if debug { + fmt.Println("Chain Node: ") + fmt.Println(n.Node) + fmt.Println(node.Args) + } + return "", outVal + /*case *parse.IdentifierNode: + fmt.Println("Identifier Node: ") + fmt.Println(node) + fmt.Println(node.Args) + + ArgLoop: + for pos, id := range node.Args { + fmt.Println(id) + switch id.String() { + case "not": + out += "!" + case "or": + out += " || " + case "and": + out += " && " + case "le": + out += c.compile_if_varsub_n(node.Args[pos + 1].String(), varholder, holdreflect) + " <= " + c.compile_if_varsub_n(node.Args[pos + 2].String(), varholder, holdreflect) + break ArgLoop + default: + fmt.Println("Variable!") + out += c.compile_if_varsub_n(id.String(), varholder, holdreflect) + } + } + return out*/ + case *parse.DotNode: + return varholder, holdreflect + case *parse.NilNode: + panic("Nil is not a command x.x") + default: + //panic("I don't know what node this is") + } + return "", outVal +} + +func (c *CTemplateSet2) compile_if_varsub_n(varname string, varholder string, template_name string, cur reflect.Value) (out string) { + out, _ = c.compile_if_varsub(varname, varholder, template_name, cur) + return out +} + +func (c *CTemplateSet2) compile_if_varsub(varname string, varholder string, template_name string, cur reflect.Value) (out string, val reflect.Value) { + if varname[0] != '.' && varname[0] != '$' { + return varname, cur + } + + bits := strings.Split(varname,".") + if varname[0] == '$' { + var res VarItemReflect + if varname[1] == '.' { + res = c.localVars[template_name]["."] + } else { + res = c.localVars[template_name][strings.TrimPrefix(bits[0],"$")] + } + out += res.Destination + cur = res.Value + + if cur.Kind() == reflect.Interface { + cur = cur.Elem() + } + } else { + if cur.Kind() == reflect.Interface { + cur = cur.Elem() + out += varholder + ".(" + cur.Type().Name() + ")" + } else { + out += varholder + } + } + bits[0] = strings.TrimPrefix(bits[0],"$") + + if debug { + fmt.Println("Cur Kind: ") + fmt.Println(cur.Kind()) + fmt.Println("Cur Type: ") + fmt.Println(cur.Type().Name()) + } + + for _, bit := range bits { + if debug { + fmt.Println("Variable Field!") + fmt.Println(bit) + } + + if bit == "" { + continue + } + + cur = cur.FieldByName(bit) + if cur.Kind() == reflect.Interface { + cur = cur.Elem() + out += "." + bit + ".(" + cur.Type().Name() + ")" + } else { + out += "." + bit + } + + if debug { + fmt.Println("Data Kind: ") + fmt.Println(cur.Kind()) + fmt.Println("Data Type: ") + fmt.Println(cur.Type().Name()) + } + } + + for _, varItem := range c.varList { + if strings.HasPrefix(out, varItem.Destination) { + out = strings.Replace(out, varItem.Destination, varItem.Name, 1) + } + } + return out, cur +} + +func (c *CTemplateSet2) compile_boolsub(varname string, varholder string, template_name string, val reflect.Value) string { + out, val := c.compile_if_varsub(varname, varholder, template_name, val) + switch val.Kind() { + case reflect.Int: + out += " > 0" + case reflect.Bool: + // Do nothing + case reflect.String: + out += " != \"\"" + case reflect.Int64: + out += " > 0" + default: + panic("I don't know what this variable's type is o.o\n") + } + return out +} + +func (c *CTemplateSet2) compile_varsub(varname string, val reflect.Value) string { + for _, varItem := range c.varList { + if strings.HasPrefix(varname, varItem.Destination) { + varname = strings.Replace(varname, varItem.Destination, varItem.Name, 1) + } + } + + if val.Kind() == reflect.Interface { + val = val.Elem() + } + + switch val.Kind() { + case reflect.Int: + return "tmpl += strconv.Itoa(" + varname + ")\n" + case reflect.Bool: + return "if " + varname + " {\ntmpl += \"true\"} else {\ntmpl += \"false\"\n}\n" + case reflect.String: + if val.Type().Name() != "string" && !strings.HasPrefix(varname,"string(") { + return "tmpl += string(" + varname + ")\n" + } else { + return "tmpl += " + varname + "\n" + } + case reflect.Int64: + return "tmpl += strconv.FormatInt(" + varname + ", 10)" + default: + fmt.Println("Unknown Kind: ") + fmt.Println(val.Kind()) + fmt.Println("Unknown Type: ") + fmt.Println(val.Type().Name()) + panic("// I don't know what this variable's type is o.o\n") + } +} + +func (c *CTemplateSet2) compile_subtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) { + if debug { + fmt.Println("Template Node: " + node.Name) + } + + fname := strings.TrimSuffix(node.Name, filepath.Ext(node.Name)) + varholder := "tmpl_" + fname + "_vars" + var holdreflect reflect.Value + if node.Pipe != nil { + for _, cmd := range node.Pipe.Cmds { + firstWord := cmd.Args[0] + switch firstWord.(type) { + case *parse.DotNode: + varholder = pvarholder + holdreflect = pholdreflect + break + case *parse.NilNode: + panic("Nil is not a command x.x") + default: + out = "var " + varholder + " := false\n" + out += c.compile_command(cmd) + } + } + } + + res, err := ioutil.ReadFile(c.dir + node.Name) + if err != nil { + log.Fatal(err) + } + content := string(res) + + tree := parse.New(node.Name, c.funcMap) + var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree) + tree, err = tree.Parse(content,"{{","}}", treeSet, c.funcMap) + if err != nil { + log.Fatal(err) + } + + c.tlist[fname] = tree + subtree := c.tlist[fname] + if debug { + fmt.Println(subtree.Root) + } + + c.localVars[fname] = make(map[string]VarItemReflect) + c.localVars[fname]["."] = VarItemReflect{".",varholder,holdreflect} + + for _, node := range subtree.Root.Nodes { + if debug { + fmt.Println("Node: " + node.String()) + } + out += c.compile_switch(varholder, holdreflect, fname, node) + } + return out +} + +func (c *CTemplateSet2) compile_command(*parse.CommandNode) (out string) { + return "" +} \ No newline at end of file diff --git a/user.go b/user.go index 380332e5..4df2f3f4 100644 --- a/user.go +++ b/user.go @@ -10,6 +10,7 @@ type User struct { ID int Name string + Email string Group int Active bool Is_Mod bool @@ -72,6 +73,9 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, noticeList // Is this session valid..? err = get_session_stmt.QueryRow(user.ID,user.Session).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName) if err == sql.ErrNoRows { + user.ID = 0 + user.Session = "" + user.Perms = GuestPerms return user, noticeList, true } else if err != nil { InternalError(err,w,r,user) @@ -131,6 +135,9 @@ func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (user User, succ // Is this session valid..? err = get_session_stmt.QueryRow(user.ID,user.Session).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName) if err == sql.ErrNoRows { + user.ID = 0 + user.Session = "" + user.Perms = GuestPerms return user, true } else if err != nil { InternalError(err,w,r,user)