diff --git a/data.sql b/data.sql index 577cef3f..a5b8a076 100644 --- a/data.sql +++ b/data.sql @@ -99,8 +99,15 @@ CREATE TABLE `plugins`( unique(`uname`) ); +CREATE TABLE `themes`( + `uname` varchar(200) not null, + `default` tinyint DEFAULT 0 not null, + unique(`uname`) +); + 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 themes(`uname`,`default`) VALUES ('tempra-simple',1); INSERT INTO users(`name`,`email`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`,`message`) VALUES ('Admin','admin@localhost',1,1,NOW(),NOW(),''); @@ -118,6 +125,7 @@ EditUserGroupSuperMod EditUserGroupAdmin ManageForums EditSettings +ManageThemes ManagePlugins ViewIPs @@ -132,11 +140,11 @@ PinTopic CloseTopic */ -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 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,"ManageThemes":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,"ManageThemes":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,"ManageThemes":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,"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`,`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,"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`,`lastTopicTime`) VALUES ('General',NOW()); INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`) diff --git a/experimental/theme-ext.json b/experimental/theme-ext.json new file mode 100644 index 00000000..52eed859 --- /dev/null +++ b/experimental/theme-ext.json @@ -0,0 +1,19 @@ +{ + "Name": "tempra-simple", + "FriendlyName": "Tempra Simple", + "Version": "0.0.1", + "Creator": "Azareal", + "Settings": { + "PostLayout": { + "FriendlyName":"Post Layout", + "Options": ["Compact","Alternate"] + } + }, + "Templates": [ + { + "Name": "topic", + "Source": "topic_alt", + "When": "PostLayout=Alternate" + } + ] +} \ No newline at end of file diff --git a/experimental/theme-ext.xml b/experimental/theme-ext.xml new file mode 100644 index 00000000..fd81450a --- /dev/null +++ b/experimental/theme-ext.xml @@ -0,0 +1,18 @@ + + + tempra-simple + Tempra Simple + 0.0.1 + Azareal + + PostLayout + Post Layout + + + + + + + + + \ No newline at end of file diff --git a/extend.go b/extend.go index 6d3f6030..c248324a 100644 --- a/extend.go +++ b/extend.go @@ -1,5 +1,6 @@ /* Copyright Azareal 2016 - 2017 */ package main +import "log" var plugins map[string]Plugin = make(map[string]Plugin) var hooks map[string]func(interface{})interface{} = make(map[string]func(interface{})interface{}) @@ -24,6 +25,16 @@ type Plugin struct hooks[name] = handler }*/ +func init_plugins() { + for name, body := range plugins { + log.Print("Added plugin " + name) + if body.Active { + log.Print("Initialised plugin " + name) + plugins[name].Init() + } + } +} + func add_hook(name string, handler interface{}) { switch h := handler.(type) { case func(interface{})interface{}: diff --git a/forum.go b/forum.go index 0913b48d..45d56670 100644 --- a/forum.go +++ b/forum.go @@ -17,4 +17,4 @@ type ForumSimple struct ID int Name string Active bool -} \ No newline at end of file +} diff --git a/general_test.go b/general_test.go index e138d4e5..d0485e3a 100644 --- a/general_test.go +++ b/general_test.go @@ -31,8 +31,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,"","","",""}) replyList = append(replyList, Reply{0,0,"Hey everyone!",template.HTML("Hey everyone!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) - tpage := TopicPage{"Topic Blah","topic",user,noticeList,replyList,topic,false} - tpage2 := TopicPage{"Topic Blah","topic",admin,noticeList,replyList,topic,false} + tpage := TopicPage{"Topic Blah",user,noticeList,replyList,topic,false} + tpage2 := TopicPage{"Topic Blah",admin,noticeList,replyList,topic,false} w := ioutil.Discard b.Run("compiled_useradmin", func(b *testing.B) { @@ -65,7 +65,7 @@ func BenchmarkTopicsTemplate(b *testing.B) { var noticeList map[int]string = make(map[int]string) noticeList[0] = "test" - var topicList []interface{} + var topicList []TopicUser 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","","",""}) @@ -77,8 +77,8 @@ func BenchmarkTopicsTemplate(b *testing.B) { 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} + tpage := TopicsPage{"Topic Blah",user,noticeList,topicList,0} + tpage2 := TopicsPage{"Topic Blah",admin,noticeList,topicList,0} w := ioutil.Discard b.Run("compiled_useradmin", func(b *testing.B) { @@ -138,6 +138,10 @@ func BenchmarkRoute(b *testing.B) { forums_req_admin.AddCookie(&admin_session_cookie) forums_handler := http.HandlerFunc(route_forums) + static_w := httptest.NewRecorder() + static_req := httptest.NewRequest("get","/static/global.js",bytes.NewReader(nil)) + static_handler := http.HandlerFunc(route_static) + debug = false nogrouplog = true @@ -146,16 +150,18 @@ func BenchmarkRoute(b *testing.B) { log.SetOutput(discard) var err error - init_database(err); + init_database(err) external_sites["YT"] = "https://www.youtube.com/" hooks["trow_assign"] = nil hooks["rrow_assign"] = nil + init_plugins() - for name, body := range plugins { - if body.Active { - plugins[name].Init() + b.Run("static_files", func(b *testing.B) { + for i := 0; i < b.N; i++ { + static_w.Body.Reset() + static_handler.ServeHTTP(static_w,static_req) } - } + }) b.Run("topic_admin", func(b *testing.B) { for i := 0; i < b.N; i++ { diff --git a/gosora.exe b/gosora.exe index 82e5dbbd..1860abde 100644 Binary files a/gosora.exe and b/gosora.exe differ diff --git a/group.go b/group.go index 155f39e7..8cc683ec 100644 --- a/group.go +++ b/group.go @@ -30,6 +30,7 @@ type Perms struct EditUserGroupAdmin bool ManageForums bool // This could be local, albeit limited for per-forum managers EditSettings bool + ManageThemes bool ManagePlugins bool ViewIPs bool @@ -86,6 +87,7 @@ func init() { EditUserGroupAdmin: true, ManageForums: true, EditSettings: true, + ManageThemes: true, ManagePlugins: true, ViewIPs: true, diff --git a/images/bench_round3.PNG b/images/bench_round3.PNG new file mode 100644 index 00000000..4ef0093b Binary files /dev/null and b/images/bench_round3.PNG differ diff --git a/images/bench_round3_staticfileimprovements.PNG b/images/bench_round3_staticfileimprovements.PNG new file mode 100644 index 00000000..62b415fc Binary files /dev/null and b/images/bench_round3_staticfileimprovements.PNG differ diff --git a/images/layout2-gradients-test.PNG b/images/layout2-gradients-test.PNG new file mode 100644 index 00000000..883719b9 Binary files /dev/null and b/images/layout2-gradients-test.PNG differ diff --git a/images/layout2-gradients-test2.PNG b/images/layout2-gradients-test2.PNG new file mode 100644 index 00000000..620dd871 Binary files /dev/null and b/images/layout2-gradients-test2.PNG differ diff --git a/images/panel-theme-list.PNG b/images/panel-theme-list.PNG new file mode 100644 index 00000000..8b8ab873 Binary files /dev/null and b/images/panel-theme-list.PNG differ diff --git a/main.go b/main.go index 1c694290..1dc6cc73 100644 --- a/main.go +++ b/main.go @@ -31,52 +31,54 @@ 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 static_files map[string]SFile = make(map[string]SFile) -var ctemplates []string + var template_topic_handle func(TopicPage,io.Writer) = nil +var template_topic_origin_handle func(TopicPage,io.Writer) = nil var template_topic_alt_handle func(TopicPage,io.Writer) = nil -var template_topics_handle func(Page,io.Writer) = nil -var template_forum_handle func(Page,io.Writer) = nil -var template_forums_handle func(Page,io.Writer) = nil +var template_topics_handle func(TopicsPage,io.Writer) = nil +var template_forum_handle func(ForumPage,io.Writer) = nil +var template_forums_handle func(ForumsPage,io.Writer) = nil var template_profile_handle func(ProfilePage,io.Writer) = nil func compile_templates() { var c CTemplateSet - user := User{0,"","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} + user := User{62,"","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,"",false,"","","","",""} var noticeList map[int]string = make(map[int]string) noticeList[0] = "test" log.Print("Compiling the templates") - topic := TopicUser{0,"",template.HTML(""),0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""} + topic := TopicUser{1,"Blah",template.HTML("Hey there!"),0,false,false,"",0,"","","",no_css_tmpl,0,"","","",""} var replyList []Reply - replyList = append(replyList, Reply{0,0,"",template.HTML(""),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) + replyList = append(replyList, Reply{0,0,"",template.HTML("Yo!"),0,"","",0,0,"",no_css_tmpl,0,"","","",""}) var varList map[string]VarItem = make(map[string]VarItem) - tpage := TopicPage{"Title","name",user,noticeList,replyList,topic,false} + tpage := TopicPage{"Title",user,noticeList,replyList,topic,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) varList = make(map[string]VarItem) - ppage := ProfilePage{"Title",user,noticeList,replyList,user,false} + ppage := ProfilePage{"User 526",user,noticeList,replyList,user,false} profile_tmpl := c.compile_template("profile.html","templates/","ProfilePage", ppage, varList) - var forumList []interface{} + var forumList []Forum for _, forum := range forums { if forum.Active { forumList = append(forumList, forum) } } varList = make(map[string]VarItem) - pi := Page{"Forum List","forums",user,noticeList,forumList,0} - forums_tmpl := c.compile_template("forums.html","templates/","Page", pi, varList) + forums_page := ForumsPage{"Forum List",user,noticeList,forumList,0} + forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage", forums_page, varList) - var topicList []interface{} + var topicList []TopicUser topicList = append(topicList, TopicUser{1,"Topic Title","The topic content.",1,false,false,"",1,"open","Admin","","",0,"","","",""}) - pi = Page{"Topic List","topics",user,noticeList,topicList,""} - topics_tmpl := c.compile_template("topics.html","templates/","Page", pi, varList) + topics_page := TopicsPage{"Topic List",user,noticeList,topicList,""} + topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) + //topics_tmpl := c.compile_template("topics.html","templates/","Page", pi, varList) - pi = Page{"General Forum","forum",user,noticeList,topicList,"There aren't any topics in this forum yet."} - forum_tmpl := c.compile_template("forum.html","templates/","Page", pi, varList) + forum_page := ForumPage{"General Forum",user,noticeList,topicList,"There aren't any topics in this forum yet."} + forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList) log.Print("Writing the templates") write_template("topic", topic_id_tmpl) @@ -102,8 +104,9 @@ func write_template(name string, content string) { } func main(){ + init_themes() var err error - init_database(err); + init_database(err) compile_templates() log.Print("Loading the static files.") @@ -132,19 +135,11 @@ func main(){ hooks["rrow_assign"] = nil templates.ParseGlob("pages/*") - for name, body := range plugins { - log.Print("Added plugin " + name) - if body.Active { - log.Print("Initialised plugin " + name) - plugins[name].Init() - } - } + init_plugins() // In a directory to stop it clashing with the other paths http.HandleFunc("/static/", route_static) - //http.HandleFunc("/static/", route_fstatic) - //fs_p := http.FileServer(http.Dir("./public")) - //http.Handle("/static/", http.StripPrefix("/static/",fs_p)) + fs_u := http.FileServer(http.Dir("./uploads")) http.Handle("/uploads/", http.StripPrefix("/uploads/",fs_u)) @@ -205,6 +200,8 @@ func main(){ http.HandleFunc("/panel/settings/", route_panel_settings) http.HandleFunc("/panel/settings/edit/", route_panel_setting) http.HandleFunc("/panel/settings/edit/submit/", route_panel_setting_edit) + http.HandleFunc("/panel/themes/", route_panel_themes) + http.HandleFunc("/panel/themes/default/", route_panel_themes_default) http.HandleFunc("/panel/plugins/", route_panel_plugins) http.HandleFunc("/panel/plugins/activate/", route_panel_plugins_activate) http.HandleFunc("/panel/plugins/deactivate/", route_panel_plugins_deactivate) diff --git a/mod_routes.go b/mod_routes.go index a7855614..34ed7ddf 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -42,8 +42,6 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) { var is_closed bool if topic_status == "closed" { is_closed = true - } else { - is_closed = false } topic_content := html.EscapeString(r.PostFormValue("topic_content")) @@ -910,7 +908,6 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request){ NoPermissions(w,r,user) return } - if r.FormValue("session") != user.Session { SecurityError(w,r,user) return @@ -1243,3 +1240,95 @@ func route_panel_groups(w http.ResponseWriter, r *http.Request){ pi := Page{"Group Manager","panel-groups",user,noticeList,groupList,0} templates.ExecuteTemplate(w,"panel-groups.html", pi) } + +func route_panel_themes(w http.ResponseWriter, r *http.Request){ + user, noticeList, ok := SessionCheck(w,r) + if !ok { + return + } + if !user.Is_Super_Mod || !user.Perms.ManageThemes { + NoPermissions(w,r,user) + return + } + + var themeList []interface{} + for _, theme := range themes { + themeList = append(themeList, theme) + } + + pi := Page{"Theme Manager","panel-themes",user,noticeList,themeList,0} + templates.ExecuteTemplate(w,"panel-themes.html", pi) +} + +func route_panel_themes_default(w http.ResponseWriter, r *http.Request){ + user, ok := SimpleSessionCheck(w,r) + if !ok { + return + } + if !user.Is_Super_Mod || !user.Perms.ManageThemes { + NoPermissions(w,r,user) + return + } + if r.FormValue("session") != user.Session { + SecurityError(w,r,user) + return + } + + uname := r.URL.Path[len("/panel/themes/default/"):] + theme, ok := themes[uname] + if !ok { + LocalError("The theme isn't registered in the system",w,r,user) + return + } + + var isDefault bool + err := db.QueryRow("SELECT `default` from `themes` where `uname` = ?", uname).Scan(&isDefault) + if err != nil && err != sql.ErrNoRows { + InternalError(err,w,r,user) + return + } + + has_theme := err != sql.ErrNoRows + if has_theme { + if isDefault { + LocalError("The theme is already active",w,r,user) + return + } + _, err = update_theme_stmt.Exec(1, uname) + if err != nil { + InternalError(err,w,r,user) + return + } + } else { + _, err := add_theme_stmt.Exec(uname,1) + if err != nil { + InternalError(err,w,r,user) + return + } + } + + _, err = update_theme_stmt.Exec(0, defaultTheme) + if err != nil { + InternalError(err,w,r,user) + return + } + + log.Print("Setting theme '" + theme.Name + "' as the default theme") + theme.Active = true + themes[uname] = theme + + dTheme, ok := themes[defaultTheme] + if !ok { + log.Fatal("The default theme is missing") + return + } + dTheme.Active = false + themes[defaultTheme] = dTheme + + defaultTheme = uname + reset_template_overrides() + add_theme_static_files(uname) + map_theme_templates(theme) + + http.Redirect(w,r,"/panel/themes/",http.StatusSeeOther) +} diff --git a/mysql.go b/mysql.go index b0e98394..dede2a45 100644 --- a/mysql.go +++ b/mysql.go @@ -42,6 +42,8 @@ var update_setting_stmt *sql.Stmt var add_plugin_stmt *sql.Stmt var update_plugin_stmt *sql.Stmt var update_user_stmt *sql.Stmt +var add_theme_stmt *sql.Stmt +var update_theme_stmt *sql.Stmt func init_database(err error) { if(dbpassword != ""){ @@ -253,6 +255,18 @@ func init_database(err error) { log.Fatal(err) } + log.Print("Preparing add_theme statement.") + add_theme_stmt, err = db.Prepare("INSERT INTO `themes`(`uname`,`default`) VALUES(?,?)") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing update_theme statement.") + update_theme_stmt, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?") + if err != nil { + 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 { @@ -381,4 +395,42 @@ func init_database(err error) { if err != nil { log.Fatal(err) } + + log.Print("Loading the themes.") + rows, err = db.Query("SELECT `uname`, `default` FROM `themes`") + if err != nil { + log.Fatal(err) + } + defer rows.Close() + + var defaultThemeSwitch bool + for rows.Next() { + err := rows.Scan(&uname, &defaultThemeSwitch) + if err != nil { + log.Fatal(err) + } + + // Was the theme deleted at some point? + theme, ok := themes[uname] + if !ok { + continue + } + + if defaultThemeSwitch { + log.Print("Loading the theme '" + theme.Name + "'") + theme.Active = true + defaultTheme = uname + add_theme_static_files(uname) + map_theme_templates(theme) + } else { + theme.Active = false + } + themes[uname] = theme + } + err = rows.Err() + if err != nil { + log.Fatal(err) + } + + } diff --git a/pages.go b/pages.go index 2399ea92..54012b41 100644 --- a/pages.go +++ b/pages.go @@ -15,7 +15,6 @@ type Page struct type TopicPage struct { Title string - Name string CurrentUser User NoticeList map[int]string ItemList []Reply @@ -23,6 +22,33 @@ type TopicPage struct ExtData interface{} } +type TopicsPage struct +{ + Title string + CurrentUser User + NoticeList map[int]string + ItemList []TopicUser + ExtData interface{} +} + +type ForumPage struct +{ + Title string + CurrentUser User + NoticeList map[int]string + ItemList []TopicUser + ExtData interface{} +} + +type ForumsPage struct +{ + Title string + CurrentUser User + NoticeList map[int]string + ItemList []Forum + ExtData interface{} +} + type ProfilePage struct { Title string @@ -36,7 +62,6 @@ type ProfilePage struct type PageSimple struct { Title string - Name string Something interface{} } diff --git a/public/fabric-base-simple-alpha.png b/public/fabric-base-simple-alpha.png new file mode 100644 index 00000000..7a102a54 Binary files /dev/null and b/public/fabric-base-simple-alpha.png differ diff --git a/routes.go b/routes.go index 04c34962..68fa0a25 100644 --- a/routes.go +++ b/routes.go @@ -24,18 +24,27 @@ var nList map[int]string // GET functions func route_static(w http.ResponseWriter, r *http.Request){ //name := r.URL.Path[len("/static/"):] - if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && static_files[r.URL.Path].Info.ModTime().Before(t.Add(1*time.Second)) { + //log.Print("Outputting static file '" + r.URL.Path + "'") + + file, ok := static_files[r.URL.Path] + if !ok { + w.WriteHeader(http.StatusNotFound) + return + } + + // Surely, there's a more efficient way of doing this? + if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && file.Info.ModTime().Before(t.Add(1 * time.Second)) { w.WriteHeader(http.StatusNotModified) return } h := w.Header() - h.Set("Last-Modified", static_files[r.URL.Path].FormattedModTime) - h.Set("Content-Type", static_files[r.URL.Path].Mimetype) - h.Set("Content-Length", strconv.FormatInt(static_files[r.URL.Path].Length, 10)) - //http.ServeContent(w,r,r.URL.Path,static_files[r.URL.Path].Info.ModTime(),static_files[r.URL.Path]) - //w.Write(static_files[r.URL.Path].Data) - io.Copy(w, bytes.NewReader(static_files[r.URL.Path].Data)) - //io.CopyN(w, bytes.NewReader(static_files[r.URL.Path].Data), static_files[r.URL.Path].Length) + h.Set("Last-Modified", file.FormattedModTime) + h.Set("Content-Type", file.Mimetype) + h.Set("Content-Length", strconv.FormatInt(file.Length, 10)) // Avoid doing a type conversion every time? + //http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file) + //w.Write(file.Data) + io.Copy(w, bytes.NewReader(file.Data)) // Use w.Write instead? + //io.CopyN(w, bytes.NewReader(file.Data), static_files[r.URL.Path].Length) } func route_fstatic(w http.ResponseWriter, r *http.Request){ @@ -83,21 +92,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){ return }*/ - var( - topicList []interface{} - tid int - title string - content string - createdBy int - is_closed bool - sticky bool - createdAt string - parentID int - status string - name string - avatar string - ) - + var topicList []TopicUser rows, err := get_topic_list_stmt.Query() if err != nil { InternalError(err,w,r,user) @@ -105,27 +100,27 @@ func route_topics(w http.ResponseWriter, r *http.Request){ } defer rows.Close() + topicItem := TopicUser{ID: 0,} for rows.Next() { - err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar) + err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.ParentID, &topicItem.CreatedByName, &topicItem.Avatar) if err != nil { InternalError(err,w,r,user) return } - if is_closed { - status = "closed" + if topicItem.Is_Closed { + topicItem.Status = "shut" } else { - status = "open" + topicItem.Status = "open" } - if avatar != "" { - if avatar[0] == '.' { - avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar + if topicItem.Avatar != "" { + if topicItem.Avatar[0] == '.' { + topicItem.Avatar = "/uploads/avatar_" + strconv.Itoa(topicItem.CreatedBy) + topicItem.Avatar } } else { - avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1) + topicItem.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topicItem.CreatedBy),1) } - topicItem := TopicUser{tid,title,content,createdBy,is_closed,sticky, createdAt,parentID,status,name,avatar,"",0,"","","",""} if hooks["trow_assign"] != nil { topicItem = run_hook("trow_assign", topicItem).(TopicUser) } @@ -137,7 +132,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){ return } - pi := Page{"Topic List","topics",user,noticeList,topicList,0} + pi := TopicsPage{"Topic List",user,noticeList,topicList,0} if template_topics_handle != nil { template_topics_handle(pi,w) } else { @@ -154,20 +149,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){ return } - var( - topicList []interface{} - tid int - title string - content string - createdBy int - is_closed bool - sticky bool - createdAt string - parentID int - status string - name string - avatar string - ) + var topicList []TopicUser fid, err := strconv.Atoi(r.URL.Path[len("/forum/"):]) if err != nil { @@ -192,27 +174,27 @@ func route_forum(w http.ResponseWriter, r *http.Request){ } defer rows.Close() + topicItem := TopicUser{ID: 0} for rows.Next() { - err := rows.Scan(&tid, &title, &content, &createdBy, &is_closed, &sticky, &createdAt, &parentID, &name, &avatar) + err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.ParentID, &topicItem.CreatedByName, &topicItem.Avatar) if err != nil { InternalError(err,w,r,user) return } - if is_closed { - status = "closed" + if topicItem.Is_Closed { + topicItem.Status = "shut" } else { - status = "open" + topicItem.Status = "open" } - if avatar != "" { - if avatar[0] == '.' { - avatar = "/uploads/avatar_" + strconv.Itoa(createdBy) + avatar + if topicItem.Avatar != "" { + if topicItem.Avatar[0] == '.' { + topicItem.Avatar = "/uploads/avatar_" + strconv.Itoa(topicItem.CreatedBy) + topicItem.Avatar } } else { - avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(createdBy),1) + topicItem.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topicItem.CreatedBy),1) } - topicItem := TopicUser{tid,title,content,createdBy,is_closed,sticky,createdAt,parentID,status,name,avatar,"",0,"","","",""} if hooks["trow_assign"] != nil { topicItem = run_hook("trow_assign", topicItem).(TopicUser) } @@ -224,7 +206,7 @@ func route_forum(w http.ResponseWriter, r *http.Request){ return } - pi := Page{forums[fid].Name,"forum",user,noticeList,topicList,0} + pi := ForumPage{forums[fid].Name,user,noticeList,topicList,0} if template_forum_handle != nil { template_forum_handle(pi,w) } else { @@ -241,14 +223,14 @@ func route_forums(w http.ResponseWriter, r *http.Request){ return } - var forumList []interface{} + var forumList []Forum for _, forum := range forums { if forum.Active { forumList = append(forumList, forum) } } - pi := Page{"Forum List","forums",user,noticeList,forumList,0} + pi := ForumsPage{"Forum List",user,noticeList,forumList,0} if template_forums_handle != nil { template_forums_handle(pi,w) } else { @@ -315,7 +297,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ topic.ContentLines = strings.Count(content,"\n") if topic.Is_Closed { - topic.Status = "closed" + topic.Status = "shut" // We don't want users posting in locked topics... if !user.Is_Mod { @@ -407,9 +389,9 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){ return } - tpage := TopicPage{topic.Title,"topic",user,noticeList,replyList,topic,0} - if template_topic_handle != nil { //if template_topic_alt_handle != nil { - template_topic_handle(tpage,w) //template_topic_alt_handle(tpage,w) + tpage := TopicPage{topic.Title,user,noticeList,replyList,topic,0} + if template_topic_handle != nil { + template_topic_handle(tpage,w) } else { err = templates.ExecuteTemplate(w,"topic.html", tpage) if err != nil { diff --git a/template_forum.go b/template_forum.go index d734569d..58b2b2a7 100644 --- a/template_forum.go +++ b/template_forum.go @@ -1,12 +1,17 @@ +/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main import "io" import "strconv" func init() { -template_forum_handle = template_forum + template_forum_handle = template_forum + //o_template_forum_handle = template_forum + ctemplates = append(ctemplates,"forum") + tmpl_ptr_map["forum"] = &template_forum_handle + tmpl_ptr_map["o_forum"] = template_forum } -func template_forum(tmpl_forum_vars Page, w io.Writer) { +func template_forum(tmpl_forum_vars ForumPage, w io.Writer) { w.Write([]byte(` @@ -68,20 +73,20 @@ w.Write([]byte(` if len(tmpl_forum_vars.ItemList) != 0 { for _, item := range tmpl_forum_vars.ItemList { w.Write([]byte(`
- ` + item.(TopicUser).Title + ` `)) -if item.(TopicUser).Is_Closed { -w.Write([]byte(`closed + ` + item.Title + ` `)) +if item.Is_Closed { +w.Write([]byte(`shut `)) } else { w.Write([]byte(`open`)) diff --git a/template_forums.go b/template_forums.go index 6b1ccd73..09e1a3fb 100644 --- a/template_forums.go +++ b/template_forums.go @@ -1,12 +1,17 @@ +/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main import "io" import "strconv" func init() { -template_forums_handle = template_forums + template_forums_handle = template_forums + //o_template_forums_handle = template_forums + ctemplates = append(ctemplates,"forums") + tmpl_ptr_map["forums"] = &template_forums_handle + tmpl_ptr_map["o_forums"] = template_forums } -func template_forums(tmpl_forums_vars Page, w io.Writer) { +func template_forums(tmpl_forums_vars ForumsPage, w io.Writer) { w.Write([]byte(` @@ -65,8 +70,8 @@ w.Write([]byte(` if len(tmpl_forums_vars.ItemList) != 0 { for _, item := range tmpl_forums_vars.ItemList { w.Write([]byte(`
- ` + item.(Forum).Name + ` - ` + item.(Forum).LastTopic + ` ` + item.(Forum).LastTopicTime + ` + ` + item.Name + ` + ` + item.LastTopic + ` ` + item.LastTopicTime + `
`)) } diff --git a/template_profile.go b/template_profile.go index 5fecd56e..fce15a79 100644 --- a/template_profile.go +++ b/template_profile.go @@ -1,9 +1,14 @@ +/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main -import "strconv" import "io" +import "strconv" func init() { -template_profile_handle = template_profile + template_profile_handle = template_profile + //o_template_profile_handle = template_profile + ctemplates = append(ctemplates,"profile") + tmpl_ptr_map["profile"] = &template_profile_handle + tmpl_ptr_map["o_profile"] = template_profile } func template_profile(tmpl_profile_vars ProfilePage, w io.Writer) { diff --git a/template_topic.go b/template_topic.go index 105852ef..27b47526 100644 --- a/template_topic.go +++ b/template_topic.go @@ -1,10 +1,15 @@ +/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main import "io" import "strconv" import "html/template" func init() { -template_topic_handle = template_topic + template_topic_handle = template_topic + //o_template_topic_handle = template_topic + ctemplates = append(ctemplates,"topic") + tmpl_ptr_map["topic"] = &template_topic_handle + tmpl_ptr_map["o_topic"] = template_topic } func template_topic(tmpl_topic_vars TopicPage, w io.Writer) { @@ -91,7 +96,7 @@ w.Write([]byte(` `)) diff --git a/template_topic_alt.go b/template_topic_alt.go index 0f7f953a..20b7149b 100644 --- a/template_topic_alt.go +++ b/template_topic_alt.go @@ -1,10 +1,15 @@ +/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main -import "strconv" import "html/template" import "io" +import "strconv" func init() { -template_topic_alt_handle = template_topic_alt + template_topic_alt_handle = template_topic_alt + //o_template_topic_alt_handle = template_topic_alt + ctemplates = append(ctemplates,"topic_alt") + tmpl_ptr_map["topic_alt"] = &template_topic_alt_handle + tmpl_ptr_map["o_topic_alt"] = template_topic_alt } func template_topic_alt(tmpl_topic_alt_vars TopicPage, w io.Writer) { @@ -63,15 +68,17 @@ w.Write([]byte(`
` + item + `
`)) w.Write([]byte(`
-
+w.Write([]byte(`"> ` + tmpl_topic_alt_vars.Topic.Title + ` ` + tmpl_topic_alt_vars.Topic.Status + ` Status @@ -91,7 +98,7 @@ w.Write([]byte(` `)) @@ -104,7 +111,7 @@ w.Write([]byte(`
-
+
 
` + tmpl_topic_alt_vars.Topic.CreatedByName + `
@@ -118,7 +125,7 @@ if len(tmpl_topic_alt_vars.ItemList) != 0 { for _, item := range tmpl_topic_alt_vars.ItemList { w.Write([]byte(`
-
+
 
` + item.CreatedByName + `
@@ -146,7 +153,7 @@ w.Write([]byte(`
`)) if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply { w.Write([]byte(` -
+
diff --git a/template_topics.go b/template_topics.go index 50bd7d88..50989ce0 100644 --- a/template_topics.go +++ b/template_topics.go @@ -1,12 +1,17 @@ +/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main import "io" import "strconv" func init() { -template_topics_handle = template_topics + template_topics_handle = template_topics + //o_template_topics_handle = template_topics + ctemplates = append(ctemplates,"topics") + tmpl_ptr_map["topics"] = &template_topics_handle + tmpl_ptr_map["o_topics"] = template_topics } -func template_topics(tmpl_topics_vars Page, w io.Writer) { +func template_topics(tmpl_topics_vars TopicsPage, w io.Writer) { w.Write([]byte(` @@ -68,20 +73,20 @@ w.Write([]byte(` if len(tmpl_topics_vars.ItemList) != 0 { for _, item := range tmpl_topics_vars.ItemList { w.Write([]byte(`
- ` + item.(TopicUser).Title + ` `)) -if item.(TopicUser).Is_Closed { -w.Write([]byte(`closed + ` + item.Title + ` `)) +if item.Is_Closed { +w.Write([]byte(`shut `)) } else { w.Write([]byte(`open`)) diff --git a/templates.go b/templates.go index bb617e98..05419862 100644 --- a/templates.go +++ b/templates.go @@ -8,6 +8,9 @@ import "path/filepath" import "io/ioutil" import "text/template/parse" +var ctemplates []string +var tmpl_ptr_map map[string]interface{} = make(map[string]interface{}) + type VarItem struct { Name string @@ -76,7 +79,6 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string, if err != nil { log.Fatal(err) } - if debug { fmt.Println(name) } @@ -90,7 +92,6 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string, 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} @@ -126,11 +127,14 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string, varString += "var " + varItem.Name + " " + varItem.Type + " = " + varItem.Destination + "\n" } - out = "package main\n" + importList + c.pVarList + "\nfunc init() {\ntemplate_" + fname +"_handle = template_" + fname + "\n}\n\nfunc template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w io.Writer) {\n" + varString + out + "}\n" + fout := "/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n" + fout += "package main\n" + importList + c.pVarList + "\n" + fout += "func init() {\n\ttemplate_" + fname +"_handle = template_" + fname + "\n\t//o_template_" + fname +"_handle = template_" + fname + "\n\tctemplates = append(ctemplates,\"" + fname + "\")\n\ttmpl_ptr_map[\"" + fname + "\"] = &template_" + fname + "_handle\n\ttmpl_ptr_map[\"o_" + fname + "\"] = template_" + fname + "\n}\n\n" + fout += "func template_" + fname + "(tmpl_" + fname + "_vars " + expects + ", w io.Writer) {\n" + varString + out + "}\n" - out = strings.Replace(out,`)) + fout = strings.Replace(fout,`)) w.Write([]byte(`," + ",-1) - out = strings.Replace(out,"` + `","",-1) + fout = strings.Replace(fout,"` + `","",-1) for index, count := range c.stats { fmt.Println(index + ": " + strconv.Itoa(count)) @@ -139,9 +143,9 @@ w.Write([]byte(`," + ",-1) if debug { fmt.Println("Output!") - fmt.Println(out) + fmt.Println(fout) } - return out + return fout } func (c *CTemplateSet) compile_switch(varholder string, holdreflect reflect.Value, template_name string, node interface{}) (out string) { @@ -380,7 +384,6 @@ func (c *CTemplateSet) compile_varswitch(varholder string, holdreflect reflect.V fmt.Println(n.String()) fmt.Println(n.Ident) } - out, _ = c.compile_if_varsub(n.String(), varholder, template_name, holdreflect) return out case *parse.NilNode: diff --git a/templates/forum.html b/templates/forum.html index 619641c3..d5d0eccb 100644 --- a/templates/forum.html +++ b/templates/forum.html @@ -4,7 +4,7 @@
{{range .ItemList}}
- {{.Title}} {{if .Is_Closed}}closed + {{.Title}} {{if .Is_Closed}}shut {{else}}open{{end}} Status
diff --git a/templates/panel-menu.html b/templates/panel-menu.html index 4a0e5e68..68a70260 100644 --- a/templates/panel-menu.html +++ b/templates/panel-menu.html @@ -4,9 +4,7 @@ {{if .CurrentUser.Perms.ManageForums}}{{end}} {{if .CurrentUser.Perms.EditSettings}}{{end}} + {{if .CurrentUser.Perms.ManageThemes}}{{end}} {{if .CurrentUser.Perms.ManagePlugins}}{{end}} - - -
\ No newline at end of file diff --git a/templates/panel-themes.html b/templates/panel-themes.html new file mode 100644 index 00000000..13460078 --- /dev/null +++ b/templates/panel-themes.html @@ -0,0 +1,20 @@ +{{template "header.html" . }} +{{template "panel-menu.html" . }} + +
+ +
+
+ {{range .ItemList}} +
+ + {{.FriendlyName}}
+ Author: {{.Creator}} +
+ + {{if .Active}}Default{{else}}Make Default{{end}} + +
+ {{end}} +
+{{template "footer.html" . }} \ No newline at end of file diff --git a/templates/topic.html b/templates/topic.html index 562ed4e1..8b144dac 100644 --- a/templates/topic.html +++ b/templates/topic.html @@ -13,7 +13,7 @@ {{end}} diff --git a/templates/topic_alt.html b/templates/topic_alt.html index e099ae42..a77b868a 100644 --- a/templates/topic_alt.html +++ b/templates/topic_alt.html @@ -1,7 +1,7 @@ {{template "header.html" . }}
-
+
{{.Topic.Title}} {{.Topic.Status}} Status @@ -13,7 +13,7 @@ {{end}} @@ -24,7 +24,7 @@
-
+
 
{{.Topic.CreatedByName}}
@@ -35,7 +35,7 @@
{{range .ItemList}}
-
+
 
{{.CreatedByName}}
@@ -50,7 +50,7 @@
{{end}}
{{if .CurrentUser.Perms.CreateReply}} -
+
diff --git a/templates/topics.html b/templates/topics.html index b5bdea20..bc1f36cb 100644 --- a/templates/topics.html +++ b/templates/topics.html @@ -4,7 +4,7 @@
{{range .ItemList}}
- {{.Title}} {{if .Is_Closed}}closed + {{.Title}} {{if .Is_Closed}}shut {{else}}open{{end}} Status
diff --git a/themes.go b/themes.go new file mode 100644 index 00000000..91a0b335 --- /dev/null +++ b/themes.go @@ -0,0 +1,319 @@ +/* Copyright Azareal 2016 - 2017 */ +package main + +import "fmt" +import "log" +import "io" +import "os" +import "strings" +import "mime" +import "io/ioutil" +import "path/filepath" +import "encoding/json" +import "net/http" + +var defaultTheme string +var themes map[string]Theme = make(map[string]Theme) +//var overriden_templates map[string]interface{} = make(map[string]interface{}) +var overriden_templates map[string]bool = make(map[string]bool) + +type Theme struct +{ + Name string + FriendlyName string + Version string + Creator string + Settings map[string]ThemeSetting + Templates []TemplateMapping + + // This variable should only be set and unset by the system, not the theme meta file + Active bool +} + +type ThemeSetting struct +{ + FriendlyName string + Options []string +} + +type TemplateMapping struct +{ + Name string + Source string + //When string +} + +func init_themes() { + themeFiles, err := ioutil.ReadDir("./themes") + if err != nil { + log.Fatal(err) + } + + for _, themeFile := range themeFiles { + if !themeFile.IsDir() { + continue + } + + themeName := themeFile.Name() + log.Print("Adding theme '" + themeName + "'") + themeFile, err := ioutil.ReadFile("./themes/" + themeName + "/theme.json") + if err != nil { + log.Fatal(err) + } + + var theme Theme + json.Unmarshal(themeFile, &theme) + theme.Active = false // Set this to false, just in case someone explicitly overrode this value in the JSON file + + themes[theme.Name] = theme + } +} + +func add_theme_static_files(themeName string) { + err := filepath.Walk("./themes/" + themeName + "/public", func(path string, f os.FileInfo, err error) error { + if err != nil { + return err + } + if f.IsDir() { + return nil + } + path = strings.Replace(path,"\\","/",-1) + + log.Print("Attempting to add static file '" + path + "' for default theme '" + themeName + "'") + data, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + path = strings.TrimPrefix(path,"themes/" + themeName + "/public") + log.Print("Added the '" + path + "' static file for default theme " + themeName + ".") + + static_files["/static" + path] = SFile{data,0,int64(len(data)),mime.TypeByExtension(filepath.Ext("/themes/" + themeName + "/public" + path)),f,f.ModTime().UTC().Format(http.TimeFormat)} + return nil + }) + if err != nil { + panic(err) + } +} + +func map_theme_templates(theme Theme) { + if theme.Templates != nil { + for _, themeTmpl := range theme.Templates { + if themeTmpl.Name == "" { + log.Fatal("Invalid destination template name") + } + if themeTmpl.Source == "" { + log.Fatal("Invalid source template name") + } + + // go generate is one possibility, but it would simply add another step of compilation + + dest_tmpl_ptr, ok := tmpl_ptr_map[themeTmpl.Name] + if !ok { + log.Fatal("The destination template doesn't exist!") + } + source_tmpl_ptr, ok := tmpl_ptr_map[themeTmpl.Source] + if !ok { + log.Fatal("The source template doesn't exist!") + } + + switch d_tmpl_ptr := dest_tmpl_ptr.(type) { + case *func(TopicPage,io.Writer): + switch s_tmpl_ptr := source_tmpl_ptr.(type) { + case *func(TopicPage,io.Writer): + //overriden_templates[themeTmpl.Name] = d_tmpl_ptr + overriden_templates[themeTmpl.Name] = true + log.Print("Topic Handle") + fmt.Println(template_topic_handle) + log.Print("Before") + fmt.Println(d_tmpl_ptr) + fmt.Println(*d_tmpl_ptr) + log.Print("Source") + fmt.Println(s_tmpl_ptr) + fmt.Println(*s_tmpl_ptr) + *d_tmpl_ptr = *s_tmpl_ptr + log.Print("After") + fmt.Println(d_tmpl_ptr) + fmt.Println(*d_tmpl_ptr) + log.Print("Source") + fmt.Println(s_tmpl_ptr) + fmt.Println(*s_tmpl_ptr) + default: + log.Fatal("The source and destination templates are incompatible") + } + case *func(TopicsPage,io.Writer): + switch s_tmpl_ptr := source_tmpl_ptr.(type) { + case *func(TopicsPage,io.Writer): + //overriden_templates[themeTmpl.Name] = d_tmpl_ptr + overriden_templates[themeTmpl.Name] = true + *d_tmpl_ptr = *s_tmpl_ptr + default: + log.Fatal("The source and destination templates are incompatible") + } + case *func(ForumPage,io.Writer): + switch s_tmpl_ptr := source_tmpl_ptr.(type) { + case *func(ForumPage,io.Writer): + //overriden_templates[themeTmpl.Name] = d_tmpl_ptr + overriden_templates[themeTmpl.Name] = true + *d_tmpl_ptr = *s_tmpl_ptr + default: + log.Fatal("The source and destination templates are incompatible") + } + case *func(ForumsPage,io.Writer): + switch s_tmpl_ptr := source_tmpl_ptr.(type) { + case *func(ForumsPage,io.Writer): + //overriden_templates[themeTmpl.Name] = d_tmpl_ptr + overriden_templates[themeTmpl.Name] = true + *d_tmpl_ptr = *s_tmpl_ptr + default: + log.Fatal("The source and destination templates are incompatible") + } + case *func(ProfilePage,io.Writer): + switch s_tmpl_ptr := source_tmpl_ptr.(type) { + case *func(ProfilePage,io.Writer): + //overriden_templates[themeTmpl.Name] = d_tmpl_ptr + overriden_templates[themeTmpl.Name] = true + *d_tmpl_ptr = *s_tmpl_ptr + default: + log.Fatal("The source and destination templates are incompatible") + } + case *func(Page,io.Writer): + switch s_tmpl_ptr := source_tmpl_ptr.(type) { + case *func(Page,io.Writer): + //overriden_templates[themeTmpl.Name] = d_tmpl_ptr + overriden_templates[themeTmpl.Name] = true + *d_tmpl_ptr = *s_tmpl_ptr + default: + log.Fatal("The source and destination templates are incompatible") + } + default: + log.Fatal("Unknown destination template type!") + } + } + } +} + +func reset_template_overrides() { + log.Print("Resetting the template overrides") + + for name, _ := range overriden_templates { + log.Print("Resetting '" + name + "' template override") + + origin_pointer, ok := tmpl_ptr_map["o_" + name] + if !ok { + //log.Fatal("The origin template doesn't exist!") + log.Print("The origin template doesn't exist!") + return + } + + dest_tmpl_ptr, ok := tmpl_ptr_map[name] + if !ok { + //log.Fatal("The destination template doesn't exist!") + log.Print("The destination template doesn't exist!") + return + } + + switch o_ptr := origin_pointer.(type) { + case func(TopicPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(TopicPage,io.Writer): + log.Print("Topic Handle") + fmt.Println(template_topic_handle) + log.Print("Before") + fmt.Println(d_ptr) + fmt.Println(*d_ptr) + log.Print("Origin") + fmt.Println(o_ptr) + *d_ptr = o_ptr + log.Print("After") + fmt.Println(d_ptr) + fmt.Println(*d_ptr) + log.Print("Origin") + fmt.Println(o_ptr) + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(TopicsPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(TopicsPage,io.Writer): + *d_ptr = o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(ForumPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(ForumPage,io.Writer): + *d_ptr = o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(ForumsPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(ForumsPage,io.Writer): + *d_ptr = o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(ProfilePage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(ProfilePage,io.Writer): + *d_ptr = o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + default: + log.Fatal("Unknown destination template type!") + } + log.Print("The template override was reset") + } + overriden_templates = make(map[string]bool) + log.Print("All of the template overrides have been reset") + + /*for name, origin_pointer := range overriden_templates { + dest_tmpl_ptr, ok := tmpl_ptr_map[name] + if !ok { + log.Fatal("The destination template doesn't exist!") + } + + switch o_ptr := origin_pointer.(type) { + case *func(TopicPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(TopicPage,io.Writer): + *d_ptr = *o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(TopicsPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(TopicsPage,io.Writer): + *d_ptr = *o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(ForumPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(ForumPage,io.Writer): + *d_ptr = *o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(ForumsPage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(ForumsPage,io.Writer): + *d_ptr = *o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + case *func(ProfilePage,io.Writer): + switch d_ptr := dest_tmpl_ptr.(type) { + case *func(ProfilePage,io.Writer): + *d_ptr = *o_ptr + default: + log.Fatal("The origin and destination templates are incompatible") + } + default: + log.Fatal("Unknown destination template type!") + } + delete(overriden_templates, name) + }*/ +} diff --git a/themes/cosmo/public/stars-mk1.png b/themes/cosmo/public/stars-mk1.png new file mode 100644 index 00000000..723b7a73 Binary files /dev/null and b/themes/cosmo/public/stars-mk1.png differ diff --git a/themes/cosmo/theme.json b/themes/cosmo/theme.json new file mode 100644 index 00000000..f7ae1441 --- /dev/null +++ b/themes/cosmo/theme.json @@ -0,0 +1,12 @@ +{ + "Name": "cosmo", + "FriendlyName": "AtomBB Cosmo", + "Version": "Coming Soon", + "Creator": "Azareal", + "Templates": [ + { + "Name": "topic", + "Source": "topic_alt" + } + ] +} \ No newline at end of file diff --git a/public/main.css b/themes/tempra-conflux/public/main.css similarity index 100% rename from public/main.css rename to themes/tempra-conflux/public/main.css diff --git a/themes/tempra-conflux/theme.json b/themes/tempra-conflux/theme.json new file mode 100644 index 00000000..06ded26c --- /dev/null +++ b/themes/tempra-conflux/theme.json @@ -0,0 +1,12 @@ +{ + "Name": "tempra-conflux", + "FriendlyName": "Tempra Conflux", + "Version": "0.0.1", + "Creator": "Azareal", + "Templates": [ + { + "Name": "topic", + "Source": "topic_alt" + } + ] +} \ No newline at end of file diff --git a/themes/tempra-simple/public/main.css b/themes/tempra-simple/public/main.css new file mode 100644 index 00000000..e5039a61 --- /dev/null +++ b/themes/tempra-simple/public/main.css @@ -0,0 +1,409 @@ +* { + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; +} + +body +{ + font-family: arial; +} + +@font-face { + font-family: 'EmojiFont'; + src: url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff2') format('woff2'), + url('https://github.com/Ranks/emojione/raw/master/assets/fonts/emojione-svg.woff') format('woff'), local("arial"); +} + +@supports (-ms-ime-align:auto) { +.user_content +{ + font-family: EmojiFont, arial; +} +} +@-moz-document url-prefix() { +.user_content +{ + font-family: EmojiFont, arial; +} +} + +/*.move_left{float: left;position: relative;left: 50%;} +.move_right{float: left;position: relative;left: -50%;}*/ +ul +{ + padding-left: 0px; + padding-right: 0px; + height: 28px; + list-style-type: none; + border: 1px solid #ccc; +} +li +{ + height: 26px; + padding-left: 10px; + padding-top: 5px; + padding-bottom: 5px; + font-weight: bold; + text-transform: uppercase; +} +li:hover { background: rgb(250,250,250); } +li a +{ + text-decoration: none; + color: #515151; +} +/*li a:hover { color: #7a7a7a; }*/ +.menu_left +{ + float: left; + border-right: 1px solid #ccc; + padding-right: 10px; +} +.menu_right +{ + float: right; + border-left: 1px solid #ccc; + padding-right: 10px; +} + +.container +{ + width: 90%; + padding: 0px; + margin-left: auto; + margin-right: auto; +} + +.rowblock +{ + border: 1px solid #ccc; + width: 100%; + padding: 0px; + padding-top: 0px; +} +.rowblock:empty +{ + display: none; +} + +.colblock_left +{ + border: 1px solid #ccc; + padding: 0px; + padding-top: 0px; + width: 30%; + float: left; + margin-right: 8px; +} +.colblock_right +{ + border: 1px solid #ccc; + padding: 0px; + padding-top: 0px; + width: 65%; + overflow: hidden; + word-wrap: break-word; +} +.colblock_left:empty +{ + display: none; +} +.colblock_right:empty +{ + display: none; +} + +.rowitem +{ + width: 100%; + padding-left: 8px; + padding-right: 8px; + padding-top: 17px; + padding-bottom: 12px; + font-weight: bold; + text-transform: uppercase; +} +.rowitem.passive +{ + font-weight: normal; + text-transform: none; +} +.rowitem:not(:last-child) +{ + border-bottom: 1px dotted #ccc; +} +.rowitem a +{ + text-decoration: none; + color: black; +} +.rowitem a:hover +{ + color: silver; +} + +.col_left +{ + width: 30%; + float: left; +} +.col_right +{ + width: 69%; + overflow: hidden; +} +.colitem +{ + padding-left: 8px; + padding-right: 8px; + padding-top: 17px; + padding-bottom: 12px; + font-weight: bold; + text-transform: uppercase; +} +.colitem.passive +{ + font-weight: normal; + text-transform: none; +} +.colitem a +{ + text-decoration: none; + color: black; +} +.colitem a:hover +{ + color: silver; +} + +.formrow +{ + /*height: 40px;*/ + width: 100%; +} + +/*Clearfix*/ +.formrow:before, +.formrow:after { + content: " "; + display: table; +} + +.formrow:after { + clear: both; +} + +.formrow:not(:last-child) +{ + border-bottom: 1px dotted #ccc; +} + +.formitem +{ + float: left; + padding-left: 8px; + padding-right: 8px; + padding-top: 13px; + padding-bottom: 8px; + font-weight: bold; +} + +.formitem:first-child +{ + font-weight: bold; +} + +.formitem:not(:last-child) +{ + border-right: 1px dotted #ccc; +} + +.formitem.invisible_border +{ + border: none; +} + +/* Mostly for textareas */ +.formitem:only-child +{ + width: 97%; +} +.formitem textarea +{ + width: 100%; + height: 100px; +} +.formitem:has-child() +{ + margin: 0 auto; + float: none; +} + +button +{ + background: white; + border: 1px solid #8e8e8e; +} + +/* Topics */ +.topic_status +{ + text-transform: none; + margin-left: 8px; + padding-left: 2px; + padding-right: 2px; + padding-top: 2px; + padding-bottom: 2px; + background-color: #E8E8E8; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */ + color: #505050; /* 80,80,80 */ + border-radius: 2px; +} + +.topic_status:empty +{ + display: none; +} + +.username +{ + text-transform: none; + margin-left: 0px; + padding-left: 4px; + padding-right: 4px; + padding-top: 2px; + padding-bottom: 2px; + color: #505050; /* 80,80,80 */ + background-color: #FFFFFF; + border-style: dotted; + border-color: #505050; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */ + border-width: 1px; + font-size: 15px; +} +button.username +{ + position: relative; + top: -0.25px; +} +.tag-mini +{ + text-transform: none; + margin-left: 0px; + padding-left: 3px; + padding-right: 3px; + padding-top: 1.5px; + padding-bottom: 0px; + color: #505050; /* 80,80,80 */ + background-color: #FFFFFF; + border-style: dotted; + border-color: #505050; /* 232,232,232. All three RGB colours being the same seems to create a shade of gray */ + border-width: 1px; + font-size: 10px; +} + +.show_on_edit +{ + display: none; +} + +.alert +{ + display: block; + padding: 5px; + margin-bottom: 10px; + border: 1px solid #ccc; +} +.alert_success +{ + display: block; + padding: 5px; + border: 1px solid A2FC00; + margin-bottom: 10px; + background-color: DAF7A6; +} +.alert_error +{ + display: block; + padding: 5px; + border: 1px solid #FF004B; + margin-bottom: 8px; + background-color: #FEB7CC; +} + +@media (max-width: 880px) { + li + { + height: 25px; + font-size: 15px; + padding-left: 7px; + } + ul { height: 26px; margin-top: 8px; } + .menu_left { padding-right: 7px; } + .menu_right { padding-right: 7px; } + body { padding-left: 4px; padding-right: 4px; margin: 0px !important; width: 100% !important; height: 100% !important; overflow-x: hidden; } + .container { width: auto; } +} + +@media (max-width: 810px) { + li + { + font-weight: normal; + text-transform: none; + } + .rowitem + { + text-transform: none; + } +} + +@media (max-width: 620px) { + li + { + padding-left: 5px; + padding-top: 2px; + padding-bottom: 2px; + height: 23px; + } + ul { height: 24px; } + .menu_left { padding-right: 5px; } + .menu_right { padding-right: 5px; } + .menu_create_topic { display: none;} + .hide_on_mobile { display: none; } +} + +@media (max-width: 470px) { + .menu_overview { display: none; } + .menu_profile { display: none; } + .hide_on_micro { display: none; } + .post_container { + overflow: visible !important; + } + .post_item { + background-position: 0px 2px !important; + background-size: 64px 64px !important; + padding-left: 2px !important; + min-height: 96px; + position: relative !important; + } + .post_item > .user_content { + margin-left: 75px !important; + width: 100% !important; + } + .post_item > .mod_button { + float: right !important; + margin-left: 2px !important; + position: relative; + top: -14px; + } + .post_item > .real_username { + position: absolute; + top: 70px; + float: left; + margin-top: 0px; + padding-top: 3px !important; + margin-right: 2px; + width: 60px; + font-size: 15px; + } + .container { width: 100% !important; } +} \ No newline at end of file diff --git a/themes/tempra-simple/theme.json b/themes/tempra-simple/theme.json new file mode 100644 index 00000000..2257bbc3 --- /dev/null +++ b/themes/tempra-simple/theme.json @@ -0,0 +1,6 @@ +{ + "Name": "tempra-simple", + "FriendlyName": "Tempra Simple", + "Version": "0.0.1", + "Creator": "Azareal" +} \ No newline at end of file