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(` - `) + `) var menu_1 []byte = []byte(` Forums Topics @@ -77,14 +77,14 @@ var topic_0 []byte = []byte(` `) -var topic_2 []byte = []byte(`<`) var topic_5 []byte = []byte(` - > `) @@ -97,8 +97,8 @@ var topic_10 []byte = []byte(` var topic_11 []byte = []byte(` topic_sticky_head`) var topic_12 []byte = []byte(` topic_closed_head`) var topic_13 []byte = []byte(`"> - `) -var topic_14 []byte = []byte(` + `) +var topic_14 []byte = []byte(` `) var topic_15 []byte = []byte(`🔒︎`) var topic_16 []byte = []byte(` @@ -240,8 +240,8 @@ var topic_87 []byte = []byte(` `) var footer_0 []byte = []byte(` `) -var footer_1 []byte = []byte(``) -var footer_2 []byte = []byte(``) +var footer_1 []byte = []byte(``) var footer_3 []byte = []byte(` @@ -249,13 +249,13 @@ var footer_3 []byte = []byte(`