From 99da1fcbaaf5ed3c58a35d4d3d3a891eaa345349 Mon Sep 17 00:00:00 2001 From: Azareal Date: Sun, 27 Aug 2017 10:33:45 +0100 Subject: [PATCH] Added support for word filters. Added support for temporary bans. Moved the Gosora specific template logic out of main.go and into template_init.go Added an internal temporary group API. We now use h1s in the theme headers. There may be some issues with the themes other than Shadow which we may need to resolve. Added ARIA attributes in a few places for improved accessibility. Added support for inputs in links for the .edit_field API. Removed a few unneccesary prepared statements. --- errors.go | 2 +- extend.go | 2 + gen_mysql.go | 63 +++- gen_pgsql.go | 14 + gen_router.go | 15 + general_test.go | 14 +- main.go | 271 +++--------------- mod_routes.go | 75 +++-- pages.go | 7 + panel_routes.go | 178 ++++++++++++ public/global.js | 2 + query_gen/main.go | 78 +++-- router_gen/routes.go | 6 + schema/mysql/query_users.sql | 1 + schema/mysql/query_users_groups_scheduler.sql | 9 + schema/mysql/query_word_filters.sql | 6 + schema/pgsql/query_users.sql | 1 + schema/pgsql/query_users_groups_scheduler.sql | 9 + schema/pgsql/query_word_filters.sql | 6 + tasks.go | 29 ++ template_init.go | 244 ++++++++++++++++ template_list.go | 169 +++++++---- template_profile.go | 76 ++--- templates/account-menu.html | 2 +- templates/account-own-edit-avatar.html | 2 +- templates/account-own-edit-email.html | 2 +- templates/account-own-edit-username.html | 2 +- templates/account-own-edit.html | 2 +- templates/are-you-sure.html | 2 +- templates/create-topic.html | 2 +- templates/error.html | 2 +- templates/footer.html | 2 +- templates/forum.html | 8 +- templates/login.html | 2 +- templates/menu.html | 2 +- templates/panel-adminlogs.html | 2 +- templates/panel-forum-edit.html | 2 +- templates/panel-forums.html | 4 +- templates/panel-group-edit-perms.html | 4 +- templates/panel-group-edit.html | 2 +- templates/panel-groups.html | 4 +- templates/panel-inner-menu.html | 3 + templates/panel-modlogs.html | 2 +- templates/panel-plugins.html | 2 +- templates/panel-setting.html | 2 +- templates/panel-settings.html | 2 +- templates/panel-themes.html | 4 +- templates/panel-user-edit.html | 2 +- templates/panel-users.html | 4 +- templates/panel-word-filters.html | 45 +++ templates/profile.html | 74 ++++- templates/register.html | 18 +- templates/socialgroups_create_group.html | 2 +- templates/topic.html | 6 +- templates/topic_alt.html | 6 +- templates/topics.html | 2 +- templates/widget_menu.html | 2 +- templates/widget_simple.html | 2 +- themes/shadow/public/main.css | 15 + topic_store.go | 6 +- user.go | 56 +++- user_store.go | 43 ++- 62 files changed, 1154 insertions(+), 469 deletions(-) create mode 100644 schema/mysql/query_users_groups_scheduler.sql create mode 100644 schema/mysql/query_word_filters.sql create mode 100644 schema/pgsql/query_users_groups_scheduler.sql create mode 100644 schema/pgsql/query_word_filters.sql create mode 100644 tasks.go create mode 100644 template_init.go create mode 100644 templates/panel-word-filters.html diff --git a/errors.go b/errors.go index c3c93d92..a6c07b5a 100644 --- a/errors.go +++ b/errors.go @@ -17,7 +17,7 @@ var error_notfound []byte func init_errors() error { var b bytes.Buffer - user := User{0,"guest","Guest","",0,false,false,false,false,false,false,GuestPerms,nil,"",false,"","","","","",0,0,"0.0.0.0.0"} + user := User{0,"guest","Guest","",0,false,false,false,false,false,false,GuestPerms,nil,"",false,"","","","","",0,0,"0.0.0.0.0",0} pi := Page{"Internal Server Error",user,hvars,tList,"A problem has occurred in the system."} err := templates.ExecuteTemplate(&b,"error.html", pi) if err != nil { diff --git a/extend.go b/extend.go index 7acb8237..75f62d75 100644 --- a/extend.go +++ b/extend.go @@ -57,6 +57,8 @@ var pre_render_hooks map[string][]func(http.ResponseWriter, *http.Request, *User "pre_render_panel_edit_forum": nil, "pre_render_panel_settings": nil, "pre_render_panel_setting": nil, + "pre_render_panel_word_filters": nil, + "pre_render_panel_word_filters_edit": nil, "pre_render_panel_plugins": nil, "pre_render_panel_users": nil, "pre_render_panel_edit_user": nil, diff --git a/gen_mysql.go b/gen_mysql.go index 9956a83f..d408f37b 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -24,6 +24,7 @@ var get_widgets_stmt *sql.Stmt var is_plugin_active_stmt *sql.Stmt var get_users_stmt *sql.Stmt var get_users_offset_stmt *sql.Stmt +var get_word_filters_stmt *sql.Stmt var is_theme_default_stmt *sql.Stmt var get_modlogs_stmt *sql.Stmt var get_modlogs_offset_stmt *sql.Stmt @@ -33,15 +34,14 @@ var get_user_reply_uid_stmt *sql.Stmt var has_liked_topic_stmt *sql.Stmt var has_liked_reply_stmt *sql.Stmt var get_user_name_stmt *sql.Stmt -var get_user_rank_stmt *sql.Stmt var get_user_active_stmt *sql.Stmt -var get_user_group_stmt *sql.Stmt var get_emails_by_user_stmt *sql.Stmt var get_topic_basic_stmt *sql.Stmt var get_activity_entry_stmt *sql.Stmt var forum_entry_exists_stmt *sql.Stmt var group_entry_exists_stmt *sql.Stmt var get_forum_topics_offset_stmt *sql.Stmt +var get_expired_scheduled_groups_stmt *sql.Stmt var get_topic_replies_offset_stmt *sql.Stmt var get_topic_list_stmt *sql.Stmt var get_topic_user_stmt *sql.Stmt @@ -67,7 +67,9 @@ var add_theme_stmt *sql.Stmt var create_group_stmt *sql.Stmt var add_modlog_entry_stmt *sql.Stmt var add_adminlog_entry_stmt *sql.Stmt +var create_word_filter_stmt *sql.Stmt var add_forum_perms_to_group_stmt *sql.Stmt +var replace_schedule_group_stmt *sql.Stmt var add_replies_to_topic_stmt *sql.Stmt var remove_replies_from_topic_stmt *sql.Stmt var add_topics_to_forum_stmt *sql.Stmt @@ -105,11 +107,14 @@ var update_group_rank_stmt *sql.Stmt var update_group_stmt *sql.Stmt var update_email_stmt *sql.Stmt var verify_email_stmt *sql.Stmt +var set_temp_group_stmt *sql.Stmt +var update_word_filter_stmt *sql.Stmt var delete_reply_stmt *sql.Stmt var delete_topic_stmt *sql.Stmt var delete_profile_reply_stmt *sql.Stmt var delete_forum_perms_by_forum_stmt *sql.Stmt var delete_activity_stream_match_stmt *sql.Stmt +var delete_word_filter_stmt *sql.Stmt var report_exists_stmt *sql.Stmt var group_count_stmt *sql.Stmt var modlog_count_stmt *sql.Stmt @@ -225,6 +230,12 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing get_word_filters statement.") + get_word_filters_stmt, err = db.Prepare("SELECT `wfid`,`find`,`replacement` FROM `word_filters`") + if err != nil { + return err + } + log.Print("Preparing is_theme_default statement.") is_theme_default_stmt, err = db.Prepare("SELECT `default` FROM `themes` WHERE `uname` = ?") if err != nil { @@ -279,24 +290,12 @@ func _gen_mysql() (err error) { return err } - log.Print("Preparing get_user_rank statement.") - get_user_rank_stmt, err = db.Prepare("SELECT `group`,`is_super_admin` FROM `users` WHERE `uid` = ?") - if err != nil { - return err - } - log.Print("Preparing get_user_active statement.") get_user_active_stmt, err = db.Prepare("SELECT `active` FROM `users` WHERE `uid` = ?") if err != nil { return err } - log.Print("Preparing get_user_group statement.") - get_user_group_stmt, err = db.Prepare("SELECT `group` FROM `users` WHERE `uid` = ?") - if err != nil { - return err - } - log.Print("Preparing get_emails_by_user statement.") get_emails_by_user_stmt, err = db.Prepare("SELECT `email`,`validated`,`token` FROM `emails` WHERE `uid` = ?") if err != nil { @@ -333,6 +332,12 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing get_expired_scheduled_groups statement.") + get_expired_scheduled_groups_stmt, err = db.Prepare("SELECT `uid` FROM `users_groups_scheduler` WHERE UTC_TIMESTAMP() > `revert_at` AND `temporary` = 1") + if err != nil { + return err + } + log.Print("Preparing get_topic_replies_offset statement.") get_topic_replies_offset_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ? LIMIT ?,?") if err != nil { @@ -483,12 +488,24 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing create_word_filter statement.") + create_word_filter_stmt, err = db.Prepare("INSERT INTO `word_filters`(`find`,`replacement`) VALUES (?,?)") + if err != nil { + return err + } + log.Print("Preparing add_forum_perms_to_group statement.") add_forum_perms_to_group_stmt, err = db.Prepare("REPLACE INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)") if err != nil { return err } + log.Print("Preparing replace_schedule_group statement.") + replace_schedule_group_stmt, err = db.Prepare("REPLACE INTO `users_groups_scheduler`(`uid`,`set_group`,`issued_by`,`issued_at`,`revert_at`,`temporary`) VALUES (?,?,?,UTC_TIMESTAMP(),?,?)") + if err != nil { + return err + } + log.Print("Preparing add_replies_to_topic statement.") add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyBy` = ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?") if err != nil { @@ -711,6 +728,18 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing set_temp_group statement.") + set_temp_group_stmt, err = db.Prepare("UPDATE `users` SET `temp_group` = ? WHERE `uid` = ?") + if err != nil { + return err + } + + log.Print("Preparing update_word_filter statement.") + update_word_filter_stmt, err = db.Prepare("UPDATE `word_filters` SET `find` = ?,`replacement` = ? WHERE `wfid` = ?") + if err != nil { + return err + } + log.Print("Preparing delete_reply statement.") delete_reply_stmt, err = db.Prepare("DELETE FROM `replies` WHERE `rid` = ?") if err != nil { @@ -741,6 +770,12 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing delete_word_filter statement.") + delete_word_filter_stmt, err = db.Prepare("DELETE FROM `word_filters` WHERE `wfid` = ?") + if err != nil { + return err + } + log.Print("Preparing report_exists statement.") report_exists_stmt, err = db.Prepare("SELECT COUNT(*) AS `count` FROM `topics` WHERE `data` = ? AND `data` != '' AND `parentID` = 1") if err != nil { diff --git a/gen_pgsql.go b/gen_pgsql.go index 64e9e07e..f0496d04 100644 --- a/gen_pgsql.go +++ b/gen_pgsql.go @@ -43,6 +43,8 @@ var update_group_rank_stmt *sql.Stmt var update_group_stmt *sql.Stmt var update_email_stmt *sql.Stmt var verify_email_stmt *sql.Stmt +var set_temp_group_stmt *sql.Stmt +var update_word_filter_stmt *sql.Stmt func _gen_pgsql() (err error) { if dev.DebugMode { @@ -270,6 +272,18 @@ func _gen_pgsql() (err error) { if err != nil { return err } + + log.Print("Preparing set_temp_group statement.") + set_temp_group_stmt, err = db.Prepare("UPDATE `users` SET `temp_group` = ? WHERE `uid` = ?") + if err != nil { + return err + } + + log.Print("Preparing update_word_filter statement.") + update_word_filter_stmt, err = db.Prepare("UPDATE `word_filters` SET `find` = ?,`replacement` = ? WHERE `wfid` = ?") + if err != nil { + return err + } return nil } diff --git a/gen_router.go b/gen_router.go index a257cb4f..e5c5115e 100644 --- a/gen_router.go +++ b/gen_router.go @@ -150,6 +150,21 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { case "/panel/settings/edit/submit/": route_panel_setting_edit(w,req,user,extra_data) return + case "/panel/settings/word-filters/": + route_panel_word_filters(w,req,user) + return + case "/panel/settings/word-filters/create/": + route_panel_word_filters_create(w,req,user) + return + case "/panel/settings/word-filters/edit/": + route_panel_word_filters_edit(w,req,user,extra_data) + return + case "/panel/settings/word-filters/edit/submit/": + route_panel_word_filters_edit_submit(w,req,user,extra_data) + return + case "/panel/settings/word-filters/delete/submit/": + route_panel_word_filters_delete_submit(w,req,user,extra_data) + return case "/panel/themes/": route_panel_themes(w,req,user) return diff --git a/general_test.go b/general_test.go index 3300f362..f6135b5a 100644 --- a/general_test.go +++ b/general_test.go @@ -25,7 +25,7 @@ var db_test *sql.DB var db_prod *sql.DB var gloinited bool -func gloinit() { +func gloinit() error { dev.DebugMode = false //nogrouplog = true @@ -40,20 +40,20 @@ func gloinit() { init_themes() err := init_database() if err != nil { - log.Fatal(err) + return err } db_prod = db //db_test, err = sql.Open("testdb","") //if err != nil { - // log.Fatal(err) + // return err //} init_templates() db_prod.SetMaxOpenConns(64) err = init_errors() if err != nil { - log.Fatal(err) + return err } if config.CacheTopicUser == CACHE_STATIC { @@ -68,8 +68,14 @@ func gloinit() { //log.SetOutput(os.Stdout) auth = NewDefaultAuth() + err = init_word_filters() + if err != nil { + return err + } + router = NewGenRouter(http.FileServer(http.Dir("./uploads"))) gloinited = true + return nil } func init() { diff --git a/main.go b/main.go index c6513946..addc4e63 100644 --- a/main.go +++ b/main.go @@ -8,8 +8,8 @@ import ( "time" "io" "os" + "sync/atomic" "net/http" - "html/template" //"runtime/pprof" ) @@ -17,6 +17,7 @@ var version Version = Version{Major:0,Minor:1,Patch:0,Tag:"dev"} const hour int = 60 * 60 const day int = hour * 24 +const week int = day * 7 const month int = day * 30 const year int = day * 365 const kilobyte int = 1024 @@ -29,9 +30,6 @@ var enable_websockets bool = false // Don't change this, the value is overwritte var router *GenRouter var startTime time.Time -//var timeLocation *time.Location -var templates = template.New("") -//var no_css_tmpl template.CSS = template.CSS("") var external_sites map[string]string = map[string]string{ "YT":"https://www.youtube.com/", } @@ -40,236 +38,45 @@ var groupCapCount int var static_files map[string]SFile = make(map[string]SFile) var logWriter io.Writer = io.MultiWriter(os.Stderr) -func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["topic"] - if !ok { - mapping = "topic" - } - err := templates.ExecuteTemplate(w,mapping + ".html", pi) - if err != nil { - InternalError(err,w) - } +type WordFilter struct { + ID int + Find string + Replacement string +} +type WordFilterBox map[int]WordFilter +var wordFilterBox atomic.Value // An atomic value holding a WordFilterBox + +func init() { + wordFilterBox.Store(WordFilterBox(make(map[int]WordFilter))) } -var template_topic_handle func(TopicPage,http.ResponseWriter) = interpreted_topic_template -var template_topic_alt_handle func(TopicPage,http.ResponseWriter) = interpreted_topic_template -var template_topics_handle func(TopicsPage,http.ResponseWriter) = func(pi TopicsPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["topics"] - if !ok { - mapping = "topics" - } - err := templates.ExecuteTemplate(w,mapping + ".html", pi) - if err != nil { - InternalError(err,w) - } -} -var template_forum_handle func(ForumPage,http.ResponseWriter) = func(pi ForumPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["forum"] - if !ok { - mapping = "forum" - } - err := templates.ExecuteTemplate(w,mapping + ".html", pi) - if err != nil { - InternalError(err,w) - } -} -var template_forums_handle func(ForumsPage,http.ResponseWriter) = func(pi ForumsPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["forums"] - if !ok { - mapping = "forums" - } - err := templates.ExecuteTemplate(w,mapping + ".html", pi) - if err != nil { - InternalError(err,w) - } -} -var template_profile_handle func(ProfilePage,http.ResponseWriter) = func(pi ProfilePage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["profile"] - if !ok { - mapping = "profile" - } - err := templates.ExecuteTemplate(w,mapping + ".html", pi) - if err != nil { - InternalError(err,w) - } -} -var template_create_topic_handle func(CreateTopicPage,http.ResponseWriter) = func(pi CreateTopicPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["create-topic"] - if !ok { - mapping = "create-topic" - } - err := templates.ExecuteTemplate(w,mapping + ".html", pi) - if err != nil { - InternalError(err,w) - } -} - -func compile_templates() error { - var c CTemplateSet - user := User{62,build_profile_url("fake-user",62),"Fake User","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"0.0.0.0.0"} - // TO-DO: Do a more accurate level calculation for this? - user2 := User{1,build_profile_url("admin-alice",1),"Admin Alice","alice@localhost",1,true,true,true,true,false,false,AllPerms,make(map[string]bool),"",true,"","","","","",58,1000,"127.0.0.1"} - user3 := User{2,build_profile_url("admin-fred",62),"Admin Fred","fred@localhost",1,true,true,true,true,false,false,AllPerms,make(map[string]bool),"",true,"","","","","",42,900,"::1"} - headerVars := HeaderVars{ - Site:site, - NoticeList:[]string{"test"}, - Stylesheets:[]string{"panel"}, - Scripts:[]string{"whatever"}, - Widgets:PageWidgets{ - LeftSidebar: template.HTML("lalala"), - }, - } - - log.Print("Compiling the templates") - - topic := TopicUser{1,"blah","Blah","Hey there!",0,false,false,"Date","Date",0,"","127.0.0.1",0,1,"classname","weird-data",build_profile_url("fake-user",62),"Fake User",config.DefaultGroup,"",0,"","","","",58,false} - var replyList []Reply - replyList = append(replyList, Reply{0,0,"Yo!","Yo!",0,"alice","Alice",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""}) - - var varList map[string]VarItem = make(map[string]VarItem) - tpage := TopicPage{"Title",user,headerVars,replyList,topic,1,1,extData} - topic_id_tmpl, err := c.compile_template("topic.html","templates/","TopicPage", tpage, varList) - if err != nil { - return err - } - topic_id_alt_tmpl, err := c.compile_template("topic_alt.html","templates/","TopicPage", tpage, varList) +func init_word_filters() error { + rows, err := get_word_filters_stmt.Query() if err != nil { return err } + defer rows.Close() - varList = make(map[string]VarItem) - ppage := ProfilePage{"User 526",user,headerVars,replyList,user,extData} - profile_tmpl, err := c.compile_template("profile.html","templates/","ProfilePage", ppage, varList) - if err != nil { - return err - } + var wordFilters WordFilterBox = wordFilterBox.Load().(WordFilterBox) + var wfid int + var find string + var replacement string - var forumList []Forum - forums, err := fstore.GetAll() - if err != nil { - return err - } - - for _, forum := range forums { - if forum.Active { - forumList = append(forumList,*forum) - } - } - varList = make(map[string]VarItem) - forums_page := ForumsPage{"Forum List",user,headerVars,forumList,extData} - forums_tmpl, err := c.compile_template("forums.html","templates/","ForumsPage",forums_page,varList) - if err != nil { - return err - } - - var topicsList []*TopicsRow - topicsList = append(topicsList,&TopicsRow{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",user3.ID,1,"","127.0.0.1",0,1,"classname","",&user2,"",0,&user3,"General","/forum/general.2"}) - topics_page := TopicsPage{"Topic List",user,headerVars,topicsList,extData} - topics_tmpl, err := c.compile_template("topics.html","templates/","TopicsPage",topics_page,varList) - if err != nil { - return err - } - - //var topicList []TopicUser - //topicList = append(topicList,TopicUser{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","admin-fred","Admin Fred",config.DefaultGroup,"",0,"","","","",58,false}) - forum_item := Forum{1,"general","General Forum","Where the general stuff happens",true,"all",0,"",0,"","",0,"",0,""} - forum_page := ForumPage{"General Forum",user,headerVars,topicsList,forum_item,1,1,extData} - forum_tmpl, err := c.compile_template("forum.html","templates/","ForumPage",forum_page,varList) - if err != nil { - return err - } - - log.Print("Writing the templates") - go write_template("topic", topic_id_tmpl) - go write_template("topic_alt", topic_id_alt_tmpl) - go write_template("profile", profile_tmpl) - go write_template("forums", forums_tmpl) - go write_template("topics", topics_tmpl) - go write_template("forum", forum_tmpl) - go func() { - err := write_file("./template_list.go","package main\n\n" + c.FragOut) + for rows.Next() { + err := rows.Scan(&wfid, &find, &replacement) if err != nil { - log.Fatal(err) + return err } - }() - - return nil + wordFilters[wfid] = WordFilter{ID:wfid,Find:find,Replacement:replacement} + } + wordFilterBox.Store(wordFilters) + return rows.Err() } -func write_template(name string, content string) { - err := write_file("./template_" + name + ".go", content) - if err != nil { - log.Fatal(err) - } -} - -func init_templates() { - if dev.DebugMode { - log.Print("Initialising the template system") - } - compile_templates() - - // TO-DO: Add support for 64-bit integers - // TO-DO: Add support for floats - fmap := make(map[string]interface{}) - fmap["add"] = func(left interface{}, right interface{})interface{} { - var left_int int - var right_int int - switch left := left.(type) { - case uint, uint8, uint16, int, int32: left_int = left.(int) - } - switch right := right.(type) { - case uint, uint8, uint16, int, int32: right_int = right.(int) - } - return left_int + right_int - } - - fmap["subtract"] = func(left interface{}, right interface{})interface{} { - var left_int int - var right_int int - switch left := left.(type) { - case uint, uint8, uint16, int, int32: left_int = left.(int) - } - switch right := right.(type) { - case uint, uint8, uint16, int, int32: right_int = right.(int) - } - return left_int - right_int - } - - fmap["multiply"] = func(left interface{}, right interface{})interface{} { - var left_int int - var right_int int - switch left := left.(type) { - case uint, uint8, uint16, int, int32: left_int = left.(int) - } - switch right := right.(type) { - case uint, uint8, uint16, int, int32: right_int = right.(int) - } - return left_int * right_int - } - - fmap["divide"] = func(left interface{}, right interface{})interface{} { - var left_int int - var right_int int - switch left := left.(type) { - case uint, uint8, uint16, int, int32: left_int = left.(int) - } - switch right := right.(type) { - case uint, uint8, uint16, int, int32: right_int = right.(int) - } - if left_int == 0 || right_int == 0 { - return 0 - } - return left_int / right_int - } - - // The interpreted templates... - if dev.DebugMode { - log.Print("Loading the template files...") - } - templates.Funcs(fmap) - template.Must(templates.ParseGlob("templates/*")) - template.Must(templates.ParseGlob("pages/*")) +func add_word_filter(id int, find string, replacement string) { + wordFilters := wordFilterBox.Load().(WordFilterBox) + wordFilters[id] = WordFilter{ID:id,Find:find,Replacement:replacement} + wordFilterBox.Store(wordFilters) } func process_config() { @@ -339,6 +146,11 @@ func main(){ log.Print("Initialising the authentication system") auth = NewDefaultAuth() + err = init_word_filters() + if err != nil { + log.Fatal(err) + } + // Run this goroutine once a second second_ticker := time.NewTicker(1 * time.Second) fifteen_minute_ticker := time.NewTicker(15 * time.Minute) @@ -347,6 +159,11 @@ func main(){ for { select { case <- second_ticker.C: + //log.Print("Running the second ticker") + err := handle_expired_scheduled_groups() + if err != nil { + LogError(err) + } // TO-DO: Handle delayed moderation tasks // TO-DO: Handle the daily clean-up. Move this to a 24 hour task? // TO-DO: Sync with the database, if there are any changes @@ -354,9 +171,9 @@ func main(){ // TO-DO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high // TO-DO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task? case <- fifteen_minute_ticker.C: - // TO-DO: Handle temporary bans. // TO-DO: Automatically lock topics, if they're really old, and the associated setting is enabled. - // TO-DO: Publish scheduled posts. Move this to a 15 minute task? + // TO-DO: Publish scheduled posts. + // TO-DO: Delete the empty users_groups_scheduler entries } } }() @@ -409,8 +226,8 @@ func main(){ router.HandleFunc("/profile/reply/create/", route_profile_reply_create) router.HandleFunc("/profile/reply/edit/submit/", route_profile_reply_edit_submit) router.HandleFunc("/profile/reply/delete/submit/", route_profile_reply_delete_submit) - //router.HandleFunc("/user/edit/submit/", route_logout) - router.HandleFunc("/users/ban/", route_ban) + //router.HandleFunc("/user/edit/submit/", route_logout) // route_logout? what on earth? o.o + //router.HandleFunc("/users/ban/", route_ban) router.HandleFunc("/users/ban/submit/", route_ban_submit) router.HandleFunc("/users/unban/", route_unban) router.HandleFunc("/users/activate/", route_activate) diff --git a/mod_routes.go b/mod_routes.go index 8e5edf58..0df52f37 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -521,7 +521,8 @@ func route_profile_reply_delete_submit(w http.ResponseWriter, r *http.Request, u } } -func route_ban(w http.ResponseWriter, r *http.Request, user User) { +// TO-DO: This is being replaced with the new ban route system +/*func route_ban(w http.ResponseWriter, r *http.Request, user User) { headerVars, ok := SessionCheck(w,r,&user) if !ok { return @@ -557,7 +558,7 @@ func route_ban(w http.ResponseWriter, r *http.Request, user User) { } } templates.ExecuteTemplate(w,"areyousure.html",pi) -} +}*/ func route_ban_submit(w http.ResponseWriter, r *http.Request, user User) { if !user.Perms.BanUsers { @@ -575,13 +576,11 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request, user User) { return } /*if uid == -2 { - LocalError("Sigh, are you really trying to ban me? Do you despise so much? Despite all of our adventures over at /arcane-tower/...?",w,r,user) + LocalError("Stop trying to ban Merlin! Ban admin! Bad! No!",w,r,user) return }*/ - var group int - var is_super_admin bool - err = get_user_rank_stmt.QueryRow(uid).Scan(&group, &is_super_admin) + targetUser, err := users.CascadeGet(uid) if err == ErrNoRows { LocalError("The user you're trying to ban no longer exists.",w,r,user) return @@ -590,7 +589,7 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request, user User) { return } - if is_super_admin || groups[group].Is_Admin || groups[group].Is_Mod { + if targetUser.Is_Super_Admin || targetUser.Is_Admin || targetUser.Is_Mod { LocalError("You may not ban another staff member.",w,r,user) return } @@ -599,13 +598,45 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request, user User) { return } - if groups[group].Is_Banned { + if targetUser.Is_Banned { LocalError("The user you're trying to unban is already banned.",w,r,user) return } - _, err = change_group_stmt.Exec(4, uid) + duration_days, err := strconv.Atoi(r.FormValue("ban-duration-days")) if err != nil { + LocalError("You can only use whole numbers for the number of days",w,r,user) + return + } + + duration_weeks, err := strconv.Atoi(r.FormValue("ban-duration-weeks")) + if err != nil { + LocalError("You can only use whole numbers for the number of weeks",w,r,user) + return + } + + duration_months, err := strconv.Atoi(r.FormValue("ban-duration-months")) + if err != nil { + LocalError("You can only use whole numbers for the number of months",w,r,user) + return + } + + var duration time.Duration + if duration_days > 1 && duration_weeks > 1 && duration_months > 1 { + duration, _ = time.ParseDuration("0") + } else { + var seconds int + seconds += duration_days * day + seconds += duration_weeks * week + seconds += duration_months * month + duration, _ = time.ParseDuration(strconv.Itoa(seconds) + "s") + } + + err = targetUser.Ban(duration,user.ID) + if err == ErrNoRows { + LocalError("The user you're trying to ban no longer exists.",w,r,user) + return + } else if err != nil { InternalError(err,w) return } @@ -621,11 +652,6 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request, user User) { return } - err = users.Load(uid) - if err != nil { - LocalError("This user no longer exists!",w,r,user) - return - } http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther) } @@ -645,8 +671,7 @@ func route_unban(w http.ResponseWriter, r *http.Request, user User) { return } - var group int - err = get_user_group_stmt.QueryRow(uid).Scan(&group) + targetUser, err := users.CascadeGet(uid) if err == ErrNoRows { LocalError("The user you're trying to unban no longer exists.",w,r,user) return @@ -655,13 +680,16 @@ func route_unban(w http.ResponseWriter, r *http.Request, user User) { return } - if !groups[group].Is_Banned { + if !targetUser.Is_Banned { LocalError("The user you're trying to unban isn't banned.",w,r,user) return } - _, err = change_group_stmt.Exec(config.DefaultGroup, uid) - if err != nil { + err = targetUser.Unban() + if err == ErrNoRows { + LocalError("The user you're trying to unban no longer exists.",w,r,user) + return + } else if err != nil { InternalError(err,w) return } @@ -677,15 +705,6 @@ func route_unban(w http.ResponseWriter, r *http.Request, user User) { return } - err = users.Load(uid) - if err != nil && err == ErrNoRows { - LocalError("This user no longer exists!",w,r,user) - return - } else if err != nil { - InternalError(err,w) - return - } - http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther) } diff --git a/pages.go b/pages.go index 6d5b0bde..950da411 100644 --- a/pages.go +++ b/pages.go @@ -120,6 +120,7 @@ type PanelStats struct Groups int Forums int Settings int + WordFilters int Themes int Reports int } @@ -463,6 +464,12 @@ func parse_message(msg string/*, user User*/) string { msg = strings.Replace(msg,":o","😲",-1) //msg = url_reg.ReplaceAllString(msg,"$2$3//$4") + // Word filter list. E.g. Swear words and other things the admins don't like + wordFilters := wordFilterBox.Load().(WordFilterBox) + for _, filter := range wordFilters { + msg = strings.Replace(msg,filter.Find,filter.Replacement,-1) + } + // Search for URLs, mentions and hashlinks in the messages... //log.Print("Parser Loop!") var msgbytes = []byte(msg) diff --git a/panel_routes.go b/panel_routes.go index 47791405..967357c1 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -722,6 +722,184 @@ func route_panel_setting_edit(w http.ResponseWriter, r *http.Request, user User, http.Redirect(w,r,"/panel/settings/",http.StatusSeeOther) } +func route_panel_word_filters(w http.ResponseWriter, r *http.Request, user User){ + headerVars, stats, ok := PanelSessionCheck(w,r,&user) + if !ok { + return + } + if !user.Perms.EditSettings { + NoPermissions(w,r,user) + return + } + + var filterList WordFilterBox = wordFilterBox.Load().(WordFilterBox) + pi := PanelPage{"Word Filter Manager",user,headerVars,stats,tList,filterList} + if pre_render_hooks["pre_render_panel_word_filters"] != nil { + if run_pre_render_hook("pre_render_panel_word_filters", w, r, &user, &pi) { + return + } + } + err := templates.ExecuteTemplate(w,"panel-word-filters.html",pi) + if err != nil { + InternalError(err,w) + } +} + +func route_panel_word_filters_create(w http.ResponseWriter, r *http.Request, user User){ + _, ok := SimplePanelSessionCheck(w,r,&user) + if !ok { + return + } + if !user.Perms.EditSettings { + NoPermissions(w,r,user) + return + } + + err := r.ParseForm() + if err != nil { + PreError("Bad Form",w,r) + return + } + is_js := r.PostFormValue("js") + if is_js == "" { + is_js = "0" + } + + find := strings.TrimSpace(r.PostFormValue("find")) + if find == "" { + LocalErrorJSQ("You need to specify what word you want to match",w,r,user,is_js) + return + } + + // Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement + replacement := strings.TrimSpace(r.PostFormValue("replacement")) + + res, err := create_word_filter_stmt.Exec(find,replacement) + if err != nil { + InternalErrorJSQ(err,w,r,is_js) + return + } + lastId, err := res.LastInsertId() + if err != nil { + InternalErrorJSQ(err,w,r,is_js) + return + } + + add_word_filter(int(lastId),find,replacement) + http.Redirect(w,r,"/panel/settings/word-filters/",http.StatusSeeOther) +} + +func route_panel_word_filters_edit(w http.ResponseWriter, r *http.Request, user User, wfid string){ + headerVars, stats, ok := PanelSessionCheck(w,r,&user) + if !ok { + return + } + if !user.Perms.EditSettings { + NoPermissions(w,r,user) + return + } + + _ = wfid + + pi := PanelPage{"Edit Word Filter",user,headerVars,stats,tList,nil} + if pre_render_hooks["pre_render_panel_word_filters_edit"] != nil { + if run_pre_render_hook("pre_render_panel_word_filters_edit", w, r, &user, &pi) { + return + } + } + err := templates.ExecuteTemplate(w,"panel-word-filters-edit.html",pi) + if err != nil { + InternalError(err,w) + } +} + +func route_panel_word_filters_edit_submit(w http.ResponseWriter, r *http.Request, user User, wfid string){ + _, ok := SimplePanelSessionCheck(w,r,&user) + if !ok { + return + } + + err := r.ParseForm() + if err != nil { + PreError("Bad Form",w,r) + return + } + is_js := r.PostFormValue("is_js") + if is_js == "" { + is_js = "0" + } + if !user.Perms.EditSettings { + NoPermissionsJSQ(w,r,user,is_js) + return + } + + id, err := strconv.Atoi(wfid) + if err != nil { + LocalErrorJSQ("The word filter ID must be an integer.",w,r,user,is_js) + return + } + + find := strings.TrimSpace(r.PostFormValue("find")) + if find == "" { + LocalErrorJSQ("You need to specify what word you want to match",w,r,user,is_js) + return + } + + // Unlike with find, it's okay if we leave this blank, as this means that the admin wants to remove the word entirely with no replacement + replacement := strings.TrimSpace(r.PostFormValue("replacement")) + + _, err = update_word_filter_stmt.Exec(find,replacement,id) + if err != nil { + InternalErrorJSQ(err,w,r,is_js) + return + } + + wordFilters := wordFilterBox.Load().(WordFilterBox) + wordFilters[id] = WordFilter{ID:id,Find:find,Replacement:replacement} + wordFilterBox.Store(wordFilters) + + http.Redirect(w,r,"/panel/settings/word-filters/",http.StatusSeeOther) +} + +func route_panel_word_filters_delete_submit(w http.ResponseWriter, r *http.Request, user User, wfid string){ + _, ok := SimplePanelSessionCheck(w,r,&user) + if !ok { + return + } + + err := r.ParseForm() + if err != nil { + PreError("Bad Form",w,r) + return + } + is_js := r.PostFormValue("is_js") + if is_js == "" { + is_js = "0" + } + if !user.Perms.EditSettings { + NoPermissionsJSQ(w,r,user,is_js) + return + } + + id, err := strconv.Atoi(wfid) + if err != nil { + LocalErrorJSQ("The word filter ID must be an integer.",w,r,user,is_js) + return + } + + _, err = delete_word_filter_stmt.Exec(id) + if err != nil { + InternalErrorJSQ(err,w,r,is_js) + return + } + + wordFilters := wordFilterBox.Load().(WordFilterBox) + delete(wordFilters,id) + wordFilterBox.Store(wordFilters) + + http.Redirect(w,r,"/panel/settings/word-filters/",http.StatusSeeOther) +} + func route_panel_plugins(w http.ResponseWriter, r *http.Request, user User){ headerVars, stats, ok := PanelSessionCheck(w,r,&user) if !ok { diff --git a/public/global.js b/public/global.js index 69a6514f..1d11e2e8 100644 --- a/public/global.js +++ b/public/global.js @@ -284,6 +284,7 @@ $(document).ready(function(){ $(".edit_fields").click(function(event) { event.preventDefault(); + if($(this).find("input").length !== 0) return; //console.log("clicked .edit_fields"); var block_parent = $(this).closest('.editable_parent'); //console.log(block_parent); @@ -353,6 +354,7 @@ $(document).ready(function(){ }); // This one's for Tempra Conflux + // TO-DO: We might want to use pure JS here $(".ip_item").each(function(){ var ip = this.textContent; if(ip.length > 10){ diff --git a/query_gen/main.go b/query_gen/main.go index c671681d..85dbb2d8 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -110,6 +110,7 @@ func create_tables(adapter qgen.DB_Adapter) error { qgen.DB_Table_Column{"megaposts","int",0,false,false,"0"}, qgen.DB_Table_Column{"topics","int",0,false,false,"0"}, //qgen.DB_Table_Column{"penalty_count","int",0,false,false,"0"}, + qgen.DB_Table_Column{"temp_group","int",0,false,false,"0"}, // For temporary groups, set this to zero when a temporary group isn't in effect }, []qgen.DB_Table_Key{ qgen.DB_Table_Key{"uid","primary"}, @@ -117,35 +118,63 @@ func create_tables(adapter qgen.DB_Adapter) error { }, ) - // Coming Soon! // What should we do about global penalties? Put them on the users table for speed? Or keep them here? - // Should we add IP Penalties? + // Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans? + // TO-DO: Perm overrides + // TO-DO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag + // TO-DO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups + // TO-DO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly. /*qgen.Install.CreateTable("users_penalties","","", []qgen.DB_Table_Column{ qgen.DB_Table_Column{"uid","int",0,false,false,""}, qgen.DB_Table_Column{"element_id","int",0,false,false,""}, - qgen.DB_Table_Column{"element_type","varchar",50,false,false,""}, //global,forum,profile?,social_group - qgen.DB_Table_Column{"overrides","text",0,false,false,"{}"}, // Perm overrides. Coming Soon - qgen.DB_Table_Column{"mod_queue","boolean",0,false,false,"0"}, // All of this user's posts will go through the mod_queue. Coming Soon - // TO-DO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag - // TO-DO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups + qgen.DB_Table_Column{"element_type","varchar",50,false,false,""}, //forum, profile?, and social_group. Leave blank for global. + qgen.DB_Table_Column{"overrides","text",0,false,false,"{}"}, - qgen.DB_Table_Column{"shadow_ban","boolean",0,false,false,"0"}, // Coming Soon. CanShadowBan permission. + qgen.DB_Table_Column{"mod_queue","boolean",0,false,false,"0"}, + qgen.DB_Table_Column{"shadow_ban","boolean",0,false,false,"0"}, qgen.DB_Table_Column{"no_avatar","boolean",0,false,false,"0"}, // Coming Soon. Should this be a perm override instead? - //qgen.DB_Table_Column{"posts_per_hour","int",0,false,false,"0"}, // Rate-limit penalty type. Coming soon - //qgen.DB_Table_Column{"topics_per_hour","int",0,false,false,"0"}, // Coming Soon - - //qgen.DB_Table_Column{"posts_count","int",0,false,false,"0"}, // Coming soon - //qgen.DB_Table_Column{"topic_count","int",0,false,false,"0"}, // Coming Soon + // Do we *really* need rate-limit penalty types? Are we going to be allowing bots or something? + //qgen.DB_Table_Column{"posts_per_hour","int",0,false,false,"0"}, + //qgen.DB_Table_Column{"topics_per_hour","int",0,false,false,"0"}, + //qgen.DB_Table_Column{"posts_count","int",0,false,false,"0"}, + //qgen.DB_Table_Column{"topic_count","int",0,false,false,"0"}, //qgen.DB_Table_Column{"last_hour","int",0,false,false,"0"}, // UNIX Time, as we don't need to do anything too fancy here. When an hour has elapsed since that time, reset the hourly penalty counters. + qgen.DB_Table_Column{"issued_by","int",0,false,false,""}, qgen.DB_Table_Column{"issued_at","createdAt",0,false,false,""}, - qgen.DB_Table_Column{"expiry","duration",0,false,false,""}, // TO-DO: Implement the duration parsing code on the adapter side + qgen.DB_Table_Column{"expires_at","datetime",0,false,false,""}, }, []qgen.DB_Table_Key{}, )*/ + qgen.Install.CreateTable("users_groups_scheduler","","", + []qgen.DB_Table_Column{ + qgen.DB_Table_Column{"uid","int",0,false,false,""}, + qgen.DB_Table_Column{"set_group","int",0,false,false,""}, + + qgen.DB_Table_Column{"issued_by","int",0,false,false,""}, + qgen.DB_Table_Column{"issued_at","createdAt",0,false,false,""}, + qgen.DB_Table_Column{"revert_at","datetime",0,false,false,""}, + qgen.DB_Table_Column{"temporary","boolean",0,false,false,""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future + }, + []qgen.DB_Table_Key{ + qgen.DB_Table_Key{"uid","primary"}, + }, + ) + + qgen.Install.CreateTable("word_filters","","", + []qgen.DB_Table_Column{ + qgen.DB_Table_Column{"wfid","int",0,false,true,""}, + qgen.DB_Table_Column{"find","varchar",200,false,false,""}, + qgen.DB_Table_Column{"replacement","varchar",200,false,false,""}, + }, + []qgen.DB_Table_Key{ + qgen.DB_Table_Key{"wfid","primary"}, + }, + ) + return nil } @@ -194,6 +223,8 @@ func write_selects(adapter qgen.DB_Adapter) error { adapter.SimpleSelect("get_users_offset","users","uid, name, group, active, is_super_admin, avatar","","","?,?") + adapter.SimpleSelect("get_word_filters","word_filters","wfid, find, replacement","","","") + adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","","") adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","") @@ -212,12 +243,8 @@ func write_selects(adapter qgen.DB_Adapter) error { adapter.SimpleSelect("get_user_name","users","name","uid = ?","","") - adapter.SimpleSelect("get_user_rank","users","group, is_super_admin","uid = ?","","") - adapter.SimpleSelect("get_user_active","users","active","uid = ?","","") - adapter.SimpleSelect("get_user_group","users","group","uid = ?","","") - adapter.SimpleSelect("get_emails_by_user","emails","email, validated, token","uid = ?","","") adapter.SimpleSelect("get_topic_basic","topics","title, content","tid = ?","","") @@ -230,6 +257,8 @@ func write_selects(adapter qgen.DB_Adapter) error { adapter.SimpleSelect("get_forum_topics_offset","topics","tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount","parentID = ?","sticky DESC, lastReplyAt DESC, createdBy DESC","?,?") + adapter.SimpleSelect("get_expired_scheduled_groups","users_groups_scheduler","uid","UTC_TIMESTAMP() > revert_at AND temporary = 1","","") + return nil } @@ -293,12 +322,16 @@ func write_inserts(adapter qgen.DB_Adapter) error { adapter.SimpleInsert("add_adminlog_entry","administration_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,UTC_TIMESTAMP()") + adapter.SimpleInsert("create_word_filter","word_filters","find, replacement","?,?") + return nil } func write_replaces(adapter qgen.DB_Adapter) error { adapter.SimpleReplace("add_forum_perms_to_group","forums_permissions","gid,fid,preset,permissions","?,?,?,?") + adapter.SimpleReplace("replace_schedule_group","users_groups_scheduler","uid, set_group, issued_by, issued_at, revert_at, temporary","?,?,?,UTC_TIMESTAMP(),?,?") + return nil } @@ -376,7 +409,11 @@ func write_updates(adapter qgen.DB_Adapter) error { adapter.SimpleUpdate("update_email","emails","email = ?, uid = ?, validated = ?, token = ?","email = ?") - adapter.SimpleUpdate("verify_email","emails","validated = 1, token = ''","email = ?") // Need to fix this: Empty string isn't working, it gets set to 1 instead x.x + adapter.SimpleUpdate("verify_email","emails","validated = 1, token = ''","email = ?") // Need to fix this: Empty string isn't working, it gets set to 1 instead x.x -- Has this been fixed? + + adapter.SimpleUpdate("set_temp_group","users","temp_group = ?","uid = ?") + + adapter.SimpleUpdate("update_word_filter","word_filters","find = ?, replacement = ?","wfid = ?") return nil } @@ -392,9 +429,10 @@ func write_deletes(adapter qgen.DB_Adapter) error { adapter.SimpleDelete("delete_forum_perms_by_forum","forums_permissions","fid = ?") adapter.SimpleDelete("delete_activity_stream_match","activity_stream_matches","watcher = ? AND asid = ?") - //adapter.SimpleDelete("delete_activity_stream_matches_by_watcher","activity_stream_matches","watcher = ?") + adapter.SimpleDelete("delete_word_filter","word_filters","wfid = ?") + return nil } diff --git a/router_gen/routes.go b/router_gen/routes.go index c6974c7d..198ee08e 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -54,6 +54,12 @@ func routes() { Route{"route_panel_settings","/panel/settings/","",[]string{}}, Route{"route_panel_setting","/panel/settings/edit/","",[]string{"extra_data"}}, Route{"route_panel_setting_edit","/panel/settings/edit/submit/","",[]string{"extra_data"}}, + + Route{"route_panel_word_filters","/panel/settings/word-filters/","",[]string{}}, + Route{"route_panel_word_filters_create","/panel/settings/word-filters/create/","",[]string{}}, + Route{"route_panel_word_filters_edit","/panel/settings/word-filters/edit/","",[]string{"extra_data"}}, + Route{"route_panel_word_filters_edit_submit","/panel/settings/word-filters/edit/submit/","",[]string{"extra_data"}}, + Route{"route_panel_word_filters_delete_submit","/panel/settings/word-filters/delete/submit/","",[]string{"extra_data"}}, Route{"route_panel_themes","/panel/themes/","",[]string{}}, Route{"route_panel_themes_default","/panel/themes/default/","",[]string{"extra_data"}}, diff --git a/schema/mysql/query_users.sql b/schema/mysql/query_users.sql index b66643b4..676b167b 100644 --- a/schema/mysql/query_users.sql +++ b/schema/mysql/query_users.sql @@ -21,6 +21,7 @@ CREATE TABLE `users` ( `bigposts` int DEFAULT 0 not null, `megaposts` int DEFAULT 0 not null, `topics` int DEFAULT 0 not null, + `temp_group` int DEFAULT 0 not null, primary key(`uid`), unique(`name`) ) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; \ No newline at end of file diff --git a/schema/mysql/query_users_groups_scheduler.sql b/schema/mysql/query_users_groups_scheduler.sql new file mode 100644 index 00000000..255c8621 --- /dev/null +++ b/schema/mysql/query_users_groups_scheduler.sql @@ -0,0 +1,9 @@ +CREATE TABLE `users_groups_scheduler` ( + `uid` int not null, + `set_group` int not null, + `issued_by` int not null, + `issued_at` datetime not null, + `revert_at` datetime not null, + `temporary` boolean not null, + primary key(`uid`) +); \ No newline at end of file diff --git a/schema/mysql/query_word_filters.sql b/schema/mysql/query_word_filters.sql new file mode 100644 index 00000000..6acdbbe6 --- /dev/null +++ b/schema/mysql/query_word_filters.sql @@ -0,0 +1,6 @@ +CREATE TABLE `word_filters` ( + `wfid` int not null AUTO_INCREMENT, + `find` varchar(200) not null, + `replacement` varchar(200) not null, + primary key(`wfid`) +); \ No newline at end of file diff --git a/schema/pgsql/query_users.sql b/schema/pgsql/query_users.sql index a83cd243..7b938e68 100644 --- a/schema/pgsql/query_users.sql +++ b/schema/pgsql/query_users.sql @@ -21,6 +21,7 @@ CREATE TABLE `users` ( `bigposts` int DEFAULT 0 not null, `megaposts` int DEFAULT 0 not null, `topics` int DEFAULT 0 not null, + `temp_group` int DEFAULT 0 not null, primary key(`uid`), unique(`name`) ); \ No newline at end of file diff --git a/schema/pgsql/query_users_groups_scheduler.sql b/schema/pgsql/query_users_groups_scheduler.sql new file mode 100644 index 00000000..87e7d5bd --- /dev/null +++ b/schema/pgsql/query_users_groups_scheduler.sql @@ -0,0 +1,9 @@ +CREATE TABLE `users_groups_scheduler` ( + `uid` int not null, + `set_group` int not null, + `issued_by` int not null, + `issued_at` timestamp not null, + `revert_at` timestamp not null, + `temporary` boolean not null, + primary key(`uid`) +); \ No newline at end of file diff --git a/schema/pgsql/query_word_filters.sql b/schema/pgsql/query_word_filters.sql new file mode 100644 index 00000000..64595157 --- /dev/null +++ b/schema/pgsql/query_word_filters.sql @@ -0,0 +1,6 @@ +CREATE TABLE `word_filters` ( + `wfid` serial not null, + `find` varchar (200) not null, + `replacement` varchar (200) not null, + primary key(`wfid`) +); \ No newline at end of file diff --git a/tasks.go b/tasks.go new file mode 100644 index 00000000..ee6267b1 --- /dev/null +++ b/tasks.go @@ -0,0 +1,29 @@ +package main + +import "time" + +func handle_expired_scheduled_groups() error { + rows, err := get_expired_scheduled_groups_stmt.Query() + if err != nil { + return err + } + defer rows.Close() + + var uid int + for rows.Next() { + err := rows.Scan(&uid) + if err != nil { + return err + } + _, err = replace_schedule_group_stmt.Exec(uid, 0, 0, time.Now(), false) + if err != nil { + return err + } + _, err = set_temp_group_stmt.Exec(0,uid) + if err != nil { + return err + } + _ = users.Load(uid) + } + return rows.Err() +} diff --git a/template_init.go b/template_init.go new file mode 100644 index 00000000..a9be0742 --- /dev/null +++ b/template_init.go @@ -0,0 +1,244 @@ +package main + +import "log" +import "html/template" +import "net/http" + +var templates = template.New("") + +func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) { + mapping, ok := themes[defaultTheme].TemplatesMap["topic"] + if !ok { + mapping = "topic" + } + err := templates.ExecuteTemplate(w,mapping + ".html", pi) + if err != nil { + InternalError(err,w) + } +} + +var template_topic_handle func(TopicPage,http.ResponseWriter) = interpreted_topic_template +var template_topic_alt_handle func(TopicPage,http.ResponseWriter) = interpreted_topic_template + +var template_topics_handle func(TopicsPage,http.ResponseWriter) = func(pi TopicsPage, w http.ResponseWriter) { + mapping, ok := themes[defaultTheme].TemplatesMap["topics"] + if !ok { + mapping = "topics" + } + err := templates.ExecuteTemplate(w,mapping + ".html", pi) + if err != nil { + InternalError(err,w) + } +} + +var template_forum_handle func(ForumPage,http.ResponseWriter) = func(pi ForumPage, w http.ResponseWriter) { + mapping, ok := themes[defaultTheme].TemplatesMap["forum"] + if !ok { + mapping = "forum" + } + err := templates.ExecuteTemplate(w,mapping + ".html", pi) + if err != nil { + InternalError(err,w) + } +} + +var template_forums_handle func(ForumsPage,http.ResponseWriter) = func(pi ForumsPage, w http.ResponseWriter) { + mapping, ok := themes[defaultTheme].TemplatesMap["forums"] + if !ok { + mapping = "forums" + } + err := templates.ExecuteTemplate(w,mapping + ".html", pi) + if err != nil { + InternalError(err,w) + } +} + +var template_profile_handle func(ProfilePage,http.ResponseWriter) = func(pi ProfilePage, w http.ResponseWriter) { + mapping, ok := themes[defaultTheme].TemplatesMap["profile"] + if !ok { + mapping = "profile" + } + err := templates.ExecuteTemplate(w,mapping + ".html", pi) + if err != nil { + InternalError(err,w) + } +} + +var template_create_topic_handle func(CreateTopicPage,http.ResponseWriter) = func(pi CreateTopicPage, w http.ResponseWriter) { + mapping, ok := themes[defaultTheme].TemplatesMap["create-topic"] + if !ok { + mapping = "create-topic" + } + err := templates.ExecuteTemplate(w,mapping + ".html", pi) + if err != nil { + InternalError(err,w) + } +} + +func compile_templates() error { + var c CTemplateSet + user := User{62,build_profile_url("fake-user",62),"Fake User","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"0.0.0.0.0",0} + // TO-DO: Do a more accurate level calculation for this? + user2 := User{1,build_profile_url("admin-alice",1),"Admin Alice","alice@localhost",1,true,true,true,true,false,false,AllPerms,make(map[string]bool),"",true,"","","","","",58,1000,"127.0.0.1",0} + user3 := User{2,build_profile_url("admin-fred",62),"Admin Fred","fred@localhost",1,true,true,true,true,false,false,AllPerms,make(map[string]bool),"",true,"","","","","",42,900,"::1",0} + headerVars := HeaderVars{ + Site: site, + NoticeList: []string{"test"}, + Stylesheets: []string{"panel"}, + Scripts: []string{"whatever"}, + Widgets: PageWidgets{ + LeftSidebar: template.HTML("lalala"), + }, + } + + log.Print("Compiling the templates") + + topic := TopicUser{1,"blah","Blah","Hey there!",0,false,false,"Date","Date",0,"","127.0.0.1",0,1,"classname","weird-data",build_profile_url("fake-user",62),"Fake User",config.DefaultGroup,"",0,"","","","",58,false} + var replyList []Reply + replyList = append(replyList, Reply{0,0,"Yo!","Yo!",0,"alice","Alice",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""}) + + var varList map[string]VarItem = make(map[string]VarItem) + tpage := TopicPage{"Title",user,headerVars,replyList,topic,1,1,extData} + topic_id_tmpl, err := c.compile_template("topic.html","templates/","TopicPage", tpage, varList) + if err != nil { + return err + } + topic_id_alt_tmpl, err := c.compile_template("topic_alt.html","templates/","TopicPage", tpage, varList) + if err != nil { + return err + } + + varList = make(map[string]VarItem) + ppage := ProfilePage{"User 526",user,headerVars,replyList,user,extData} + profile_tmpl, err := c.compile_template("profile.html","templates/","ProfilePage", ppage, varList) + if err != nil { + return err + } + + var forumList []Forum + forums, err := fstore.GetAll() + if err != nil { + return err + } + + for _, forum := range forums { + if forum.Active { + forumList = append(forumList,*forum) + } + } + varList = make(map[string]VarItem) + forums_page := ForumsPage{"Forum List",user,headerVars,forumList,extData} + forums_tmpl, err := c.compile_template("forums.html","templates/","ForumsPage",forums_page,varList) + if err != nil { + return err + } + + var topicsList []*TopicsRow + topicsList = append(topicsList,&TopicsRow{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",user3.ID,1,"","127.0.0.1",0,1,"classname","",&user2,"",0,&user3,"General","/forum/general.2"}) + topics_page := TopicsPage{"Topic List",user,headerVars,topicsList,extData} + topics_tmpl, err := c.compile_template("topics.html","templates/","TopicsPage",topics_page,varList) + if err != nil { + return err + } + + //var topicList []TopicUser + //topicList = append(topicList,TopicUser{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","admin-fred","Admin Fred",config.DefaultGroup,"",0,"","","","",58,false}) + forum_item := Forum{1,"general","General Forum","Where the general stuff happens",true,"all",0,"",0,"","",0,"",0,""} + forum_page := ForumPage{"General Forum",user,headerVars,topicsList,forum_item,1,1,extData} + forum_tmpl, err := c.compile_template("forum.html","templates/","ForumPage",forum_page,varList) + if err != nil { + return err + } + + log.Print("Writing the templates") + go write_template("topic", topic_id_tmpl) + go write_template("topic_alt", topic_id_alt_tmpl) + go write_template("profile", profile_tmpl) + go write_template("forums", forums_tmpl) + go write_template("topics", topics_tmpl) + go write_template("forum", forum_tmpl) + go func() { + err := write_file("./template_list.go","package main\n\n" + c.FragOut) + if err != nil { + log.Fatal(err) + } + }() + + return nil +} + +func write_template(name string, content string) { + err := write_file("./template_" + name + ".go", content) + if err != nil { + log.Fatal(err) + } +} + +func init_templates() { + if dev.DebugMode { + log.Print("Initialising the template system") + } + compile_templates() + + // TO-DO: Add support for 64-bit integers + // TO-DO: Add support for floats + fmap := make(map[string]interface{}) + fmap["add"] = func(left interface{}, right interface{})interface{} { + var left_int int + var right_int int + switch left := left.(type) { + case uint, uint8, uint16, int, int32: left_int = left.(int) + } + switch right := right.(type) { + case uint, uint8, uint16, int, int32: right_int = right.(int) + } + return left_int + right_int + } + + fmap["subtract"] = func(left interface{}, right interface{})interface{} { + var left_int int + var right_int int + switch left := left.(type) { + case uint, uint8, uint16, int, int32: left_int = left.(int) + } + switch right := right.(type) { + case uint, uint8, uint16, int, int32: right_int = right.(int) + } + return left_int - right_int + } + + fmap["multiply"] = func(left interface{}, right interface{})interface{} { + var left_int int + var right_int int + switch left := left.(type) { + case uint, uint8, uint16, int, int32: left_int = left.(int) + } + switch right := right.(type) { + case uint, uint8, uint16, int, int32: right_int = right.(int) + } + return left_int * right_int + } + + fmap["divide"] = func(left interface{}, right interface{})interface{} { + var left_int int + var right_int int + switch left := left.(type) { + case uint, uint8, uint16, int, int32: left_int = left.(int) + } + switch right := right.(type) { + case uint, uint8, uint16, int, int32: right_int = right.(int) + } + if left_int == 0 || right_int == 0 { + return 0 + } + return left_int / right_int + } + + // The interpreted templates... + if dev.DebugMode { + log.Print("Loading the template files...") + } + templates.Funcs(fmap) + template.Must(templates.ParseGlob("templates/*")) + template.Must(templates.ParseGlob("pages/*")) +} diff --git a/template_list.go b/template_list.go index ff2ad7e3..40ee1620 100644 --- a/template_list.go +++ b/template_list.go @@ -34,7 +34,7 @@ var menu_0 []byte = []byte(`
- +

{{.Name}} Group

@@ -36,7 +36,7 @@
- +

Extended Permissions

{{if .CurrentUser.Perms.EditGroupGlobalPerms}} diff --git a/templates/panel-group-edit.html b/templates/panel-group-edit.html index 594b8e41..d3d4bd94 100644 --- a/templates/panel-group-edit.html +++ b/templates/panel-group-edit.html @@ -12,7 +12,7 @@
- +

{{.Name}} Group

diff --git a/templates/panel-groups.html b/templates/panel-groups.html index 4b5045e2..baf1af31 100644 --- a/templates/panel-groups.html +++ b/templates/panel-groups.html @@ -3,7 +3,7 @@
- +

Groups

{{range .ItemList}} @@ -28,7 +28,7 @@
{{end}}
- +

Create Group

diff --git a/templates/panel-inner-menu.html b/templates/panel-inner-menu.html index db1459fd..1ce38239 100644 --- a/templates/panel-inner-menu.html +++ b/templates/panel-inner-menu.html @@ -13,6 +13,9 @@
{{end}} {{if .CurrentUser.Perms.EditSettings}} + {{end}} {{if .CurrentUser.Perms.ManageThemes}}
Themes ({{.Stats.Themes}}) diff --git a/templates/panel-modlogs.html b/templates/panel-modlogs.html index b6482b2c..0e9f983f 100644 --- a/templates/panel-modlogs.html +++ b/templates/panel-modlogs.html @@ -11,7 +11,7 @@
- +

Moderation Logs

{{range .Logs}} diff --git a/templates/panel-plugins.html b/templates/panel-plugins.html index e83c0f2e..91358e84 100644 --- a/templates/panel-plugins.html +++ b/templates/panel-plugins.html @@ -2,7 +2,7 @@ {{template "panel-menu.html" . }}
- +

Plugins

{{range .ItemList}} diff --git a/templates/panel-setting.html b/templates/panel-setting.html index 17e59c92..81a48fc9 100644 --- a/templates/panel-setting.html +++ b/templates/panel-setting.html @@ -2,7 +2,7 @@ {{template "panel-menu.html" . }}
- +

Edit Setting

diff --git a/templates/panel-settings.html b/templates/panel-settings.html index 2291a7bf..d46eeb80 100644 --- a/templates/panel-settings.html +++ b/templates/panel-settings.html @@ -2,7 +2,7 @@ {{template "panel-menu.html" . }}
- +

Settings

{{range $key, $value := .Something}} diff --git a/templates/panel-themes.html b/templates/panel-themes.html index 00d2cbfa..2c6d195a 100644 --- a/templates/panel-themes.html +++ b/templates/panel-themes.html @@ -20,7 +20,7 @@
- +

Primary Themes

{{range .PrimaryThemes}} @@ -38,7 +38,7 @@ {{end}}
- +

Variant Themes

{{range .VariantThemes}} diff --git a/templates/panel-user-edit.html b/templates/panel-user-edit.html index 371b3d40..37e028f4 100644 --- a/templates/panel-user-edit.html +++ b/templates/panel-user-edit.html @@ -2,7 +2,7 @@ {{template "panel-menu.html" . }}
- +

User Editor

diff --git a/templates/panel-users.html b/templates/panel-users.html index bde330cb..dd879423 100644 --- a/templates/panel-users.html +++ b/templates/panel-users.html @@ -3,7 +3,7 @@
- +

Users

{{range .ItemList}} @@ -13,7 +13,7 @@ {{if (.Tag) and (.Is_Super_Mod)}}{{.Tag}}{{end}} - {{if .Is_Banned}}Unban{{else if not .Is_Super_Mod}}Ban{{end}} + {{if .Is_Banned}}Unban{{else if not .Is_Super_Mod}}Ban{{end}} {{if not .Active}}Activate{{end}}
diff --git a/templates/panel-word-filters.html b/templates/panel-word-filters.html new file mode 100644 index 00000000..7fac2a91 --- /dev/null +++ b/templates/panel-word-filters.html @@ -0,0 +1,45 @@ +{{template "header.html" . }} +{{template "panel-menu.html" . }} +
+
+

Word Filters

+
+
+ {{range .Something}} + + {{else}} + + {{end}} +
+ +
+

Add Filter

+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ +
+
+{{template "footer.html" . }} diff --git a/templates/profile.html b/templates/profile.html index 21adeaf8..08b16332 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -1,9 +1,9 @@ {{template "header.html" . }}
- +
@@ -16,7 +16,7 @@
{{if (.CurrentUser.Is_Super_Mod) and not (.ProfileOwner.Is_Super_Mod) }}
{{if .ProfileOwner.Is_Banned }}Unban - {{else}}Ban{{end}} + {{else}}Ban{{end}}
{{end}}
Report @@ -25,10 +25,48 @@
-
- + {{if .CurrentUser.Perms.BanUsers}} + + -
{{range .ItemList}} + + {{end}} + +
+

Comments

+
+
{{range .ItemList}}
{{.ContentHtml}} @@ -47,7 +85,7 @@ {{end}}
{{if not .CurrentUser.Is_Banned}} -
+
@@ -61,4 +99,24 @@ {{end}}
+{{/** Quick subpage switcher **/}} +{{/** TO-DO: Stop inlining this **/}} + + {{template "footer.html" . }} diff --git a/templates/register.html b/templates/register.html index aabe2b0c..247ad396 100644 --- a/templates/register.html +++ b/templates/register.html @@ -2,25 +2,25 @@
- +

Create Account

- -
+ +
- -
+ +
diff --git a/templates/socialgroups_create_group.html b/templates/socialgroups_create_group.html index 8473d0b0..00ae83ad 100644 --- a/templates/socialgroups_create_group.html +++ b/templates/socialgroups_create_group.html @@ -2,7 +2,7 @@
- +

Create Group

diff --git a/templates/topic.html b/templates/topic.html index 6c4d2fd4..5b06e9bc 100644 --- a/templates/topic.html +++ b/templates/topic.html @@ -1,18 +1,18 @@ {{template "header.html" . }} -{{if gt .Page 1}}{{end}} +{{if gt .Page 1}}{{end}} {{if ne .LastPage .Page}}
- > +
{{end}}
- {{.Topic.Title}} +

{{.Topic.Title}}

{{if .Topic.Is_Closed}}🔒︎{{end}} {{if .CurrentUser.Perms.EditTopic}} diff --git a/templates/topic_alt.html b/templates/topic_alt.html index 51bdd349..17862501 100644 --- a/templates/topic_alt.html +++ b/templates/topic_alt.html @@ -1,14 +1,14 @@ {{template "header.html" . }} -{{if gt .Page 1}}{{end}} +{{if gt .Page 1}}{{end}} {{if ne .LastPage .Page}} -{{end}} +{{end}}
- {{.Topic.Title}} +

{{.Topic.Title}}

{{if .Topic.Is_Closed}}🔒︎{{end}} {{if .CurrentUser.Perms.EditTopic}} diff --git a/templates/topics.html b/templates/topics.html index 41409002..ca4485ba 100644 --- a/templates/topics.html +++ b/templates/topics.html @@ -2,7 +2,7 @@
- +

Topic List

{{range .ItemList}}
diff --git a/templates/widget_menu.html b/templates/widget_menu.html index d2a11507..7ea1ca86 100644 --- a/templates/widget_menu.html +++ b/templates/widget_menu.html @@ -1,5 +1,5 @@
-
{{.Name}}
+

{{.Name}}