diff --git a/extend.go b/extend.go index 921f5dea..563e561c 100644 --- a/extend.go +++ b/extend.go @@ -49,7 +49,7 @@ type Plugin struct Init func() Activate func()error Deactivate func() - + Hooks map[string]int } @@ -65,7 +65,7 @@ func NewPlugin(uname string, name string, author string, url string, settings st Init: init, Activate: activate, Deactivate: deactivate, - + /* The Active field should never be altered by a plugin. It's used internally by the software to determine whether an admin has enabled a plugin or not and whether to run it. This will be overwritten by the user's preference. */ @@ -118,7 +118,11 @@ func init_plugins() { log.Print("Added plugin " + name) if body.Active { log.Print("Initialised plugin " + name) - plugins[name].Init() + if plugins[name].Init != nil { + plugins[name].Init() + } else { + log.Print("Plugin " + name + " doesn't have an initialiser.") + } } } plugins_inited = true @@ -137,4 +141,4 @@ func run_vhook(name string, data ...interface{}) interface{} { func run_vhook_noreturn(name string, data ...interface{}) { _ = vhooks[name](data...) -} \ No newline at end of file +} diff --git a/forum_store.go b/forum_store.go index f473373c..2fc50f05 100644 --- a/forum_store.go +++ b/forum_store.go @@ -6,6 +6,10 @@ import "errors" import "database/sql" import "./query_gen/lib" +var forums []Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency +var forum_perms map[int]map[int]ForumPerms // [gid][fid]Perms +var fstore ForumStore // :soon: +var forumCapCount int var err_noforum = errors.New("This forum doesn't exist") type ForumStore interface diff --git a/gen_mysql.go b/gen_mysql.go index 1959f65b..cad0d5e7 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -21,6 +21,7 @@ var get_forums_stmt *sql.Stmt var get_forums_permissions_stmt *sql.Stmt var get_plugins_stmt *sql.Stmt var get_themes_stmt *sql.Stmt +var get_widgets_stmt *sql.Stmt var is_plugin_active_stmt *sql.Stmt var get_users_stmt *sql.Stmt var is_theme_default_stmt *sql.Stmt @@ -109,6 +110,8 @@ var delete_topic_stmt *sql.Stmt var delete_profile_reply_stmt *sql.Stmt var delete_forum_perms_by_forum_stmt *sql.Stmt var report_exists_stmt *sql.Stmt +var add_forum_perms_to_forum_admins_stmt *sql.Stmt +var notify_watchers_stmt *sql.Stmt func gen_mysql() (err error) { if debug { @@ -205,6 +208,12 @@ func gen_mysql() (err error) { return err } + log.Print("Preparing get_widgets statement.") + get_widgets_stmt, err = db.Prepare("SELECT `position`,`side`,`type`,`active`,`location`,`data` FROM `widgets` ORDER BY position ASC") + if err != nil { + return err + } + log.Print("Preparing is_plugin_active statement.") is_plugin_active_stmt, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?") if err != nil { @@ -248,13 +257,13 @@ func gen_mysql() (err error) { } log.Print("Preparing has_liked_topic statement.") - has_liked_topic_stmt, err = db.Prepare("SELECT `targetItem` FROM `likes` WHERE `sentBy` = ? AND `targetItem` = ? AND `targetType` = 'topics'") + has_liked_topic_stmt, err = db.Prepare("SELECT `targetItem` FROM `likes` WHERE `sentBy` = ? AND `targetItem` = ? AND `targetType` = 'topics'") if err != nil { return err } log.Print("Preparing has_liked_reply statement.") - has_liked_reply_stmt, err = db.Prepare("SELECT `targetItem` FROM `likes` WHERE `sentBy` = ? AND `targetItem` = ? AND `targetType` = 'replies'") + has_liked_reply_stmt, err = db.Prepare("SELECT `targetItem` FROM `likes` WHERE `sentBy` = ? AND `targetItem` = ? AND `targetType` = 'replies'") if err != nil { return err } @@ -302,25 +311,25 @@ func gen_mysql() (err error) { } log.Print("Preparing forum_entry_exists statement.") - forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' ORDER BY fid ASC LIMIT 0,1") + forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' ORDER BY fid ASC LIMIT 0,1") if err != nil { return err } log.Print("Preparing group_entry_exists statement.") - group_entry_exists_stmt, err = db.Prepare("SELECT `gid` FROM `users_groups` WHERE `name` = '' ORDER BY gid ASC LIMIT 0,1") + group_entry_exists_stmt, err = db.Prepare("SELECT `gid` FROM `users_groups` WHERE `name` = '' ORDER BY gid ASC LIMIT 0,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 ?,?") + 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 { return err } log.Print("Preparing get_forum_topics_offset statement.") - get_forum_topics_offset_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`topics`.`postCount`,`topics`.`likeCount`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC LIMIT ?,?") + get_forum_topics_offset_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`topics`.`postCount`,`topics`.`likeCount`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC LIMIT ?,?") if err != nil { return err } @@ -350,7 +359,7 @@ func gen_mysql() (err error) { } log.Print("Preparing get_forum_topics statement.") - get_forum_topics_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC") + get_forum_topics_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC") if err != nil { return err } @@ -482,223 +491,223 @@ func gen_mysql() (err error) { } log.Print("Preparing add_replies_to_topic statement.") - add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyAt` = NOW() WHERE `tid` = ? ") + add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyAt` = NOW() WHERE `tid` = ?") if err != nil { return err } log.Print("Preparing remove_replies_from_topic statement.") - remove_replies_from_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` - ? WHERE `tid` = ? ") + remove_replies_from_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` - ? WHERE `tid` = ?") if err != nil { return err } log.Print("Preparing add_topics_to_forum statement.") - add_topics_to_forum_stmt, err = db.Prepare("UPDATE `forums` SET `topicCount` = `topicCount` + ? WHERE `fid` = ? ") + add_topics_to_forum_stmt, err = db.Prepare("UPDATE `forums` SET `topicCount` = `topicCount` + ? WHERE `fid` = ?") if err != nil { return err } log.Print("Preparing remove_topics_from_forum statement.") - remove_topics_from_forum_stmt, err = db.Prepare("UPDATE `forums` SET `topicCount` = `topicCount` - ? WHERE `fid` = ? ") + remove_topics_from_forum_stmt, err = db.Prepare("UPDATE `forums` SET `topicCount` = `topicCount` - ? WHERE `fid` = ?") if err != nil { return err } log.Print("Preparing update_forum_cache statement.") - update_forum_cache_stmt, err = db.Prepare("UPDATE `forums` SET `lastTopic` = ?,`lastTopicID` = ?,`lastReplyer` = ?,`lastReplyerID` = ?,`lastTopicTime` = NOW() WHERE `fid` = ? ") + update_forum_cache_stmt, err = db.Prepare("UPDATE `forums` SET `lastTopic` = ?,`lastTopicID` = ?,`lastReplyer` = ?,`lastReplyerID` = ?,`lastTopicTime` = NOW() WHERE `fid` = ?") if err != nil { return err } log.Print("Preparing add_likes_to_topic statement.") - add_likes_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `likeCount` = `likeCount` + ? WHERE `tid` = ? ") + add_likes_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `likeCount` = `likeCount` + ? WHERE `tid` = ?") if err != nil { return err } log.Print("Preparing add_likes_to_reply statement.") - add_likes_to_reply_stmt, err = db.Prepare("UPDATE `replies` SET `likeCount` = `likeCount` + ? WHERE `rid` = ? ") + add_likes_to_reply_stmt, err = db.Prepare("UPDATE `replies` SET `likeCount` = `likeCount` + ? WHERE `rid` = ?") if err != nil { return err } log.Print("Preparing edit_topic statement.") - edit_topic_stmt, err = db.Prepare("UPDATE `topics` SET `title` = ?,`content` = ?,`parsed_content` = ?,`is_closed` = ? WHERE `tid` = ? ") + edit_topic_stmt, err = db.Prepare("UPDATE `topics` SET `title` = ?,`content` = ?,`parsed_content` = ?,`is_closed` = ? WHERE `tid` = ?") if err != nil { return err } log.Print("Preparing edit_reply statement.") - edit_reply_stmt, err = db.Prepare("UPDATE `replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ? ") + edit_reply_stmt, err = db.Prepare("UPDATE `replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ?") if err != nil { return err } log.Print("Preparing stick_topic statement.") - stick_topic_stmt, err = db.Prepare("UPDATE `topics` SET `sticky` = 1 WHERE `tid` = ? ") + stick_topic_stmt, err = db.Prepare("UPDATE `topics` SET `sticky` = 1 WHERE `tid` = ?") if err != nil { return err } log.Print("Preparing unstick_topic statement.") - unstick_topic_stmt, err = db.Prepare("UPDATE `topics` SET `sticky` = 0 WHERE `tid` = ? ") + unstick_topic_stmt, err = db.Prepare("UPDATE `topics` SET `sticky` = 0 WHERE `tid` = ?") if err != nil { return err } log.Print("Preparing update_last_ip statement.") - update_last_ip_stmt, err = db.Prepare("UPDATE `users` SET `last_ip` = ? WHERE `uid` = ? ") + update_last_ip_stmt, err = db.Prepare("UPDATE `users` SET `last_ip` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing update_session statement.") - update_session_stmt, err = db.Prepare("UPDATE `users` SET `session` = ? WHERE `uid` = ? ") + update_session_stmt, err = db.Prepare("UPDATE `users` SET `session` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing logout statement.") - logout_stmt, err = db.Prepare("UPDATE `users` SET `session` = '' WHERE `uid` = ? ") + logout_stmt, err = db.Prepare("UPDATE `users` SET `session` = '' WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing set_password statement.") - set_password_stmt, err = db.Prepare("UPDATE `users` SET `password` = ?,`salt` = ? WHERE `uid` = ? ") + set_password_stmt, err = db.Prepare("UPDATE `users` SET `password` = ?,`salt` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing set_avatar statement.") - set_avatar_stmt, err = db.Prepare("UPDATE `users` SET `avatar` = ? WHERE `uid` = ? ") + set_avatar_stmt, err = db.Prepare("UPDATE `users` SET `avatar` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing set_username statement.") - set_username_stmt, err = db.Prepare("UPDATE `users` SET `name` = ? WHERE `uid` = ? ") + set_username_stmt, err = db.Prepare("UPDATE `users` SET `name` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing change_group statement.") - change_group_stmt, err = db.Prepare("UPDATE `users` SET `group` = ? WHERE `uid` = ? ") + change_group_stmt, err = db.Prepare("UPDATE `users` SET `group` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing activate_user statement.") - activate_user_stmt, err = db.Prepare("UPDATE `users` SET `active` = 1 WHERE `uid` = ? ") + activate_user_stmt, err = db.Prepare("UPDATE `users` SET `active` = 1 WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing update_user_level statement.") - update_user_level_stmt, err = db.Prepare("UPDATE `users` SET `level` = ? WHERE `uid` = ? ") + update_user_level_stmt, err = db.Prepare("UPDATE `users` SET `level` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing increment_user_score statement.") - increment_user_score_stmt, err = db.Prepare("UPDATE `users` SET `score` = `score` + ? WHERE `uid` = ? ") + increment_user_score_stmt, err = db.Prepare("UPDATE `users` SET `score` = `score` + ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing increment_user_posts statement.") - increment_user_posts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ? WHERE `uid` = ? ") + increment_user_posts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing increment_user_bigposts statement.") - increment_user_bigposts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ?,`bigposts` = `bigposts` + ? WHERE `uid` = ? ") + increment_user_bigposts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ?,`bigposts` = `bigposts` + ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing increment_user_megaposts statement.") - increment_user_megaposts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ?,`bigposts` = `bigposts` + ?,`megaposts` = `megaposts` + ? WHERE `uid` = ? ") + increment_user_megaposts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ?,`bigposts` = `bigposts` + ?,`megaposts` = `megaposts` + ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing increment_user_topics statement.") - increment_user_topics_stmt, err = db.Prepare("UPDATE `users` SET `topics` = `topics` + ? WHERE `uid` = ? ") + increment_user_topics_stmt, err = db.Prepare("UPDATE `users` SET `topics` = `topics` + ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing edit_profile_reply statement.") - edit_profile_reply_stmt, err = db.Prepare("UPDATE `users_replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ? ") + edit_profile_reply_stmt, err = db.Prepare("UPDATE `users_replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ?") if err != nil { return err } log.Print("Preparing delete_forum statement.") - delete_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = '',`active` = 0 WHERE `fid` = ? ") + delete_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = '',`active` = 0 WHERE `fid` = ?") if err != nil { return err } log.Print("Preparing update_forum statement.") - update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ? ") + update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?") if err != nil { return err } log.Print("Preparing update_setting statement.") - update_setting_stmt, err = db.Prepare("UPDATE `settings` SET `content` = ? WHERE `name` = ? ") + update_setting_stmt, err = db.Prepare("UPDATE `settings` SET `content` = ? WHERE `name` = ?") if err != nil { return err } log.Print("Preparing update_plugin statement.") - update_plugin_stmt, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ? ") + update_plugin_stmt, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?") if err != nil { return err } log.Print("Preparing update_theme statement.") - update_theme_stmt, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ? ") + update_theme_stmt, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ?") if err != nil { return err } log.Print("Preparing update_user statement.") - update_user_stmt, err = db.Prepare("UPDATE `users` SET `name` = ?,`email` = ?,`group` = ? WHERE `uid` = ? ") + update_user_stmt, err = db.Prepare("UPDATE `users` SET `name` = ?,`email` = ?,`group` = ? WHERE `uid` = ?") if err != nil { return err } log.Print("Preparing update_group_perms statement.") - update_group_perms_stmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ? ") + update_group_perms_stmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ?") if err != nil { return err } log.Print("Preparing update_group_rank statement.") - update_group_rank_stmt, err = db.Prepare("UPDATE `users_groups` SET `is_admin` = ?,`is_mod` = ?,`is_banned` = ? WHERE `gid` = ? ") + update_group_rank_stmt, err = db.Prepare("UPDATE `users_groups` SET `is_admin` = ?,`is_mod` = ?,`is_banned` = ? WHERE `gid` = ?") if err != nil { return err } log.Print("Preparing update_group statement.") - update_group_stmt, err = db.Prepare("UPDATE `users_groups` SET `name` = ?,`tag` = ? WHERE `gid` = ? ") + update_group_stmt, err = db.Prepare("UPDATE `users_groups` SET `name` = ?,`tag` = ? WHERE `gid` = ?") if err != nil { return err } log.Print("Preparing update_email statement.") - update_email_stmt, err = db.Prepare("UPDATE `emails` SET `email` = ?,`uid` = ?,`validated` = ?,`token` = ? WHERE `email` = ? ") + update_email_stmt, err = db.Prepare("UPDATE `emails` SET `email` = ?,`uid` = ?,`validated` = ?,`token` = ? WHERE `email` = ?") if err != nil { return err } log.Print("Preparing verify_email statement.") - verify_email_stmt, err = db.Prepare("UPDATE `emails` SET `validated` = 1,`token` = '1' WHERE `email` = ? ") + verify_email_stmt, err = db.Prepare("UPDATE `emails` SET `validated` = 1,`token` = '' WHERE `email` = ?") if err != nil { return err } @@ -728,7 +737,19 @@ func gen_mysql() (err error) { } 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") + report_exists_stmt, err = db.Prepare("SELECT COUNT(*) AS `count` FROM `topics` WHERE `data` = ? AND `data` != '' AND `parentID` = 1") + if err != nil { + return err + } + + log.Print("Preparing add_forum_perms_to_forum_admins statement.") + add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) SELECT `gid`, ? AS `fid`, ? AS `preset`, ? AS `permissions` FROM `users_groups` WHERE `is_admin` = 1") + if err != nil { + return err + } + + log.Print("Preparing notify_watchers statement.") + notify_watchers_stmt, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?") if err != nil { return err } diff --git a/main.go b/main.go index dfeb099b..b12bc378 100644 --- a/main.go +++ b/main.go @@ -36,15 +36,12 @@ var enable_websockets bool = false // Don't change this, the value is overwritte var startTime time.Time var timeLocation *time.Location var templates = template.New("") -var no_css_tmpl = template.CSS("") -var staff_css_tmpl = template.CSS(staff_css) +var no_css_tmpl template.CSS = template.CSS("") +var staff_css_tmpl template.CSS = template.CSS(staff_css) var settings map[string]interface{} = make(map[string]interface{}) var external_sites map[string]string = make(map[string]string) var groups []Group -var forums []Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency -var forum_perms map[int]map[int]ForumPerms // [gid][fid]Perms -var fstore ForumStore // :soon: -var groupCapCount, forumCapCount int +var groupCapCount int var static_files map[string]SFile = make(map[string]SFile) var template_topic_handle func(TopicPage,io.Writer) = nil @@ -62,6 +59,9 @@ func compile_templates() { NoticeList:[]string{"test"}, Stylesheets:[]string{"panel"}, Scripts:[]string{"whatever"}, + Sidebars:HeaderSidebars{ + Left: template.HTML("lalala"), + }, } log.Print("Compiling the templates") @@ -87,18 +87,18 @@ func compile_templates() { } varList = make(map[string]VarItem) forums_page := ForumsPage{"Forum List",user,headerVars,forumList,extData} - forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage", forums_page, varList) + forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage",forums_page,varList) var topicsList []TopicsRow topicsList = append(topicsList,TopicsRow{1,"Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","Admin","","",0,"","","","",58,"General"}) topics_page := TopicsPage{"Topic List",user,headerVars,topicsList,extData} - topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) + topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage",topics_page,varList) var topicList []TopicUser topicList = append(topicList,TopicUser{1,"Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","Admin",default_group,"","",0,"","","","",58,false}) forum_item := Forum{1,"General Forum","Where the general stuff happens",true,"all",0,"",0,"",0,""} forum_page := ForumPage{"General Forum",user,headerVars,topicList,forum_item,1,1,extData} - forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList) + forum_tmpl := c.compile_template("forum.html","templates/","ForumPage",forum_page,varList) log.Print("Writing the templates") go write_template("topic", topic_id_tmpl) @@ -209,6 +209,13 @@ func main(){ hooks["rrow_assign"] = nil init_plugins() + log.Print("Initialising the widgets") + err = init_widgets() + if err != nil { + log.Fatal(err) + } + + log.Print("Initialising the router") router := NewGenRouter(http.FileServer(http.Dir("./uploads"))) ///router.HandleFunc("/static/", route_static) ///router.HandleFunc("/overview/", route_overview) @@ -300,6 +307,7 @@ func main(){ // pprof.StopCPUProfile() //} + log.Print("Initialising the HTTP server") if !enable_ssl { if server_port == "" { server_port = "80" diff --git a/mysql.go b/mysql.go index 892c0860..0e0a8d28 100644 --- a/mysql.go +++ b/mysql.go @@ -3,15 +3,12 @@ package main import "log" -import "strings" import "database/sql" import _ "github.com/go-sql-driver/mysql" import "./query_gen/lib" -var notify_watchers_stmt *sql.Stmt var get_activity_feed_by_watcher_stmt *sql.Stmt var get_activity_count_by_watcher_stmt *sql.Stmt -var add_forum_perms_to_forum_admins_stmt *sql.Stmt var add_forum_perms_to_forum_staff_stmt *sql.Stmt var add_forum_perms_to_forum_members_stmt *sql.Stmt var update_forum_perms_for_group_stmt *sql.Stmt @@ -56,12 +53,6 @@ func _init_database() (err error) { return err } - log.Print("Preparing notify_watchers statement.") - notify_watchers_stmt, err = db.Prepare("INSERT INTO activity_stream_matches(watcher, asid) SELECT activity_subscriptions.user, activity_stream.asid FROM activity_stream INNER JOIN activity_subscriptions ON activity_subscriptions.targetType = activity_stream.elementType and activity_subscriptions.targetID = activity_stream.elementID and activity_subscriptions.user != activity_stream.actor where asid = ?") - if err != nil { - return err - } - log.Print("Preparing get_activity_feed_by_watcher statement.") get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8") if err != nil { @@ -74,12 +65,6 @@ func _init_database() (err error) { return err } - log.Print("Preparing add_forum_perms_to_forum_admins statement.") - add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 1") - if err != nil { - return err - } - log.Print("Preparing add_forum_perms_to_forum_staff statement.") add_forum_perms_to_forum_staff_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 0 AND is_mod = 1") if err != nil { @@ -118,8 +103,3 @@ func _init_database() (err error) { return nil } - -// Temporary hack so that we can move all the raw queries out of the other files and into here -func topic_list_query(visible_fids []string) string { - return "select topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar from topics left join users ON topics.createdBy = users.uid where parentID in("+strings.Join(visible_fids,",")+") order by topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC" -} diff --git a/mysql.sql b/mysql.sql index 718c8a41..a6453283 100644 --- a/mysql.sql +++ b/mysql.sql @@ -174,6 +174,15 @@ CREATE TABLE `themes`( unique(`uname`) ); +CREATE TABLE `widgets`( + `position` int not null, + `side` varchar(100) not null, + `type` varchar(100) not null, + `active` tinyint(1) DEFAULT 0 not null, + `location` varchar(100) not null, + `data` text DEFAULT '' not null +); + CREATE TABLE `moderation_logs`( `action` varchar(100) not null, `elementID` int not null, diff --git a/pages.go b/pages.go index 5df8bdaf..06faaf84 100644 --- a/pages.go +++ b/pages.go @@ -13,6 +13,13 @@ type HeaderVars struct NoticeList []string Scripts []string Stylesheets []string + Sidebars HeaderSidebars +} + +type HeaderSidebars struct +{ + Left template.HTML + Right template.HTML } type ExtData struct diff --git a/query_gen/lib/builder.go b/query_gen/lib/builder.go index 372e9860..a5f777ef 100644 --- a/query_gen/lib/builder.go +++ b/query_gen/lib/builder.go @@ -1,5 +1,7 @@ /* WIP Under Construction */ package qgen + +//import "fmt" import "database/sql" var Builder *builder @@ -36,6 +38,24 @@ func (build *builder) SimpleSelect(table string, columns string, where string, o return build.conn.Prepare(res) } +func (build *builder) SimpleLeftJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) { + res, err := build.adapter.SimpleLeftJoin("_builder", table1, table2, columns, joiners, where, orderby, limit) + if err != nil { + return stmt, err + } + //fmt.Println("res",res) + return build.conn.Prepare(res) +} + +func (build *builder) SimpleInnerJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) { + res, err := build.adapter.SimpleInnerJoin("_builder", table1, table2, columns, joiners, where, orderby, limit) + if err != nil { + return stmt, err + } + //fmt.Println("res",res) + return build.conn.Prepare(res) +} + func (build *builder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) { res, err := build.adapter.SimpleInsert("_builder", table, columns, fields) if err != nil { diff --git a/query_gen/lib/mysql.go b/query_gen/lib/mysql.go index 97be2b62..4dc8fc2e 100644 --- a/query_gen/lib/mysql.go +++ b/query_gen/lib/mysql.go @@ -135,24 +135,23 @@ func (adapter *Mysql_Adapter) SimpleUpdate(name string, table string, set string // Remove the trailing comma querystr = querystr[0:len(querystr) - 1] + // Add support for BETWEEN x.x if len(where) != 0 { querystr += " WHERE" for _, loc := range _process_where(where) { - var left, right string - - if loc.LeftType == "column" { - left = "`" + loc.LeftColumn + "`" - } else { - left = loc.LeftColumn + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + querystr += " `" + token.Contents + "`" + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } } - - if loc.RightType == "column" { - right = "`" + loc.RightColumn + "`" - } else { - right = loc.RightColumn - } - - querystr += " " + left + " " + loc.Operator + " " + right + " AND " + querystr += " AND" } querystr = querystr[0:len(querystr) - 4] } @@ -173,22 +172,22 @@ func (adapter *Mysql_Adapter) SimpleDelete(name string, table string, where stri } var querystr string = "DELETE FROM `" + table + "` WHERE" + + // Add support for BETWEEN x.x for _, loc := range _process_where(where) { - var left, right string - - if loc.LeftType == "column" { - left = "`" + loc.LeftColumn + "`" - } else { - left = loc.LeftColumn + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + querystr += " `" + token.Contents + "`" + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } } - - if loc.RightType == "column" { - right = "`" + loc.RightColumn + "`" - } else { - right = loc.RightColumn - } - - querystr += " " + left + " " + loc.Operator + " " + right + " AND " + querystr += " AND" } querystr = strings.TrimSpace(querystr[0:len(querystr) - 4]) @@ -232,24 +231,24 @@ func (adapter *Mysql_Adapter) SimpleSelect(name string, table string, columns st querystr = querystr[0:len(querystr) - 1] querystr += " FROM `" + table + "`" + + // Add support for BETWEEN x.x if len(where) != 0 { querystr += " WHERE" for _, loc := range _process_where(where) { - var left, right string - - if loc.LeftType == "column" { - left = "`" + loc.LeftColumn + "`" - } else { - left = loc.LeftColumn + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + querystr += " `" + token.Contents + "`" + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } } - - if loc.RightType == "column" { - right = "`" + loc.RightColumn + "`" - } else { - right = loc.RightColumn - } - - querystr += " " + left + " " + loc.Operator + " " + right + " AND " + querystr += " AND" } querystr = querystr[0:len(querystr) - 4] } @@ -318,28 +317,28 @@ func (adapter *Mysql_Adapter) SimpleLeftJoin(name string, table1 string, table2 // Remove the trailing AND querystr = querystr[0:len(querystr) - 4] + // Add support for BETWEEN x.x if len(where) != 0 { querystr += " WHERE" for _, loc := range _process_where(where) { - var left, right string - - if loc.LeftTable != "" { - left = "`" + loc.LeftTable + "`.`" + loc.LeftColumn + "`" - } else if loc.LeftType == "column" { - left = "`" + loc.LeftColumn + "`" - } else { - left = loc.LeftColumn + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + halves := strings.Split(token.Contents,".") + if len(halves) == 2 { + querystr += " `" + halves[0] + "`.`" + halves[1] + "`" + } else { + querystr += " `" + token.Contents + "`" + } + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } } - - if loc.RightTable != "" { - right = "`" + loc.RightTable + "`.`" + loc.RightColumn + "`" - } else if loc.RightType == "column" { - right = "`" + loc.RightColumn + "`" - } else { - right = loc.RightColumn - } - - querystr += " " + left + " " + loc.Operator + " " + right + " AND " + querystr += " AND" } querystr = querystr[0:len(querystr) - 4] } @@ -408,28 +407,28 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 // Remove the trailing AND querystr = querystr[0:len(querystr) - 4] + // Add support for BETWEEN x.x if len(where) != 0 { querystr += " WHERE" for _, loc := range _process_where(where) { - var left, right string - - if loc.LeftTable != "" { - left = "`" + loc.LeftTable + "`.`" + loc.LeftColumn + "`" - } else if loc.LeftType == "column" { - left = "`" + loc.LeftColumn + "`" - } else { - left = loc.LeftColumn + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + halves := strings.Split(token.Contents,".") + if len(halves) == 2 { + querystr += " `" + halves[0] + "`.`" + halves[1] + "`" + } else { + querystr += " `" + token.Contents + "`" + } + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } } - - if loc.RightTable != "" { - right = "`" + loc.RightTable + "`.`" + loc.RightColumn + "`" - } else if loc.RightType == "column" { - right = "`" + loc.RightColumn + "`" - } else { - right = loc.RightColumn - } - - querystr += " " + left + " " + loc.Operator + " " + right + " AND " + querystr += " AND" } querystr = querystr[0:len(querystr) - 4] } @@ -451,6 +450,163 @@ func (adapter *Mysql_Adapter) SimpleInnerJoin(name string, table1 string, table2 return querystr, nil } +func (adapter *Mysql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) { + /* Insert Portion */ + var querystr string = "INSERT INTO `" + ins.Table + "`(" + + // Escape the column names, just in case we've used a reserved keyword + for _, column := range _process_columns(ins.Columns) { + if column.Type == "function" { + querystr += column.Left + "," + } else { + querystr += "`" + column.Left + "`," + } + } + querystr = querystr[0:len(querystr) - 1] + ") SELECT" + + /* Select Portion */ + + for _, column := range _process_columns(sel.Columns) { + var source, alias string + + // Escape the column names, just in case we've used a reserved keyword + if column.Type == "function" || column.Type == "substitute" { + source = column.Left + } else { + source = "`" + column.Left + "`" + } + + if column.Alias != "" { + alias = " AS `" + column.Alias + "`" + } + querystr += " " + source + alias + "," + } + querystr = querystr[0:len(querystr) - 1] + + querystr += " FROM `" + sel.Table + "`" + + // Add support for BETWEEN x.x + if len(sel.Where) != 0 { + querystr += " WHERE" + for _, loc := range _process_where(sel.Where) { + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + querystr += " `" + token.Contents + "`" + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } + } + querystr += " AND" + } + querystr = querystr[0:len(querystr) - 4] + } + + if len(sel.Orderby) != 0 { + querystr += " ORDER BY " + for _, column := range _process_orderby(sel.Orderby) { + querystr += column.Column + " " + strings.ToUpper(column.Order) + "," + } + querystr = querystr[0:len(querystr) - 1] + } + + if sel.Limit != "" { + querystr += " LIMIT " + sel.Limit + } + + querystr = strings.TrimSpace(querystr) + adapter.push_statement(name,querystr) + return querystr, nil +} + +func (adapter *Mysql_Adapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { + /* Insert Portion */ + var querystr string = "INSERT INTO `" + ins.Table + "`(" + + // Escape the column names, just in case we've used a reserved keyword + for _, column := range _process_columns(ins.Columns) { + if column.Type == "function" { + querystr += column.Left + "," + } else { + querystr += "`" + column.Left + "`," + } + } + querystr = querystr[0:len(querystr) - 1] + ") SELECT" + + /* Select Portion */ + + for _, column := range _process_columns(sel.Columns) { + var source, alias string + + // Escape the column names, just in case we've used a reserved keyword + if column.Table != "" { + source = "`" + column.Table + "`.`" + column.Left + "`" + } else if column.Type == "function" { + source = column.Left + } else { + source = "`" + column.Left + "`" + } + + if column.Alias != "" { + alias = " AS `" + column.Alias + "`" + } + querystr += " " + source + alias + "," + } + querystr = querystr[0:len(querystr) - 1] + + querystr += " FROM `" + sel.Table1 + "` INNER JOIN `" + sel.Table2 + "` ON " + for _, joiner := range _process_joiner(sel.Joiners) { + querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " + } + querystr = querystr[0:len(querystr) - 4] + + // Add support for BETWEEN x.x + if len(sel.Where) != 0 { + querystr += " WHERE" + for _, loc := range _process_where(sel.Where) { + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + halves := strings.Split(token.Contents,".") + if len(halves) == 2 { + querystr += " `" + halves[0] + "`.`" + halves[1] + "`" + } else { + querystr += " `" + token.Contents + "`" + } + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } + } + querystr += " AND" + } + querystr = querystr[0:len(querystr) - 4] + } + + if len(sel.Orderby) != 0 { + querystr += " ORDER BY " + for _, column := range _process_orderby(sel.Orderby) { + querystr += column.Column + " " + strings.ToUpper(column.Order) + "," + } + querystr = querystr[0:len(querystr) - 1] + } + + if sel.Limit != "" { + querystr += " LIMIT " + sel.Limit + } + + querystr = strings.TrimSpace(querystr) + adapter.push_statement(name,querystr) + return querystr, nil +} + func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -460,24 +616,27 @@ func (adapter *Mysql_Adapter) SimpleCount(name string, table string, where strin } var querystr string = "SELECT COUNT(*) AS `count` FROM `" + table + "`" + + // Add support for BETWEEN x.x if len(where) != 0 { querystr += " WHERE" + //fmt.Println("SimpleCount:",name) + //fmt.Println("where:",where) + //fmt.Println("_process_where:",_process_where(where)) for _, loc := range _process_where(where) { - var left, right string - - if loc.LeftType == "column" { - left = "`" + loc.LeftColumn + "`" - } else { - left = loc.LeftColumn + for _, token := range loc.Expr { + switch(token.Type) { + case "function","operator","number","substitute": + querystr += " " + token.Contents + "" + case "column": + querystr += " `" + token.Contents + "`" + case "string": + querystr += " '" + token.Contents + "'" + default: + panic("This token doesn't exist o_o") + } } - - if loc.RightType == "column" { - right = "`" + loc.RightColumn + "`" - } else { - right = loc.RightColumn - } - - querystr += " " + left + " " + loc.Operator + " " + right + " AND " + querystr += " AND" } querystr = querystr[0:len(querystr) - 4] } diff --git a/query_gen/lib/querygen.go b/query_gen/lib/querygen.go index 251e54fb..bdb070ea 100644 --- a/query_gen/lib/querygen.go +++ b/query_gen/lib/querygen.go @@ -6,6 +6,51 @@ import "errors" var DB_Registry []DB_Adapter var No_Adapter = errors.New("This adapter doesn't exist") +type DB_Select struct +{ + Table string + Columns string + Where string + Orderby string + Limit string +} + +type DB_Join struct +{ + Table1 string + Table2 string + Columns string + Joiners string + Where string + Orderby string + Limit string +} + +type DB_Insert struct +{ + Table string + Columns string + Fields string +} + +/*type DB_Select struct +{ + Name string + Table string + Columns []DB_Column + Where []DB_Where + Orderby []DB_Order + Limit DB_Limit +} + +type DB_Insert struct +{ + Name string + Table string + Columns []DB_Column + Fields []DB_Field +}*/ + type DB_Column struct { Table string @@ -22,13 +67,7 @@ type DB_Field struct type DB_Where struct { - LeftTable string - LeftColumn string - RightTable string - RightColumn string - Operator string - LeftType string - RightType string + Expr []DB_Token // Simple expressions, the innards of functions are opaque for now. } type DB_Joiner struct @@ -71,6 +110,8 @@ type DB_Adapter interface { SimpleSelect(string,string,string,string,string,string) (string, error) SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error) SimpleInnerJoin(string,string,string,string,string,string,string,string) (string, error) + SimpleInsertSelect(string,DB_Insert,DB_Select) (string,error) + SimpleInsertInnerJoin(string,DB_Insert,DB_Join) (string,error) SimpleCount(string,string,string,string) (string, error) Write() error diff --git a/query_gen/lib/utils.go b/query_gen/lib/utils.go index b083219f..9d69cd2a 100644 --- a/query_gen/lib/utils.go +++ b/query_gen/lib/utils.go @@ -28,6 +28,8 @@ func _process_columns(colstr string) (columns []DB_Column) { } if halves[0][len(halves[0]) - 1] == ')' { outcol.Type = "function" + } else if halves[0] == "?" { + outcol.Type = "substitute" } else { outcol.Type = "column" } @@ -83,42 +85,97 @@ func _process_joiner(joinstr string) (joiner []DB_Joiner) { return joiner } -// TO-DO: Add support for keywords like BETWEEN. We'll probably need an arbitrary expression parser like with the update setters. func _process_where(wherestr string) (where []DB_Where) { if wherestr == "" { return where } wherestr = strings.Replace(wherestr," and "," AND ",-1) + + var buffer string + var optype int // 0: None, 1: Number, 2: Column, 3: Function, 4: String, 5: Operator for _, segment := range strings.Split(wherestr," AND ") { - // TO-DO: Subparse the contents of a function and spit out a DB_Function struct - var outwhere DB_Where - var parseOffset int - var left, right string - - left, parseOffset = _get_identifier(segment, parseOffset) - outwhere.Operator, parseOffset = _get_operator(segment, parseOffset + 1) - right, parseOffset = _get_identifier(segment, parseOffset + 1) - outwhere.LeftType = _get_identifier_type(left) - outwhere.RightType = _get_identifier_type(right) - - left_operand := strings.Split(left,".") - right_operand := strings.Split(right,".") - - if len(left_operand) == 2 { - outwhere.LeftTable = strings.TrimSpace(left_operand[0]) - outwhere.LeftColumn = strings.TrimSpace(left_operand[1]) - } else { - outwhere.LeftColumn = strings.TrimSpace(left_operand[0]) + var tmp_where DB_Where + segment += ")" + for i := 0; i < len(segment); i++ { + char := segment[i] + //fmt.Println("optype",optype) + switch(optype) { + case 0: // unknown + //fmt.Println("case 0:",char,string(char)) + if ('0' <= char && char <= '9') { + optype = 1 + buffer = string(char) + } else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' { + optype = 2 + buffer = string(char) + } else if char == '\'' { + optype = 4 + buffer = "" + } else if _is_op_byte(char) { + optype = 5 + buffer = string(char) + } else if char == '?' { + //fmt.Println("Expr:","?") + tmp_where.Expr = append(tmp_where.Expr,DB_Token{"?","substitute"}) + } + case 1: // number + if ('0' <= char && char <= '9') { + buffer += string(char) + } else { + optype = 0 + i-- + //fmt.Println("Expr:",buffer) + tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"number"}) + } + case 2: // column + if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '.' || char == '_' { + buffer += string(char) + } else if char == '(' { + optype = 3 + i-- + } else { + optype = 0 + i-- + //fmt.Println("Expr:",buffer) + tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"column"}) + } + case 3: // function + var pre_i int = i + //fmt.Println("buffer",buffer) + //fmt.Println("len(halves)",len(halves[1])) + //fmt.Println("pre_i",string(halves[1][pre_i])) + //fmt.Println("msg prior to pre_i",halves[1][0:pre_i]) + i = _skip_function_call(segment,i-1) + //fmt.Println("i",i) + //fmt.Println("msg prior to i-1",halves[1][0:i-1]) + //fmt.Println("string(i-1)",string(halves[1][i-1])) + //fmt.Println("string(i)",string(halves[1][i])) + buffer += segment[pre_i:i] + string(segment[i]) + //fmt.Println("Expr:",buffer) + tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"function"}) + optype = 0 + case 4: // string + if char != '\'' { + buffer += string(char) + } else { + optype = 0 + //fmt.Println("Expr:",buffer) + tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"string"}) + } + case 5: // operator + if _is_op_byte(char) { + buffer += string(char) + } else { + optype = 0 + i-- + //fmt.Println("Expr:",buffer) + tmp_where.Expr = append(tmp_where.Expr,DB_Token{buffer,"operator"}) + } + default: + panic("Bad optype in _process_where") + } } - - if len(right_operand) == 2 { - outwhere.RightTable = strings.TrimSpace(right_operand[0]) - outwhere.RightColumn = strings.TrimSpace(right_operand[1]) - } else { - outwhere.RightColumn = strings.TrimSpace(right_operand[0]) - } - - where = append(where,outwhere) + where = append(where,tmp_where) } return where } @@ -170,11 +227,12 @@ func _process_set(setstr string) (setter []DB_Setter) { if ('0' <= char && char <= '9') { optype = 1 buffer = string(char) - } else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') { + } else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' { optype = 2 buffer = string(char) } else if char == '\'' { optype = 4 + buffer = "" } else if _is_op_byte(char) { optype = 5 buffer = string(char) @@ -192,7 +250,7 @@ func _process_set(setstr string) (setter []DB_Setter) { tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"number"}) } case 2: // column - if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') { + if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' { buffer += string(char) } else if char == '(' { optype = 3 @@ -235,6 +293,8 @@ func _process_set(setstr string) (setter []DB_Setter) { //fmt.Println("Expr:",buffer) tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"operator"}) } + default: + panic("Bad optype in _process_set") } } setter = append(setter,tmp_setter) @@ -258,6 +318,10 @@ func _is_op_byte(char byte) bool { return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' } +func _is_op_rune(char rune) bool { + return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/' +} + func _process_fields(fieldstr string) (fields []DB_Field) { if fieldstr == "" { return fields diff --git a/query_gen/main.go b/query_gen/main.go index d213d02e..5f6a22f3 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -18,34 +18,57 @@ func write_statements(adapter qgen.DB_Adapter) error { if err != nil { return err } + err = write_left_joins(adapter) if err != nil { return err } + err = write_inner_joins(adapter) if err != nil { return err } + err = write_inserts(adapter) if err != nil { return err } + err = write_replaces(adapter) if err != nil { return err } + err = write_updates(adapter) if err != nil { return err } + err = write_deletes(adapter) if err != nil { return err } + err = write_simple_counts(adapter) if err != nil { return err } + + err = write_insert_selects(adapter) + if err != nil { + return err + } + + err = write_insert_left_joins(adapter) + if err != nil { + return err + } + + err = write_insert_inner_joins(adapter) + if err != nil { + return err + } + return nil } @@ -84,6 +107,8 @@ func write_selects(adapter qgen.DB_Adapter) error { adapter.SimpleSelect("get_themes","themes","uname, default","","","") + adapter.SimpleSelect("get_widgets","widgets","position, side, type, active, location, data","","position ASC","") + adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","","") adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","") @@ -291,7 +316,29 @@ func write_deletes(adapter qgen.DB_Adapter) error { } func write_simple_counts(adapter qgen.DB_Adapter) error { - adapter.SimpleCount("report_exists","topics","data = ? and data != '' and parentID = 1","") + adapter.SimpleCount("report_exists","topics","data = ? AND data != '' AND parentID = 1","") + + return nil +} + +func write_insert_selects(adapter qgen.DB_Adapter) error { + adapter.SimpleInsertSelect("add_forum_perms_to_forum_admins", + qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""}, + qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 1","",""}, + ) + + return nil +} + +func write_insert_left_joins(adapter qgen.DB_Adapter) error { + return nil +} + +func write_insert_inner_joins(adapter qgen.DB_Adapter) error { + adapter.SimpleInsertInnerJoin("notify_watchers", + qgen.DB_Insert{"activity_stream_matches","watcher, asid",""}, + qgen.DB_Join{"activity_stream","activity_subscriptions","activity_subscriptions.user, activity_stream.asid","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","",""}, + ) return nil } diff --git a/routes.go b/routes.go index f70c26d9..1a1166ce 100644 --- a/routes.go +++ b/routes.go @@ -16,6 +16,8 @@ import ( "html" "html/template" "database/sql" + + "./query_gen/lib" ) import _ "github.com/go-sql-driver/mysql" @@ -104,16 +106,25 @@ func route_topics(w http.ResponseWriter, r *http.Request){ return } - var fidList []string + var qlist string + var fidList []interface{} group := groups[user.Group] for _, fid := range group.CanSee { if forums[fid].Name != "" { fidList = append(fidList,strconv.Itoa(fid)) + qlist += "?," } } + qlist = qlist[0:len(qlist) - 1] var topicList []TopicsRow - rows, err := db.Query(topic_list_query(fidList)) + stmt, err := qgen.Builder.SimpleLeftJoin("topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar","topics.createdBy = users.uid","parentID IN("+qlist+")","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","") + if err != nil { + InternalError(err,w,r) + return + } + + rows, err := stmt.Query(fidList...) if err != nil { InternalError(err,w,r) return @@ -166,9 +177,9 @@ func route_topics(w http.ResponseWriter, r *http.Request){ if template_topics_handle != nil { template_topics_handle(pi,w) } else { - mapping, ok := themes[defaultTheme].TemplatesMap["topic"] + mapping, ok := themes[defaultTheme].TemplatesMap["topics"] if !ok { - mapping = "topic" + mapping = "topics" } err = templates.ExecuteTemplate(w,mapping + ".html", pi) if err != nil { @@ -1689,8 +1700,20 @@ func route_register_submit(w http.ResponseWriter, r *http.Request) { LocalError("You didn't put in a password.",w,r,user) return } - if password == "test" || password == "123456" || password == "123" || password == "password" { - LocalError("Your password is too weak.",w,r,user) + + if password == username { + LocalError("You can't use your username as your password.",w,r,user) + return + } + + if password == email { + LocalError("You can't use your email as your password.",w,r,user) + return + } + + err = weak_password(password) + if err != nil { + LocalError(err.Error(),w,r,user) return } diff --git a/template_forum.go b/template_forum.go index 2db4e46e..6db71038 100644 --- a/template_forum.go +++ b/template_forum.go @@ -51,11 +51,15 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) +if tmpl_forum_vars.Header.Sidebars.Right != "" { +w.Write(header_10) +} +w.Write(header_11) if len(tmpl_forum_vars.Header.NoticeList) != 0 { for _, item := range tmpl_forum_vars.Header.NoticeList { -w.Write(header_10) +w.Write(header_12) w.Write([]byte(item)) -w.Write(header_11) +w.Write(header_13) } } if tmpl_forum_vars.Page > 1 { @@ -133,4 +137,10 @@ w.Write(forum_31) } w.Write(forum_32) w.Write(footer_0) +if tmpl_forum_vars.Header.Sidebars.Right != "" { +w.Write(footer_1) +w.Write([]byte(string(tmpl_forum_vars.Header.Sidebars.Right))) +w.Write(footer_2) +} +w.Write(footer_3) } diff --git a/template_forums.go b/template_forums.go index 45cb1fa7..736b7a75 100644 --- a/template_forums.go +++ b/template_forums.go @@ -51,11 +51,15 @@ w.Write(menu_6) } w.Write(menu_7) w.Write(header_9) +if tmpl_forums_vars.Header.Sidebars.Right != "" { +w.Write(header_10) +} +w.Write(header_11) if len(tmpl_forums_vars.Header.NoticeList) != 0 { for _, item := range tmpl_forums_vars.Header.NoticeList { -w.Write(header_10) +w.Write(header_12) w.Write([]byte(item)) -w.Write(header_11) +w.Write(header_13) } } w.Write(forums_0) @@ -106,4 +110,10 @@ w.Write(forums_20) } w.Write(forums_21) w.Write(footer_0) +if tmpl_forums_vars.Header.Sidebars.Right != "" { +w.Write(footer_1) +w.Write([]byte(string(tmpl_forums_vars.Header.Sidebars.Right))) +w.Write(footer_2) +} +w.Write(footer_3) } diff --git a/template_list.go b/template_list.go index d822ca10..85fe900e 100644 --- a/template_list.go +++ b/template_list.go @@ -9,14 +9,14 @@ var header_1 []byte = []byte(` `) var header_2 []byte = []byte(` +var header_3 []byte = []byte(`" rel="stylesheet" type="text/css"> `) var header_4 []byte = []byte(` `) var header_5 []byte = []byte(` +var header_6 []byte = []byte(`"> `) var header_7 []byte = []byte(` {{range .Header.Scripts}} - + {{end}} @@ -18,5 +18,5 @@
{{template "menu.html" .}} -
+
{{range .Header.NoticeList}}
{{.}}
{{end}} diff --git a/templates/widget_simple.html b/templates/widget_simple.html new file mode 100644 index 00000000..cd18d4a3 --- /dev/null +++ b/templates/widget_simple.html @@ -0,0 +1,6 @@ +
+
{{.Name}}
+
+
+
{{.Text}}
+
diff --git a/themes.go b/themes.go index 2b39b41c..96f7acb8 100644 --- a/themes.go +++ b/themes.go @@ -32,9 +32,11 @@ type Theme struct ForkOf string Tag string URL string + Sidebars string // Allowed Values: left, right, both, false Settings map[string]ThemeSetting Templates []TemplateMapping - TemplatesMap map[string]string // TO-DO: Make template mapping work without the template compiler + TemplatesMap map[string]string + Resources []ThemeResource // This variable should only be set and unset by the system, not the theme meta file Active bool @@ -53,6 +55,12 @@ type TemplateMapping struct //When string } +type ThemeResource struct +{ + Name string + Location string +} + func LoadThemes() error { rows, err := get_themes_stmt.Query() if err != nil { diff --git a/themes/tempra-simple/public/main.css b/themes/tempra-simple/public/main.css index 7a0bd8c6..703971a1 100644 --- a/themes/tempra-simple/public/main.css +++ b/themes/tempra-simple/public/main.css @@ -141,7 +141,9 @@ li a { padding: 0px; padding-top: 0px; } -.rowblock:empty { display: none; } +.rowblock:empty { + display: none; +} .rowsmall { font-size:12px; } @@ -449,6 +451,18 @@ button.username { position: relative; top: -0.25px; } top: -2px; } +@media(min-width: 881px) { + .shrink_main { + float: left; + width: calc(75% - 12px); + } + .sidebar { + float: left; + width: 25%; + margin-left: 12px; + } +} + @media (max-width: 880px) { li { height: 29px; @@ -477,6 +491,7 @@ button.username { position: relative; top: -0.25px; } overflow-x: hidden; } .container { width: auto; } + .sidebar { display: none; } .selectedAlert .alertList { top: 37px; right: 4px; } } diff --git a/themes/tempra-simple/public/sample.css b/themes/tempra-simple/public/sample.css new file mode 100644 index 00000000..8836f2ef --- /dev/null +++ b/themes/tempra-simple/public/sample.css @@ -0,0 +1 @@ +/* Sample CSS file injected by Tempra Simple. Doesn't do anything. */ diff --git a/themes/tempra-simple/theme.json b/themes/tempra-simple/theme.json index d15ad0e8..e8c2c4a6 100644 --- a/themes/tempra-simple/theme.json +++ b/themes/tempra-simple/theme.json @@ -5,5 +5,12 @@ "Creator": "Azareal", "FullImage": "tempra-simple.png", "MobileFriendly": true, - "URL": "github.com/Azareal/Gosora" + "URL": "github.com/Azareal/Gosora", + "Sidebars":"false", + "Resources": [ + { + "Name": "sample.css", + "Location": "none" + } + ] } diff --git a/user.go b/user.go index 83b6db08..08f3151b 100644 --- a/user.go +++ b/user.go @@ -2,9 +2,11 @@ package main import ( //"fmt" + "strings" "strconv" "net" "net/http" + "html/template" "golang.org/x/crypto/bcrypt" "database/sql" _ "github.com/go-sql-driver/mysql" @@ -80,11 +82,12 @@ func SendValidationEmail(username string, email string, token string) bool { } func SimpleForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user User, success bool) { + user, success = SimpleSessionCheck(w,r) if !forum_exists(fid) { PreError("The target forum doesn't exist.",w,r) return user, false } - user, success = SimpleSessionCheck(w,r) + fperms := groups[user.Group].Forums[fid] if fperms.Overrides && !user.Is_Super_Admin { user.Perms.ViewTopic = fperms.ViewTopic @@ -108,11 +111,12 @@ func SimpleForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (u } func ForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user User, headerVars HeaderVars, success bool) { + user, headerVars, success = SessionCheck(w,r) if !forum_exists(fid) { NotFound(w,r) return user, headerVars, false } - user, success = SimpleSessionCheck(w,r) + fperms := groups[user.Group].Forums[fid] //fmt.Printf("%+v\n", user.Perms) //fmt.Printf("%+v\n", fperms) @@ -134,9 +138,6 @@ func ForumSessionCheck(w http.ResponseWriter, r *http.Request, fid int) (user Us } } } - if user.Is_Banned { - headerVars.NoticeList = append(headerVars.NoticeList,"Your account has been suspended. Some of your permissions may have been revoked.") - } return user, headerVars, success } @@ -147,7 +148,25 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request) (user User, he NoPermissions(w,r,user) return user, headerVars, false } - headerVars.Stylesheets = append(headerVars.Stylesheets,"panel") + + headerVars.Stylesheets = append(headerVars.Stylesheets,"panel.css") + if len(themes[defaultTheme].Resources) != 0 { + rlist := themes[defaultTheme].Resources + for _, resource := range rlist { + if resource.Location == "global" || resource.Location == "panel" { + halves := strings.Split(resource.Name,".") + if len(halves) != 2 { + continue + } + if halves[1] == "css" { + headerVars.Stylesheets = append(headerVars.Stylesheets,resource.Name) + } else if halves[1] == "js" { + headerVars.Scripts = append(headerVars.Scripts,resource.Name) + } + } + } + } + return user, headerVars, success } func _simple_panel_session_check(w http.ResponseWriter, r *http.Request) (user User, success bool) { @@ -164,6 +183,40 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, headerVars if user.Is_Banned { headerVars.NoticeList = append(headerVars.NoticeList,"Your account has been suspended. Some of your permissions may have been revoked.") } + + if len(themes[defaultTheme].Resources) != 0 { + rlist := themes[defaultTheme].Resources + for _, resource := range rlist { + if resource.Location == "global" || resource.Location == "frontend" { + halves := strings.Split(resource.Name,".") + if len(halves) != 2 { + continue + } + if halves[1] == "css" { + headerVars.Stylesheets = append(headerVars.Stylesheets,resource.Name) + } else if halves[1] == "js" { + headerVars.Scripts = append(headerVars.Scripts,resource.Name) + } + } + } + } + + // TO-DO: Support for left sidebars and sidebars on both sides + //fmt.Println("themes[defaultTheme].Sidebars",themes[defaultTheme].Sidebars) + if themes[defaultTheme].Sidebars == "right" { + if len(docks.RightSidebar) != 0 { + var sbody string + for _, widget := range docks.RightSidebar { + //fmt.Println("widget",widget) + if widget.Enabled && widget.Location == "global" { + sbody += widget.Body + //fmt.Println("sbody",sbody) + } + } + headerVars.Sidebars.Right = template.HTML(sbody) + } + } + return user, headerVars, success } diff --git a/utils.go b/utils.go index 0a85dd6a..af48b138 100644 --- a/utils.go +++ b/utils.go @@ -5,6 +5,7 @@ import ( "time" "os" "math" + "errors" "strings" "unicode" "strconv" @@ -171,6 +172,57 @@ func SendEmail(email string, subject string, msg string) (res bool) { return true } +func weak_password(password string) error { + if len(password) < 8 { + return errors.New("Your password needs to be at-least eight characters long.") + } + var charMap map[rune]int = make(map[rune]int) + var numbers, /*letters, */symbols, upper, lower int + for _, char := range password { + charItem, ok := charMap[char] + if ok { + charItem++ + } else { + charItem = 1 + } + charMap[char] = charItem + + if unicode.IsLetter(char) { + //letters++ + if unicode.IsUpper(char) { + upper++ + } else { + lower++ + } + } else if unicode.IsNumber(char) { + numbers++ + } else { + symbols++ + } + } + + if numbers == 0 { + return errors.New("You don't have any numbers in your password.") + } + /*if letters == 0 { + return errors.New("You don't have any letters in your password.") + }*/ + if upper == 0 { + return errors.New("You don't have any uppercase characters in your password.") + } + if lower == 0 { + return errors.New("You don't have any lowercase characters in your password.") + } + if (len(password) / 2) < len(charMap) { + return errors.New("You don't have enough unique characters in your password.") + } + + if strings.Contains(strings.ToLower(password),"test") || /*strings.Contains(strings.ToLower(password),"123456") || */strings.Contains(strings.ToLower(password),"123") || strings.Contains(strings.ToLower(password),"password") || strings.Contains(strings.ToLower(password),"qwerty") { + return errors.New("You may not have 'test', '123', 'password' or 'qwerty' in your password.") + } + return nil +} + func write_file(name string, content string) (err error) { f, err := os.Create(name) if err != nil { diff --git a/widgets.go b/widgets.go new file mode 100644 index 00000000..9935b32e --- /dev/null +++ b/widgets.go @@ -0,0 +1,95 @@ +/* Copyright Azareal 2017 - 2018 */ +package main + +import "fmt" +import "bytes" +import "sync" +import "encoding/json" +//import "html/template" + +var docks WidgetDocks +var widget_update_mutex sync.RWMutex + +type WidgetDocks struct +{ + LeftSidebar []Widget + RightSidebar []Widget + //PanelLeft []Menus +} + +type Widget struct +{ + Enabled bool + Location string // Coming Soon: overview, topics, topic / topic_view, forums, forum, global + Position int + Body string +} + +type NameTextPair struct +{ + Name string + Text string +} + +func init_widgets() error { + rows, err := get_widgets_stmt.Query() + if err != nil { + return err + } + defer rows.Close() + + var sbytes []byte + var side, wtype, data string + + var leftWidgets []Widget + var rightWidgets []Widget + + for rows.Next() { + var widget Widget + err = rows.Scan(&widget.Position, &side, &wtype, &widget.Enabled, &widget.Location, &data) + if err != nil { + return err + } + + sbytes = []byte(data) + switch(wtype) { + case "simple": + var tmp NameTextPair + err = json.Unmarshal(sbytes, &tmp) + if err != nil { + return err + } + + var b bytes.Buffer + err = templates.ExecuteTemplate(&b,"widget_simple.html",tmp) + if err != nil { + return err + } + widget.Body = string(b.Bytes()) + default: + widget.Body = data + } + + if side == "left" { + leftWidgets = append(leftWidgets,widget) + } else if side == "right" { + rightWidgets = append(rightWidgets,widget) + } + } + err = rows.Err() + if err != nil { + return err + } + + widget_update_mutex.Lock() + docks.LeftSidebar = leftWidgets + docks.RightSidebar = rightWidgets + widget_update_mutex.Unlock() + + if super_debug { + fmt.Println("docks.LeftSidebar",docks.LeftSidebar) + fmt.Println("docks.RightSidebar",docks.RightSidebar) + } + + return nil +}