diff --git a/alerts.go b/alerts.go index d6fe8ba3..224dba90 100644 --- a/alerts.go +++ b/alerts.go @@ -127,6 +127,7 @@ func notifyWatchers(asid int64) { log.Fatal(err.Error()) return } + defer rows.Close() var uid int var uids []int @@ -143,7 +144,6 @@ func notifyWatchers(asid int64) { log.Fatal(err.Error()) return } - _ = rows.Close() var actorID, targetUserID, elementID int var event, elementType string diff --git a/auth.go b/auth.go index e67310c7..dbd9862a 100644 --- a/auth.go +++ b/auth.go @@ -109,6 +109,7 @@ func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) { http.SetCookie(w, &cookie) } +// TODO: Set the cookie domain func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) { cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: year} http.SetCookie(w, &cookie) diff --git a/cache.go b/cache.go index 8aa82197..9f85944b 100644 --- a/cache.go +++ b/cache.go @@ -9,7 +9,7 @@ const CACHE_DYNAMIC int = 1 const CACHE_SQL int = 2 // ErrCacheDesync is thrown whenever a piece of data, for instance, a user is out of sync with the database. Currently unused. -var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TO-DO: A cross-server synchronisation mechanism +var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TODO: A cross-server synchronisation mechanism // ErrStoreCapacityOverflow is thrown whenever a datastore reaches it's maximum hard capacity. I'm not sure *if* this one is used, at the moment. Probably. var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.") diff --git a/database.go b/database.go index d1f39d08..e52ac8f8 100644 --- a/database.go +++ b/database.go @@ -72,7 +72,7 @@ func initDatabase() (err error) { GuestPerms = groups[6].Perms log.Print("Loading the forums.") - fstore = NewStaticForumStore() + fstore = NewMemoryForumStore() err = fstore.LoadForums() if err != nil { return err diff --git a/errors.go b/errors.go index 98e4e308..2a7cdaad 100644 --- a/errors.go +++ b/errors.go @@ -7,7 +7,7 @@ import "sync" import "net/http" import "runtime/debug" -// TO-DO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page? +// TODO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page? var errorBufferMutex sync.RWMutex var errorBuffer []error diff --git a/extend.go b/extend.go index 36c316ba..4b95da32 100644 --- a/extend.go +++ b/extend.go @@ -15,7 +15,7 @@ var plugins = make(map[string]*Plugin) var hooks = map[string][]func(interface{}) interface{}{ "forums_frow_assign": nil, "topic_create_frow_assign": nil, - "rrow_assign": nil, // TO-DO: Rename this hook to topic_rrow_assign + "rrow_assign": nil, // TODO: Rename this hook to topic_rrow_assign } // Hooks with a variable number of arguments diff --git a/files.go b/files.go index 92efa373..425905af 100644 --- a/files.go +++ b/files.go @@ -82,7 +82,7 @@ func addStaticFile(path string, prefix string) error { func compressBytesGzip(in []byte) []byte { var buff bytes.Buffer gz := gzip.NewWriter(&buff) - _, _ = gz.Write(in) // TO-DO: What if this errors? What circumstances could it error under? Should we add a second return value? + _, _ = gz.Write(in) // TODO: What if this errors? What circumstances could it error under? Should we add a second return value? _ = gz.Close() return buff.Bytes() } diff --git a/forum.go b/forum.go index c90e6143..5635f0e0 100644 --- a/forum.go +++ b/forum.go @@ -39,7 +39,7 @@ type ForumSimple struct { Preset string } -func buildForumUrl(slug string, fid int) string { +func buildForumURL(slug string, fid int) string { if slug == "" { return "/forum/" + strconv.Itoa(fid) } diff --git a/forum_store.go b/forum_store.go index 73becf91..60b2c2c6 100644 --- a/forum_store.go +++ b/forum_store.go @@ -1,12 +1,14 @@ /* Work in progress. Check back later! */ package main -import "log" -import "sync" +import ( + "database/sql" + "log" + "sync" + "sync/atomic" -//import "sync/atomic" -import "database/sql" -import "./query_gen/lib" + "./query_gen/lib" +) var forumUpdateMutex sync.Mutex var forumCreateMutex sync.Mutex @@ -24,7 +26,7 @@ type ForumStore interface { Set(forum *Forum) error //Update(Forum) error //CascadeUpdate(Forum) error - Delete(id int) error + Delete(id int) CascadeDelete(id int) error IncrementTopicCount(id int) error DecrementTopicCount(id int) error @@ -32,6 +34,8 @@ type ForumStore interface { Exists(id int) bool GetAll() ([]*Forum, error) GetAllIDs() ([]int, error) + GetAllVisible() ([]*Forum, error) + GetAllVisibleIDs() ([]int, error) //GetChildren(parentID int, parentType string) ([]*Forum,error) //GetFirstChild(parentID int, parentType string) (*Forum,error) CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error) @@ -39,17 +43,20 @@ type ForumStore interface { GetGlobalCount() int } -type StaticForumStore struct { - 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 +type MemoryForumStore struct { + //forums map[int]*Forum + forums sync.Map + forumView atomic.Value // []*Forum //fids []int - forumCapCount int + forumCount int - get *sql.Stmt - getAll *sql.Stmt - forumCount *sql.Stmt + get *sql.Stmt + getAll *sql.Stmt + delete *sql.Stmt + getForumCount *sql.Stmt } -func NewStaticForumStore() *StaticForumStore { +func NewMemoryForumStore() *MemoryForumStore { getStmt, err := qgen.Builder.SimpleSelect("forums", "name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime", "fid = ?", "", "") if err != nil { log.Fatal(err) @@ -58,30 +65,44 @@ func NewStaticForumStore() *StaticForumStore { if err != nil { log.Fatal(err) } + deleteStmt, err := qgen.Builder.SimpleUpdate("forums", "name= '', active = 0", "fid = ?") + if err != nil { + log.Fatal(err) + } forumCountStmt, err := qgen.Builder.SimpleCount("forums", "name != ''", "") if err != nil { log.Fatal(err) } - return &StaticForumStore{ - get: getStmt, - getAll: getAllStmt, - forumCount: forumCountStmt, + return &MemoryForumStore{ + get: getStmt, + getAll: getAllStmt, + delete: deleteStmt, + getForumCount: forumCountStmt, } } -func (sfs *StaticForumStore) LoadForums() error { +func (mfs *MemoryForumStore) LoadForums() error { log.Print("Adding the uncategorised forum") - var forums = []*Forum{ - &Forum{0, buildForumUrl(nameToSlug("Uncategorised"), 0), "Uncategorised", "", config.UncategorisedForumVisible, "all", 0, "", 0, "", "", 0, "", 0, ""}, + forumUpdateMutex.Lock() + defer forumUpdateMutex.Unlock() + + var forumView []*Forum + addForum := func(forum *Forum) { + mfs.forums.Store(forum.ID, forum) + if forum.Active && forum.Name != "" { + forumView = append(forumView, forum) + } } + addForum(&Forum{0, buildForumURL(nameToSlug("Uncategorised"), 0), "Uncategorised", "", config.UncategorisedForumVisible, "all", 0, "", 0, "", "", 0, "", 0, ""}) + rows, err := get_forums_stmt.Query() if err != nil { return err } defer rows.Close() - var i = 1 + var i = 0 for ; rows.Next(); i++ { forum := Forum{ID: 0, Active: true, Preset: "all"} err = rows.Scan(&forum.ID, &forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) @@ -89,12 +110,6 @@ func (sfs *StaticForumStore) LoadForums() error { return err } - // Ugh, you really shouldn't physically delete these items, it makes a big mess of things - if forum.ID != i { - log.Print("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forum() instead x.x") - sfs.fillForumIDGap(i, forum.ID) - } - if forum.Name == "" { if dev.DebugMode { log.Print("Adding a placeholder forum") @@ -103,132 +118,164 @@ func (sfs *StaticForumStore) LoadForums() error { log.Print("Adding the " + forum.Name + " forum") } - forum.Link = buildForumUrl(nameToSlug(forum.Name), forum.ID) + forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID) forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID) - forums = append(forums, &forum) + addForum(&forum) } - err = rows.Err() - if err != nil { - return err - } - sfs.forums = forums - sfs.forumCapCount = i - - return nil + mfs.forumCount = i + mfs.forumView.Store(forumView) + return rows.Err() } -func (sfs *StaticForumStore) DirtyGet(id int) *Forum { - if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") { +// TODO: Hide social groups too +func (mfs *MemoryForumStore) rebuildView() { + var forumView []*Forum + mfs.forums.Range(func(_ interface{}, value interface{}) bool { + forum := value.(*Forum) + if forum.Active && forum.Name != "" { + forumView = append(forumView, forum) + } + return true + }) + mfs.forumView.Store(forumView) +} + +func (mfs *MemoryForumStore) DirtyGet(id int) *Forum { + fint, ok := mfs.forums.Load(id) + forum := fint.(*Forum) + if !ok || forum.Name == "" { return &Forum{ID: -1, Name: ""} } - return sfs.forums[id] + return forum } -func (sfs *StaticForumStore) Get(id int) (*Forum, error) { - if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") { +func (mfs *MemoryForumStore) Get(id int) (*Forum, error) { + fint, ok := mfs.forums.Load(id) + forum := fint.(*Forum) + if !ok || forum.Name == "" { return nil, ErrNoRows } - return sfs.forums[id], nil + return forum, nil } -func (sfs *StaticForumStore) CascadeGet(id int) (*Forum, error) { - if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") { - return nil, ErrNoRows +func (mfs *MemoryForumStore) CascadeGet(id int) (*Forum, error) { + fint, ok := mfs.forums.Load(id) + forum := fint.(*Forum) + if !ok || forum.Name == "" { + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + + forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID) + forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID) + return forum, err } - return sfs.forums[id], nil + return forum, nil } -func (sfs *StaticForumStore) CascadeGetCopy(id int) (forum Forum, err error) { - if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") { - return forum, ErrNoRows +func (mfs *MemoryForumStore) CascadeGetCopy(id int) (Forum, error) { + fint, ok := mfs.forums.Load(id) + forum := fint.(*Forum) + if !ok || forum.Name == "" { + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + + forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID) + forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID) + return *forum, err } - return *sfs.forums[id], nil + return *forum, nil } -func (sfs *StaticForumStore) BypassGet(id int) (*Forum, error) { +func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) { var forum = Forum{ID: id} - err := sfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + + forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID) + forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID) return &forum, err } -func (sfs *StaticForumStore) Load(id int) error { +func (mfs *MemoryForumStore) Load(id int) error { var forum = Forum{ID: id} - err := sfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) + err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime) if err != nil { return err } - sfs.Set(&forum) + forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID) + forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID) + + mfs.Set(&forum) return nil } -// TO-DO: Set should be able to add new indices not just replace existing ones for consistency with UserStore and TopicStore -func (sfs *StaticForumStore) Set(forum *Forum) error { - forumUpdateMutex.Lock() - if !sfs.Exists(forum.ID) { - forumUpdateMutex.Unlock() +func (mfs *MemoryForumStore) Set(forum *Forum) error { + if !mfs.Exists(forum.ID) { return ErrNoRows } - sfs.forums[forum.ID] = forum - forumUpdateMutex.Unlock() + mfs.forums.Store(forum.ID, forum) + mfs.rebuildView() return nil } -func (sfs *StaticForumStore) GetAll() ([]*Forum, error) { - return sfs.forums, nil +func (mfs *MemoryForumStore) GetAll() (forumView []*Forum, err error) { + mfs.forums.Range(func(_ interface{}, value interface{}) bool { + forumView = append(forumView, value.(*Forum)) + return true + }) + return forumView, nil } -// TO-DO: Implement sub-forums. -/*func (sfs *StaticForumStore) GetChildren(parentID int, parentType string) ([]*Forum,error) { - return nil, nil +func (mfs *MemoryForumStore) GetAllIDs() (ids []int, err error) { + mfs.forums.Range(func(_ interface{}, value interface{}) bool { + ids = append(ids, value.(*Forum).ID) + return true + }) + return ids, nil } -func (sfs *StaticForumStore) GetFirstChild(parentID int, parentType string) (*Forum,error) { - return nil, nil -}*/ -// We can cheat slightly, as the StaticForumStore has all the IDs under the cap ;) -// Should we cache this? Well, it's only really used for superadmins right now. -func (sfs *StaticForumStore) GetAllIDs() ([]int, error) { - var max = sfs.forumCapCount - var ids = make([]int, max) - for i := 0; i < max; i++ { - ids[i] = i +func (mfs *MemoryForumStore) GetAllVisible() ([]*Forum, error) { + return mfs.forumView.Load().([]*Forum), nil +} + +func (mfs *MemoryForumStore) GetAllVisibleIDs() ([]int, error) { + forumView := mfs.forumView.Load().([]*Forum) + var ids = make([]int, len(forumView)) + for i := 0; i < len(forumView); i++ { + ids[i] = forumView[i].ID } return ids, nil } -func (sfs *StaticForumStore) Exists(id int) bool { - return (id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "" +// TODO: Implement sub-forums. +/*func (mfs *MemoryForumStore) GetChildren(parentID int, parentType string) ([]*Forum,error) { + return nil, nil +} +func (mfs *MemoryForumStore) GetFirstChild(parentID int, parentType string) (*Forum,error) { + return nil, nil +}*/ + +func (mfs *MemoryForumStore) Exists(id int) bool { + forum, ok := mfs.forums.Load(id) + return ok && forum.(*Forum).Name != "" } -func (sfs *StaticForumStore) Delete(id int) error { +// TODO: Batch deletions with name blanking? Is this necessary? +func (mfs *MemoryForumStore) Delete(id int) { + mfs.forums.Delete(id) + mfs.rebuildView() +} + +func (mfs *MemoryForumStore) CascadeDelete(id int) error { forumUpdateMutex.Lock() - if !sfs.Exists(id) { - forumUpdateMutex.Unlock() - return nil - } - sfs.forums[id].Name = "" - forumUpdateMutex.Unlock() - return nil -} - -func (sfs *StaticForumStore) CascadeDelete(id int) error { - forum, err := sfs.CascadeGet(id) + defer forumUpdateMutex.Unlock() + _, err := mfs.delete.Exec(id) if err != nil { return err } - - forumUpdateMutex.Lock() - _, err = delete_forum_stmt.Exec(id) - if err != nil { - return err - } - forum.Name = "" - forumUpdateMutex.Unlock() + mfs.Delete(id) return nil } -func (sfs *StaticForumStore) IncrementTopicCount(id int) error { - forum, err := sfs.CascadeGet(id) +func (mfs *MemoryForumStore) IncrementTopicCount(id int) error { + forum, err := mfs.CascadeGet(id) if err != nil { return err } @@ -240,8 +287,8 @@ func (sfs *StaticForumStore) IncrementTopicCount(id int) error { return nil } -func (sfs *StaticForumStore) DecrementTopicCount(id int) error { - forum, err := sfs.CascadeGet(id) +func (mfs *MemoryForumStore) DecrementTopicCount(id int) error { + forum, err := mfs.CascadeGet(id) if err != nil { return err } @@ -253,19 +300,19 @@ func (sfs *StaticForumStore) DecrementTopicCount(id int) error { return nil } -// TO-DO: Have a pointer to the last topic rather than storing it on the forum itself -func (sfs *StaticForumStore) UpdateLastTopic(topic_name string, tid int, username string, uid int, time string, fid int) error { - forum, err := sfs.CascadeGet(fid) +// TODO: Have a pointer to the last topic rather than storing it on the forum itself +func (mfs *MemoryForumStore) UpdateLastTopic(topicName string, tid int, username string, uid int, time string, fid int) error { + forum, err := mfs.CascadeGet(fid) if err != nil { return err } - _, err = update_forum_cache_stmt.Exec(topic_name, tid, username, uid, fid) + _, err = update_forum_cache_stmt.Exec(topicName, tid, username, uid, fid) if err != nil { return err } - forum.LastTopic = topic_name + forum.LastTopic = topicName forum.LastTopicID = tid forum.LastReplyer = username forum.LastReplyerID = uid @@ -274,30 +321,7 @@ func (sfs *StaticForumStore) UpdateLastTopic(topic_name string, tid int, usernam return nil } -func (sfs *StaticForumStore) CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error) { - var fid int - err := forum_entry_exists_stmt.QueryRow().Scan(&fid) - if err != nil && err != ErrNoRows { - return 0, err - } - if err != ErrNoRows { - forumUpdateMutex.Lock() - _, err = update_forum_stmt.Exec(forumName, forumDesc, active, preset, fid) - if err != nil { - return fid, err - } - forum, err := sfs.Get(fid) - if err != nil { - return 0, ErrCacheDesync - } - forum.Name = forumName - forum.Desc = forumDesc - forum.Active = active - forum.Preset = preset - forumUpdateMutex.Unlock() - return fid, nil - } - +func (mfs *MemoryForumStore) CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error) { forumCreateMutex.Lock() res, err := create_forum_stmt.Exec(forumName, forumDesc, active, preset) if err != nil { @@ -308,34 +332,29 @@ func (sfs *StaticForumStore) CreateForum(forumName string, forumDesc string, act if err != nil { return 0, err } - fid = int(fid64) + fid := int(fid64) - sfs.forums = append(sfs.forums, &Forum{fid, buildForumUrl(nameToSlug(forumName), fid), forumName, forumDesc, active, preset, 0, "", 0, "", "", 0, "", 0, ""}) - sfs.forumCapCount++ + mfs.forums.Store(fid, &Forum{fid, buildForumURL(nameToSlug(forumName), fid), forumName, forumDesc, active, preset, 0, "", 0, "", "", 0, "", 0, ""}) + mfs.forumCount++ - // TO-DO: Add a GroupStore. How would it interact with the ForumStore? + // TODO: Add a GroupStore. How would it interact with the ForumStore? permmapToQuery(presetToPermmap(preset), fid) forumCreateMutex.Unlock() + + if active { + mfs.rebuildView() + } return fid, nil } -func (sfs *StaticForumStore) fillForumIDGap(biggerID int, smallerID int) { - dummy := Forum{ID: 0, Name: "", Active: false, Preset: "all"} - for i := smallerID; i > biggerID; i++ { - sfs.forums = append(sfs.forums, &dummy) - } -} - -// TO-DO: Get the total count of forums in the forum store minus the blanked forums rather than doing a heavy query for this? +// TODO: Get the total count of forums in the forum store minus the blanked forums rather than doing a heavy query for this? // GetGlobalCount returns the total number of forums -func (sfs *StaticForumStore) GetGlobalCount() (fcount int) { - err := sfs.forumCount.QueryRow().Scan(&fcount) +func (mfs *MemoryForumStore) GetGlobalCount() (fcount int) { + err := mfs.getForumCount.QueryRow().Scan(&fcount) if err != nil { LogError(err) } return fcount } -// TO-DO: Work on MapForumStore - -// TO-DO: Work on SqlForumStore +// TODO: Work on SqlForumStore diff --git a/gen_mysql.go b/gen_mysql.go index 481dc247..bd89378f 100644 --- a/gen_mysql.go +++ b/gen_mysql.go @@ -43,6 +43,7 @@ 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_sync_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 @@ -96,7 +97,6 @@ var increment_user_bigposts_stmt *sql.Stmt var increment_user_megaposts_stmt *sql.Stmt var increment_user_topics_stmt *sql.Stmt var edit_profile_reply_stmt *sql.Stmt -var delete_forum_stmt *sql.Stmt var update_forum_stmt *sql.Stmt var update_setting_stmt *sql.Stmt var update_plugin_stmt *sql.Stmt @@ -110,6 +110,7 @@ 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 bump_sync_stmt *sql.Stmt var delete_reply_stmt *sql.Stmt var delete_topic_stmt *sql.Stmt var delete_profile_reply_stmt *sql.Stmt @@ -340,6 +341,12 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing get_sync statement.") + get_sync_stmt, err = db.Prepare("SELECT `last_update` FROM `sync`") + 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 { @@ -658,12 +665,6 @@ func _gen_mysql() (err error) { return err } - log.Print("Preparing delete_forum statement.") - 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` = ?") if err != nil { @@ -742,6 +743,12 @@ func _gen_mysql() (err error) { return err } + log.Print("Preparing bump_sync statement.") + bump_sync_stmt, err = db.Prepare("UPDATE `sync` SET `last_update` = UTC_TIMESTAMP()") + 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 { diff --git a/gen_pgsql.go b/gen_pgsql.go index 697778ee..f88ec30c 100644 --- a/gen_pgsql.go +++ b/gen_pgsql.go @@ -32,7 +32,6 @@ var increment_user_bigposts_stmt *sql.Stmt var increment_user_megaposts_stmt *sql.Stmt var increment_user_topics_stmt *sql.Stmt var edit_profile_reply_stmt *sql.Stmt -var delete_forum_stmt *sql.Stmt var update_forum_stmt *sql.Stmt var update_setting_stmt *sql.Stmt var update_plugin_stmt *sql.Stmt @@ -46,6 +45,7 @@ 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 bump_sync_stmt *sql.Stmt // nolint func _gen_pgsql() (err error) { @@ -203,12 +203,6 @@ func _gen_pgsql() (err error) { return err } - log.Print("Preparing delete_forum statement.") - 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` = ?") if err != nil { @@ -286,6 +280,12 @@ func _gen_pgsql() (err error) { if err != nil { return err } + + log.Print("Preparing bump_sync statement.") + bump_sync_stmt, err = db.Prepare("UPDATE `sync` SET `last_update` = LOCALTIMESTAMP()") + if err != nil { + return err + } return nil } diff --git a/gen_router.go b/gen_router.go index 53b7d4f9..1cffeae5 100644 --- a/gen_router.go +++ b/gen_router.go @@ -65,9 +65,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { if dev.SuperDebug { log.Print("before route_static") - log.Print("prefix:", prefix) - log.Print("req.URL.Path:", req.URL.Path) - log.Print("extra_data:", extra_data) + log.Print("prefix: ", prefix) + log.Print("req.URL.Path: ", req.URL.Path) + log.Print("extra_data: ", extra_data) + log.Print("req.Referer(): ", req.Referer()) } if prefix == "/static" { @@ -103,6 +104,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { case "/forum": route_forum(w,req,user,extra_data) return + case "/theme": + route_change_theme(w,req,user) + return case "/report": switch(req.URL.Path) { case "/report/submit/": @@ -230,7 +234,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return case "": // Stop the favicons, robots.txt file, etc. resolving to the topics list - // TO-DO: Add support for favicons and robots.txt files + // TODO: Add support for favicons and robots.txt files switch(extra_data) { case "robots.txt": route_robots_txt(w,req) diff --git a/general_test.go b/general_test.go index d9e0ae3b..1e50c251 100644 --- a/general_test.go +++ b/general_test.go @@ -56,8 +56,8 @@ func gloinit() error { users = NewMemoryUserStore(config.UserCacheCapacity) topics = NewMemoryTopicStore(config.TopicCacheCapacity) } else { - users = NewSqlUserStore() - topics = NewSqlTopicStore() + users = NewSQLUserStore() + topics = NewSQLTopicStore() } log.Print("Loading the static files.") @@ -67,7 +67,7 @@ func gloinit() error { } auth = NewDefaultAuth() - err = initWordFilters() + err = LoadWordFilters() if err != nil { return err } @@ -84,7 +84,7 @@ func init() { } } -// TO-DO: Swap out LocalError for a panic for this? +// TODO: Swap out LocalError for a panic for this? func BenchmarkTopicAdminRouteParallel(b *testing.B) { b.ReportAllocs() if !gloinited { @@ -143,7 +143,7 @@ func BenchmarkTopicGuestRouteParallel(b *testing.B) { }) } -// TO-DO: Make these routes compatible with the changes to the router +// TODO: Make these routes compatible with the changes to the router /* func BenchmarkForumsAdminRouteParallel(b *testing.B) { b.ReportAllocs() @@ -1091,7 +1091,7 @@ func TestLevels(t *testing.T) { } } -// TO-DO: Make this compatible with the changes to the router +// TODO: Make this compatible with the changes to the router /* func TestStaticRoute(t *testing.T) { if !gloinited { @@ -1166,7 +1166,7 @@ func TestStaticRoute(t *testing.T) { t.Print("No problems found in the topic-guest route!") }*/ -// TO-DO: Make these routes compatible with the changes to the router +// TODO: Make these routes compatible with the changes to the router /* func TestForumsAdminRoute(t *testing.T) { if !gloinited { diff --git a/install/pgsql.go b/install/pgsql.go index b22b322c..03bee8b0 100644 --- a/install/pgsql.go +++ b/install/pgsql.go @@ -31,12 +31,12 @@ func _initPgsql() (err error) { } fmt.Println("Successfully connected to the database") - // TO-DO: Create the database, if it doesn't exist + // TODO: Create the database, if it doesn't exist return nil } func _pgEscapeBit(bit string) string { - // TO-DO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible? + // TODO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible? return strings.Replace(bit, "'", "\\'", -1) } diff --git a/main.go b/main.go index eeb0493f..97ab107c 100644 --- a/main.go +++ b/main.go @@ -52,14 +52,14 @@ func init() { wordFilterBox.Store(WordFilterBox(make(map[int]WordFilter))) } -func initWordFilters() error { +func LoadWordFilters() error { rows, err := get_word_filters_stmt.Query() if err != nil { return err } defer rows.Close() - var wordFilters = wordFilterBox.Load().(WordFilterBox) + var wordFilters = WordFilterBox(make(map[int]WordFilter)) var wfid int var find string var replacement string @@ -92,8 +92,8 @@ func processConfig() { } func main() { - // TO-DO: Have a file for each run with the time/date the server started as the file name? - // TO-DO: Log panics with recover() + // TODO: Have a file for each run with the time/date the server started as the file name? + // TODO: Log panics with recover() f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) if err != nil { log.Fatal(err) @@ -137,8 +137,8 @@ func main() { users = NewMemoryUserStore(config.UserCacheCapacity) topics = NewMemoryTopicStore(config.TopicCacheCapacity) } else { - users = NewSqlUserStore() - topics = NewSqlTopicStore() + users = NewSQLUserStore() + topics = NewSQLTopicStore() } log.Print("Loading the static files.") @@ -156,7 +156,7 @@ func main() { log.Print("Initialising the authentication system") auth = NewDefaultAuth() - err = initWordFilters() + err = LoadWordFilters() if err != nil { log.Fatal(err) } @@ -174,16 +174,23 @@ func main() { 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 - // TO-DO: Manage the TopicStore, UserStore, and ForumStore - // 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? + + // TODO: Handle delayed moderation tasks + // TODO: Handle the daily clean-up. Move this to a 24 hour task? + + // Sync with the database, if there are any changes + err = handleServerSync() + if err != nil { + LogError(err) + } + + // TODO: Manage the TopicStore, UserStore, and ForumStore + // TODO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high + // TODO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task? case <-fifteenMinuteTicker.C: - // TO-DO: Automatically lock topics, if they're really old, and the associated setting is enabled. - // TO-DO: Publish scheduled posts. - // TO-DO: Delete the empty users_groups_scheduler entries + // TODO: Automatically lock topics, if they're really old, and the associated setting is enabled. + // TODO: Publish scheduled posts. + // TODO: Delete the empty users_groups_scheduler entries } } }() @@ -286,12 +293,13 @@ func main() { // pprof.StopCPUProfile() //} - // TO-DO: Let users run *both* HTTP and HTTPS + // TODO: Let users run *both* HTTP and HTTPS log.Print("Initialising the HTTP server") if !site.EnableSsl { if site.Port == "" { site.Port = "80" } + log.Print("Listening on port " + site.Port) err = http.ListenAndServe(":"+site.Port, router) } else { if site.Port == "" { @@ -299,14 +307,16 @@ func main() { } if site.Port == "80" || site.Port == "443" { // We should also run the server on port 80 - // TO-DO: Redirect to port 443 + // TODO: Redirect to port 443 go func() { - err = http.ListenAndServe(":80", &HttpsRedirect{}) + log.Print("Listening on port 80") + err = http.ListenAndServe(":80", &HTTPSRedirect{}) if err != nil { log.Fatal(err) } }() } + log.Print("Listening on port " + site.Port) err = http.ListenAndServeTLS(":"+site.Port, config.SslFullchain, config.SslPrivkey, router) } diff --git a/misc_test.go b/misc_test.go index 1b9c40dc..e95e00f2 100644 --- a/misc_test.go +++ b/misc_test.go @@ -3,7 +3,7 @@ package main import "strconv" import "testing" -// TO-DO: Generate a test database to work with rather than a live one +// TODO: Generate a test database to work with rather than a live one func TestUserStore(t *testing.T) { if !gloinited { err := gloinit() @@ -18,14 +18,14 @@ func TestUserStore(t *testing.T) { var user *User var err error - user, err = users.CascadeGet(-1) + _, err = users.CascadeGet(-1) if err == nil { t.Error("UID #-1 shouldn't exist") } else if err != ErrNoRows { t.Fatal(err) } - user, err = users.CascadeGet(0) + _, err = users.CascadeGet(0) if err == nil { t.Error("UID #0 shouldn't exist") } else if err != ErrNoRows { @@ -43,7 +43,7 @@ func TestUserStore(t *testing.T) { t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.") } - // TO-DO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message? + // TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message? var userList map[int]*User _, err = users.BulkCascadeGetMap([]int{-1}) if err == nil { @@ -90,7 +90,7 @@ func TestForumStore(t *testing.T) { var forum *Forum var err error - forum, err = fstore.CascadeGet(-1) + _, err = fstore.CascadeGet(-1) if err == nil { t.Error("FID #-1 shouldn't exist") } else if err != ErrNoRows { @@ -131,6 +131,8 @@ func TestForumStore(t *testing.T) { } else if err != nil { t.Fatal(err) } + + _ = forum } func TestSlugs(t *testing.T) { diff --git a/mod_routes.go b/mod_routes.go index 472b840e..fe336061 100644 --- a/mod_routes.go +++ b/mod_routes.go @@ -10,6 +10,8 @@ import ( "time" ) +// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes +// TODO: Disable stat updates in posts handled by plugin_socialgroups func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) { //log.Print("in route_edit_topic") err := r.ParseForm() @@ -35,7 +37,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) { return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, oldTopic.ParentID) if !ok { return @@ -108,6 +110,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) { } } +// TODO: Disable stat updates in posts handled by plugin_socialgroups func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) { tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):]) if err != nil { @@ -124,7 +127,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) { return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID) if !ok { return @@ -161,9 +164,15 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) { //log.Print("Topic #" + strconv.Itoa(tid) + " was deleted by User #" + strconv.Itoa(user.ID)) http.Redirect(w, r, "/", http.StatusSeeOther) - wcount := wordCount(topic.Content) - err = decrease_post_user_stats(wcount, topic.CreatedBy, true, user) - if err != nil { + topicCreator, err := users.CascadeGet(topic.CreatedBy) + if err == nil { + wcount := wordCount(topic.Content) + err = topicCreator.decreasePostStats(wcount, true) + if err != nil { + InternalError(err, w) + return + } + } else if err != ErrNoRows { InternalError(err, w) return } @@ -192,7 +201,7 @@ func route_stick_topic(w http.ResponseWriter, r *http.Request, user User) { return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID) if !ok { return @@ -248,7 +257,7 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request, user User) { return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID) if !ok { return @@ -288,6 +297,8 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request, user User) { http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) } +// TODO: Disable stat updates in posts handled by plugin_socialgroups +// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User) { err := r.ParseForm() if err != nil { @@ -327,7 +338,7 @@ func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User) return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, fid) if !ok { return @@ -344,6 +355,7 @@ func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User) } } +// TODO: Disable stat updates in posts handled by plugin_socialgroups func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User) { err := r.ParseForm() if err != nil { @@ -377,7 +389,7 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, fid) if !ok { return @@ -399,9 +411,15 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User w.Write(successJSONBytes) } - wcount := wordCount(reply.Content) - err = decrease_post_user_stats(wcount, reply.CreatedBy, false, user) - if err != nil { + replyCreator, err := users.CascadeGet(reply.CreatedBy) + if err == nil { + wcount := wordCount(reply.Content) + err = replyCreator.decreasePostStats(wcount, false) + if err != nil { + InternalErrorJSQ(err, w, r, isJs) + return + } + } else if err != ErrNoRows { InternalErrorJSQ(err, w, r, isJs) return } @@ -524,7 +542,7 @@ func route_ips(w http.ResponseWriter, r *http.Request, user User) { ip := html.EscapeString(r.URL.Path[len("/users/ips/"):]) var uid int - var reqUserList map[int]bool = make(map[int]bool) + var reqUserList = make(map[int]bool) rows, err := find_users_by_ip_users_stmt.Query(ip) if err != nil { @@ -590,14 +608,14 @@ func route_ips(w http.ResponseWriter, r *http.Request, user User) { } // Convert the user ID map to a slice, then bulk load the users - var idSlice []int = make([]int, len(reqUserList)) + var idSlice = make([]int, len(reqUserList)) var i int for userID := range reqUserList { idSlice[i] = userID i++ } - // TO-DO: What if a user is deleted via the Control Panel? + // TODO: What if a user is deleted via the Control Panel? userList, err := users.BulkCascadeGetMap(idSlice) if err != nil { InternalError(err, w) @@ -616,7 +634,7 @@ func route_ips(w http.ResponseWriter, r *http.Request, user User) { } } -// TO-DO: This is being replaced with the new ban route system +// TODO: 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 { diff --git a/mysql.go b/mysql.go index a63b1110..648b92fb 100644 --- a/mysql.go +++ b/mysql.go @@ -66,7 +66,7 @@ func _initDatabase() (err error) { return err } - // TO-DO: Is there a less noisy way of doing this for tests? + // TODO: Is there a less noisy way of doing this for tests? 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 { diff --git a/mysql.sql b/mysql.sql index df3d4537..4d02afe3 100644 --- a/mysql.sql +++ b/mysql.sql @@ -24,7 +24,7 @@ CREATE TABLE `forums`( `active` tinyint DEFAULT 1 not null, `topicCount` int DEFAULT 0 not null, `preset` varchar(100) DEFAULT '' not null, - `parentID` int DEFAULT 0 not null, /* TO-DO: Add support for subforums */ + `parentID` int DEFAULT 0 not null, /* TODO: Add support for subforums */ `parentType` varchar(50) DEFAULT '' not null, `lastTopic` varchar(100) DEFAULT '' not null, `lastTopicID` int DEFAULT 0 not null, @@ -181,16 +181,13 @@ CREATE TABLE `administration_logs`( `doneAt` datetime not null ); -CREATE TABLE `sync`( - `last_update` int not null, - `node_id` int not null -); +INSERT INTO sync(`last_update`) VALUES (UTC_TIMESTAMP()); INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool'); INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3'); INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int'); INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int'); -/* TO-DO: Implement the html-attribute setting type before deploying this */ +/* TODO: Implement the html-attribute setting type before deploying this */ /*INSERT INTO settings(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');*/ INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1); INSERT INTO emails(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1); diff --git a/no_websockets.go b/no_websockets.go index edc901d7..bdddc091 100644 --- a/no_websockets.go +++ b/no_websockets.go @@ -6,33 +6,33 @@ import "errors" import "net/http" var wsHub WS_Hub -var wsNouser error = errors.New("This user isn't connected via WebSockets") +var errWsNouser = errors.New("This user isn't connected via WebSockets") type WS_Hub struct { } -func (_ *WS_Hub) guest_count() int { +func (_ *WS_Hub) guestCount() int { return 0 } -func (_ *WS_Hub) user_count() int { +func (_ *WS_Hub) userCount() int { return 0 } -func (hub *WS_Hub) broadcast_message(_ string) error { +func (hub *WS_Hub) broadcastMessage(_ string) error { return nil } -func (hub *WS_Hub) push_message(_ int, _ string) error { - return wsNouser +func (hub *WS_Hub) pushMessage(_ int, _ string) error { + return errWsNouser } -func (hub *WS_Hub) push_alert(_ int, _ int, _ string, _ string, _ int, _ int, _ int) error { - return wsNouser +func (hub *WS_Hub) pushAlert(_ int, _ int, _ string, _ string, _ int, _ int, _ int) error { + return errWsNouser } -func (hub *WS_Hub) push_alerts(_ []int, _ int, _ string, _ string, _ int, _ int, _ int) error { - return wsNouser +func (hub *WS_Hub) pushAlerts(_ []int, _ int, _ string, _ string, _ int, _ int, _ int) error { + return errWsNouser } func route_websockets(_ http.ResponseWriter, _ *http.Request, _ User) { diff --git a/pages.go b/pages.go index b78031fe..2d8ece76 100644 --- a/pages.go +++ b/pages.go @@ -15,11 +15,12 @@ type HeaderVars struct { Widgets PageWidgets Site *Site Settings map[string]interface{} + Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed ThemeName string ExtData ExtData } -// TO-DO: Add this to routes which don't use templates. E.g. Json APIs. +// TODO: Add this to routes which don't use templates. E.g. Json APIs. type HeaderLite struct { Site *Site Settings SettingBox @@ -36,7 +37,7 @@ type PageWidgets struct { items map[string]interface{} // Key: pluginname }*/ -// TO-DO: Add a ExtDataHolder interface with methods for manipulating the contents? +// TODO: Add a ExtDataHolder interface with methods for manipulating the contents? type ExtData struct { items map[string]interface{} // Key: pluginname sync.RWMutex @@ -531,7 +532,7 @@ func parseMessage(msg string /*, user User*/) string { } outbytes = append(outbytes, urlOpen...) - var urlBit = []byte(buildForumUrl("", fid)) + var urlBit = []byte(buildForumURL("", fid)) outbytes = append(outbytes, urlBit...) outbytes = append(outbytes, urlOpen2...) var fidBit = []byte("#fid-" + strconv.Itoa(fid)) @@ -539,7 +540,7 @@ func parseMessage(msg string /*, user User*/) string { outbytes = append(outbytes, urlClose...) lastItem = i } else { - // TO-DO: Forum Shortcode Link + // TODO: Forum Shortcode Link } } else if msgbytes[i] == '@' { //log.Print("IN @") @@ -798,7 +799,7 @@ func coerceIntBytes(data []byte) (res int, length int) { return conv, i } -// TO-DO: Write tests for this +// TODO: Write tests for this func paginate(count int, perPage int, maxPages int) []int { if count < perPage { return []int{1} @@ -815,7 +816,7 @@ func paginate(count int, perPage int, maxPages int) []int { return out } -// TO-DO: Write tests for this +// TODO: Write tests for this func pageOffset(count int, page int, perPage int) (int, int, int) { var offset int lastPage := (count / perPage) + 1 diff --git a/panel_routes.go b/panel_routes.go index 5c2f2889..2ff39233 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -253,6 +253,7 @@ func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request, us http.Redirect(w, r, "/panel/forums/", http.StatusSeeOther) } +// TODO: Revamp this func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User, sfid string) { headerVars, stats, ok := PanelSessionCheck(w, r, &user) if !ok { @@ -282,8 +283,8 @@ func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User return } - confirm_msg := "Are you sure you want to delete the '" + forum.Name + "' forum?" - yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirm_msg} + confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?" + yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg} pi := PanelPage{"Delete Forum", user, headerVars, stats, tList, yousure} if preRenderHooks["pre_render_panel_delete_forum"] != nil { @@ -406,10 +407,10 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request, user return } - forum_name := r.PostFormValue("forum_name") - forum_desc := r.PostFormValue("forum_desc") - forum_preset := stripInvalidPreset(r.PostFormValue("forum_preset")) - forum_active := r.PostFormValue("forum_active") + forumName := r.PostFormValue("forum_name") + forumDesc := r.PostFormValue("forum_desc") + forumPreset := stripInvalidPreset(r.PostFormValue("forum_preset")) + forumActive := r.PostFormValue("forum_active") forum, err := fstore.CascadeGet(fid) if err == ErrNoRows { @@ -420,40 +421,40 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request, user return } - if forum_name == "" { - forum_name = forum.Name + if forumName == "" { + forumName = forum.Name } var active bool - if forum_active == "" { + if forumActive == "" { active = forum.Active - } else if forum_active == "1" || forum_active == "Show" { + } else if forumActive == "1" || forumActive == "Show" { active = true } else { active = false } forumUpdateMutex.Lock() - _, err = update_forum_stmt.Exec(forum_name, forum_desc, active, forum_preset, fid) + _, err = update_forum_stmt.Exec(forumName, forumDesc, active, forumPreset, fid) if err != nil { InternalErrorJSQ(err, w, r, isJs) return } - if forum.Name != forum_name { - forum.Name = forum_name + if forum.Name != forumName { + forum.Name = forumName } - if forum.Desc != forum_desc { - forum.Desc = forum_desc + if forum.Desc != forumDesc { + forum.Desc = forumDesc } if forum.Active != active { forum.Active = active } - if forum.Preset != forum_preset { - forum.Preset = forum_preset + if forum.Preset != forumPreset { + forum.Preset = forumPreset } forumUpdateMutex.Unlock() - permmapToQuery(presetToPermmap(forum_preset), fid) + permmapToQuery(presetToPermmap(forumPreset), fid) if !isJs { http.Redirect(w, r, "/panel/forums/", http.StatusSeeOther) @@ -495,8 +496,8 @@ func route_panel_forums_edit_perms_submit(w http.ResponseWriter, r *http.Request return } - perm_preset := stripInvalidGroupForumPreset(r.PostFormValue("perm_preset")) - fperms, changed := groupForumPresetToForumPerms(perm_preset) + permPreset := stripInvalidGroupForumPreset(r.PostFormValue("perm_preset")) + fperms, changed := groupForumPresetToForumPerms(permPreset) forum, err := fstore.CascadeGet(fid) if err == ErrNoRows { @@ -519,7 +520,7 @@ func route_panel_forums_edit_perms_submit(w http.ResponseWriter, r *http.Request return } - _, err = add_forum_perms_to_group_stmt.Exec(gid, fid, perm_preset, perms) + _, err = add_forum_perms_to_group_stmt.Exec(gid, fid, permPreset, perms) if err != nil { InternalErrorJSQ(err, w, r, isJs) return @@ -552,7 +553,7 @@ func route_panel_settings(w http.ResponseWriter, r *http.Request, user User) { } //log.Print("headerVars.Settings",headerVars.Settings) - var settingList map[string]interface{} = make(map[string]interface{}) + var settingList = make(map[string]interface{}) rows, err := get_settings_stmt.Query() if err != nil { InternalError(err, w) @@ -560,6 +561,7 @@ func route_panel_settings(w http.ResponseWriter, r *http.Request, user User) { } defer rows.Close() + // nolint need the type so people viewing this file understand what it returns without visiting setting.go var settingLabels map[string]string = GetAllSettingLabels() var sname, scontent, stype string for rows.Next() { @@ -722,7 +724,7 @@ func route_panel_word_filters(w http.ResponseWriter, r *http.Request, user User) return } - var filterList WordFilterBox = wordFilterBox.Load().(WordFilterBox) + var filterList = wordFilterBox.Load().(WordFilterBox) pi := PanelPage{"Word Filter Manager", user, headerVars, stats, tList, filterList} if preRenderHooks["pre_render_panel_word_filters"] != nil { if runPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) { @@ -766,14 +768,14 @@ func route_panel_word_filters_create(w http.ResponseWriter, r *http.Request, use InternalErrorJSQ(err, w, r, isJs) return } - lastId, err := res.LastInsertId() + lastID, err := res.LastInsertId() if err != nil { InternalErrorJSQ(err, w, r, isJs) return } - addWordFilter(int(lastId), find, replacement) - http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther) // TO-DO: Return json for JS? + addWordFilter(int(lastID), find, replacement) + http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther) // TODO: Return json for JS? } func route_panel_word_filters_edit(w http.ResponseWriter, r *http.Request, user User, wfid string) { @@ -811,7 +813,7 @@ func route_panel_word_filters_edit_submit(w http.ResponseWriter, r *http.Request PreError("Bad Form", w, r) return } - // TO-DO: Either call it isJs or js rather than flip-flopping back and forth across the routes x.x + // TODO: Either call it isJs or js rather than flip-flopping back and forth across the routes x.x isJs := (r.PostFormValue("isJs") == "1") if !user.Perms.EditSettings { NoPermissionsJSQ(w, r, user, isJs) @@ -943,7 +945,7 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request, user U InternalError(err, w) return } - var has_plugin bool = (err == nil) + var hasPlugin = (err == nil) if plugins[uname].Activate != nil { err = plugins[uname].Activate() @@ -955,7 +957,7 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request, user U //log.Print("err",err) //log.Print("active",active) - if has_plugin { + if hasPlugin { if active { LocalError("The plugin is already active", w, r, user) return @@ -1071,7 +1073,7 @@ func route_panel_plugins_install(w http.ResponseWriter, r *http.Request, user Us InternalError(err, w) return } - var has_plugin bool = (err == nil) + var hasPlugin = (err == nil) if plugins[uname].Install != nil { err = plugins[uname].Install() @@ -1089,7 +1091,7 @@ func route_panel_plugins_install(w http.ResponseWriter, r *http.Request, user Us } } - if has_plugin { + if hasPlugin { _, err = update_plugin_install_stmt.Exec(1, uname) if err != nil { InternalError(err, w) @@ -1139,7 +1141,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User) { } defer rows.Close() - // TO-DO: Add a UserStore method for iterating over global users and global user offsets + // TODO: Add a UserStore method for iterating over global users and global user offsets for rows.Next() { puser := User{ID: 0} err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.IsSuperAdmin, &puser.Avatar) @@ -1485,7 +1487,7 @@ func route_panel_groups_edit_perms(w http.ResponseWriter, r *http.Request, user return } - // TO-DO: Load the phrases in bulk for efficiency? + // TODO: Load the phrases in bulk for efficiency? var localPerms []NameLangToggle localPerms = append(localPerms, NameLangToggle{"ViewTopic", GetLocalPermPhrase("ViewTopic"), group.Perms.ViewTopic}) localPerms = append(localPerms, NameLangToggle{"LikeItem", GetLocalPermPhrase("LikeItem"), group.Perms.LikeItem}) @@ -1877,7 +1879,9 @@ func route_panel_themes_set_default(w http.ResponseWriter, r *http.Request, user } } - // TO-DO: Make this less racey + // TODO: Make this less racey + changeDefaultThemeMutex.Lock() + defaultTheme := defaultThemeBox.Load().(string) _, err = update_theme_stmt.Exec(0, defaultTheme) if err != nil { InternalError(err, w) @@ -1896,9 +1900,10 @@ func route_panel_themes_set_default(w http.ResponseWriter, r *http.Request, user dTheme.Active = false themes[defaultTheme] = dTheme - defaultTheme = uname // TO-DO: Make this less racey + defaultThemeBox.Store(uname) resetTemplateOverrides() mapThemeTemplates(theme) + changeDefaultThemeMutex.Unlock() http.Redirect(w, r, "/panel/themes/", http.StatusSeeOther) } diff --git a/permissions.go b/permissions.go index 69bac172..f127a213 100644 --- a/permissions.go +++ b/permissions.go @@ -508,7 +508,7 @@ func stripInvalidPreset(preset string) string { } } -// TO-DO: Move this into the phrase system? +// TODO: Move this into the phrase system? func presetToLang(preset string) string { switch preset { case "all": @@ -530,6 +530,7 @@ func presetToLang(preset string) string { } } +// TODO: Is this racey? func rebuildGroupPermissions(gid int) error { var permstr []byte log.Print("Reloading a group") @@ -538,15 +539,15 @@ func rebuildGroupPermissions(gid int) error { return err } - tmp_perms := Perms{ + tmpPerms := Perms{ //ExtData: make(map[string]bool), } - err = json.Unmarshal(permstr, &tmp_perms) + err = json.Unmarshal(permstr, &tmpPerms) if err != nil { return err } - groups[gid].Perms = tmp_perms + groups[gid].Perms = tmpPerms return nil } @@ -558,7 +559,7 @@ func overridePerms(perms *Perms, status bool) { } } -// TO-DO: We need a better way of overriding forum perms rather than setting them one by one +// TODO: We need a better way of overriding forum perms rather than setting them one by one func overrideForumPerms(perms *Perms, status bool) { perms.ViewTopic = status perms.LikeItem = status diff --git a/pgsql.go b/pgsql.go index bb43de30..a3b76d09 100644 --- a/pgsql.go +++ b/pgsql.go @@ -10,7 +10,7 @@ import "database/sql" import _ "github.com/lib/pq" import "./query_gen/lib" -// TO-DO: Add support for SSL for all database drivers, not just pgsql +// TODO: Add support for SSL for all database drivers, not just pgsql var db_sslmode = "disable" // verify-full var get_activity_feed_by_watcher_stmt *sql.Stmt var get_activity_count_by_watcher_stmt *sql.Stmt @@ -24,7 +24,7 @@ func init() { } func _init_database() (err error) { - // TO-DO: Investigate connect_timeout to see what it does exactly and whether it's relevant to us + // TODO: Investigate connect_timeout to see what it does exactly and whether it's relevant to us var _dbpassword string if(dbpassword != ""){ _dbpassword = " password='" + _escape_bit(db_config.Password) + "'" @@ -68,6 +68,6 @@ func _init_database() (err error) { } func _escape_bit(bit string) string { - // TO-DO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible? + // TODO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible? return strings.Replace(bit,"'","\\'",-1) } diff --git a/phrases.go b/phrases.go index 94255e0c..3aa9ab28 100644 --- a/phrases.go +++ b/phrases.go @@ -11,7 +11,8 @@ import ( "sync/atomic" ) -// TO-DO: Let the admin edit phrases from inside the Control Panel? How should we persist these? Should we create a copy of the langpack or edit the primaries? Use the changeLangpack mutex for this? +// TODO: Let the admin edit phrases from inside the Control Panel? How should we persist these? Should we create a copy of the langpack or edit the primaries? Use the changeLangpack mutex for this? +// nolint Be quiet megacheck, this *is* used var changeLangpackMutex sync.Mutex var currentLanguage = "english" var currentLangPack atomic.Value @@ -33,7 +34,8 @@ type LanguagePack struct { SettingLabels map[string]string } -// TO-DO: Move the english language pack into it's own file and just keep the common logic here +// TODO: Add the ability to edit language JSON files from the Control Panel and automatically scan the files for changes +// TODO: Move the english language pack into a JSON file and load that on start-up var langpacks = map[string]*LanguagePack{ "english": &LanguagePack{ Name: "english", @@ -135,7 +137,8 @@ func DeletePhrase() { } -// TO-DO: Use atomics to store the pointer of the current active langpack? +// TODO: Use atomics to store the pointer of the current active langpack? +// nolint func ChangeLanguagePack(name string) (exists bool) { changeLangpackMutex.Lock() pack, ok := langpacks[name] diff --git a/plugin_bbcode.go b/plugin_bbcode.go index bf7a27fd..a3325270 100644 --- a/plugin_bbcode.go +++ b/plugin_bbcode.go @@ -203,9 +203,8 @@ func bbcodeParseWithoutCode(msg string) string { msg = bbcodeURLLabel.ReplaceAllString(msg, "$4") msg = bbcodeQuotes.ReplaceAllString(msg, "$1") return bbcodeCode.ReplaceAllString(msg, "$1") - } else { - return string(msgbytes) } + return string(msgbytes) } // Does every type of BBCode @@ -368,7 +367,7 @@ func bbcodeFullParse(msg string) string { goto MainLoop } - // TO-DO: Add support for negative numbers? + // TODO: Add support for negative numbers? if number < 0 { outbytes = append(outbytes, bbcodeNoNegative...) goto MainLoop diff --git a/plugin_markdown.go b/plugin_markdown.go index b87c7ecd..87028eb6 100644 --- a/plugin_markdown.go +++ b/plugin_markdown.go @@ -4,7 +4,7 @@ package main import "regexp" import "strings" -var markdownMaxDepth int = 25 // How deep the parser will go when parsing Markdown strings +var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings var markdownUnclosedElement []byte var markdownBoldTagOpen, markdownBoldTagClose []byte @@ -88,7 +88,7 @@ func _markdownParse(msg string, n int) string { switch msg[index] { case '_': - var startIndex int = index + var startIndex = index if (index + 1) >= len(msg) { break } @@ -111,7 +111,7 @@ func _markdownParse(msg string, n int) string { lastElement = index index-- case '~': - var startIndex int = index + var startIndex = index if (index + 1) >= len(msg) { break } @@ -142,8 +142,8 @@ func _markdownParse(msg string, n int) string { //log.Print("start string(msg[index])",string(msg[index])) //log.Print("start []byte(msg[:index])",[]byte(msg[:index])) - var startIndex int = index - var italic bool = true + var startIndex = index + var italic = true var bold bool if (index + 2) < len(msg) { //log.Print("start index + 1",index + 1) @@ -339,7 +339,7 @@ SwitchLoop: // plugin_markdown doesn't support lists yet, but I want it to be easy to have nested lists when we do have them func markdownSkipList(data string, index int) int { var lastNewline int - var datalen int = len(data) + var datalen = len(data) for ; index < datalen; index++ { SkipListInnerLoop: diff --git a/plugin_socialgroups.go b/plugin_socialgroups.go index b172c94c..418c0b5f 100644 --- a/plugin_socialgroups.go +++ b/plugin_socialgroups.go @@ -25,7 +25,7 @@ var socialgroupsAttachForumStmt *sql.Stmt var socialgroupsUnattachForumStmt *sql.Stmt var socialgroupsAddMemberStmt *sql.Stmt -// TO-DO: Add a better way of splitting up giant plugins like this +// TODO: Add a better way of splitting up giant plugins like this // SocialGroup is a struct representing a social group type SocialGroup struct { @@ -87,12 +87,12 @@ type SocialGroupMember struct { RankString string /* Member, Mod, Admin, Owner */ PostCount int JoinedAt string - Offline bool // TO-DO: Need to track the online states of members when WebSockets are enabled + Offline bool // TODO: Need to track the online states of members when WebSockets are enabled User User } -// TO-DO: Add a plugin interface instead of having a bunch of argument to AddPlugin? +// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin? func init() { plugins["socialgroups"] = NewPlugin("socialgroups", "Social Groups", "Azareal", "http://github.com/Azareal", "", "", "", initSocialgroups, nil, deactivateSocialgroups, installSocialgroups, nil) } @@ -104,7 +104,7 @@ func initSocialgroups() (err error) { plugins["socialgroups"].AddHook("pre_render_view_forum", socialgroupsPreRenderViewForum) plugins["socialgroups"].AddHook("simple_forum_check_pre_perms", socialgroupsForumCheck) plugins["socialgroups"].AddHook("forum_check_pre_perms", socialgroupsForumCheck) - // TO-DO: Auto-grant this perm to admins upon installation? + // TODO: Auto-grant this perm to admins upon installation? registerPluginPerm("CreateSocialGroup") router.HandleFunc("/groups/", socialgroupsGroupList) router.HandleFunc("/group/", socialgroupsViewGroup) @@ -175,7 +175,7 @@ func deactivateSocialgroups() { _ = socialgroupsAddMemberStmt.Close() } -// TO-DO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process +// TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process func installSocialgroups() error { sgTableStmt, err := qgen.Builder.CreateTable("socialgroups", "utf8mb4", "utf8mb4_general_ci", []qgen.DB_Table_Column{ @@ -229,9 +229,9 @@ func uninstallSocialgroups() error { return nil } -// TO-DO: Do this properly via the widget system +// TODO: Do this properly via the widget system func socialgroupsCommonAreaWidgets(headerVars *HeaderVars) { - // TO-DO: Hot Groups? Featured Groups? Official Groups? + // TODO: Hot Groups? Featured Groups? Official Groups? var b bytes.Buffer var menu = WidgetMenu{"Social Groups", []WidgetMenuItem{ WidgetMenuItem{"Create Group", "/group/create/", false}, @@ -243,15 +243,15 @@ func socialgroupsCommonAreaWidgets(headerVars *HeaderVars) { return } - if themes[defaultTheme].Sidebars == "left" { + if themes[headerVars.ThemeName].Sidebars == "left" { headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes())) - } else if themes[defaultTheme].Sidebars == "right" || themes[defaultTheme].Sidebars == "both" { + } else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" { headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes())) } } -// TO-DO: Do this properly via the widget system -// TO-DO: Make a better more customisable group widget system +// TODO: Do this properly via the widget system +// TODO: Make a better more customisable group widget system func socialgroupsGroupWidgets(headerVars *HeaderVars, sgItem *SocialGroup) (success bool) { return false // Disabled until the next commit @@ -267,9 +267,9 @@ func socialgroupsGroupWidgets(headerVars *HeaderVars, sgItem *SocialGroup) (succ return false } - if themes[defaultTheme].Sidebars == "left" { + if themes[headerVars.ThemeName].Sidebars == "left" { headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes())) - } else if themes[defaultTheme].Sidebars == "right" || themes[defaultTheme].Sidebars == "both" { + } else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" { headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes())) } else { return false @@ -356,7 +356,7 @@ func socialgroupsCreateGroup(w http.ResponseWriter, r *http.Request, user User) if !ok { return } - // TO-DO: Add an approval queue mode for group creation + // TODO: Add an approval queue mode for group creation if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] { NoPermissions(w, r, user) return @@ -371,7 +371,7 @@ func socialgroupsCreateGroup(w http.ResponseWriter, r *http.Request, user User) } func socialgroupsCreateGroupSubmit(w http.ResponseWriter, r *http.Request, user User) { - // TO-DO: Add an approval queue mode for group creation + // TODO: Add an approval queue mode for group creation if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] { NoPermissions(w, r, user) return @@ -561,7 +561,7 @@ func socialgroupsTrowAssign(args ...interface{}) interface{} { return nil } -// TO-DO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from +// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} { var fid = args[2].(int) if fstore.DirtyGet(fid).ParentType == "socialgroup" { @@ -571,9 +571,9 @@ func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} { return nil } -// TO-DO: Add privacy options -// TO-DO: Add support for multiple boards and add per-board simplified permissions -// TO-DO: Take isJs into account for routes which expect JSON responses +// TODO: Add privacy options +// TODO: Add support for multiple boards and add per-board simplified permissions +// TODO: Take isJs into account for routes which expect JSON responses func socialgroupsForumCheck(args ...interface{}) (skip interface{}) { var r = args[1].(*http.Request) var fid = args[3].(*int) @@ -604,10 +604,10 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) { var posts int var joinedAt string - // TO-DO: Group privacy settings. For now, groups are all globally visible + // TODO: Group privacy settings. For now, groups are all globally visible // Clear the default group permissions - // TO-DO: Do this more efficiently, doing it quick and dirty for now to get this out quickly + // TODO: Do this more efficiently, doing it quick and dirty for now to get this out quickly overrideForumPerms(&user.Perms, false) user.Perms.ViewTopic = true @@ -620,7 +620,7 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) { return true } - // TO-DO: Implement bans properly by adding the Local Ban API in the next commit + // TODO: Implement bans properly by adding the Local Ban API in the next commit if rank < 0 { return true } @@ -641,7 +641,7 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) { return false } -// TO-DO: Override redirects? I don't think this is needed quite yet +// TODO: Override redirects? I don't think this is needed quite yet func socialgroupsWidgets(args ...interface{}) interface{} { var zone = args[0].(string) diff --git a/plugin_test.go b/plugin_test.go index 1e76db61..c6e12cdd 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -3,7 +3,7 @@ package main import "strconv" import "testing" -// TO-DO: Replace the soft tabs with hard ones +// TODO: Replace the soft tabs with hard ones // go test -v type ME_Pair struct { diff --git a/public/global.js b/public/global.js index 59dfbc4d..a32e7ebc 100644 --- a/public/global.js +++ b/public/global.js @@ -19,7 +19,7 @@ function bind_to_alerts() { }); } -// TO-DO: Add the ability for users to dismiss alerts +// TODO: Add the ability for users to dismiss alerts function load_alerts(menu_alerts) { var alertListNode = menu_alerts.getElementsByClassName("alertList")[0]; @@ -119,7 +119,7 @@ $(document).ready(function(){ conn.onopen = function() { conn.send("page " + document.location.pathname + '\r'); - // TO-DO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on + // TODO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on Notification.requestPermission(); } conn.onclose = function() { @@ -148,15 +148,15 @@ $(document).ready(function(){ for (var i = 0; i < alertList.length; i++) alist += alertList[i]; //console.log(alist); - // TO-DO: Add support for other alert feeds like PM Alerts + // TODO: Add support for other alert feeds like PM Alerts var general_alerts = document.getElementById("general_alerts"); var alertListNode = general_alerts.getElementsByClassName("alertList")[0]; var alertCounterNode = general_alerts.getElementsByClassName("alert_counter")[0]; alertListNode.innerHTML = alist; alertCounterNode.textContent = alertCount; - // TO-DO: Add some sort of notification queue to avoid flooding the end-user with notices? - // TO-DO: Use the site name instead of "Something Happened" + // TODO: Add some sort of notification queue to avoid flooding the end-user with notices? + // TODO: Use the site name instead of "Something Happened" if(Notification.permission === "granted") { var n = new Notification("Something Happened",{ body: msg, @@ -354,7 +354,7 @@ $(document).ready(function(){ }); // This one's for Tempra Conflux - // TO-DO: We might want to use pure JS here + // TODO: We might want to use pure JS here $(".ip_item").each(function(){ var ip = this.textContent; if(ip.length > 10){ @@ -396,6 +396,32 @@ $(document).ready(function(){ event.stopPropagation(); }) + $("#themeSelectorSelect").change(function(){ + console.log("Changing the theme to " + this.options[this.selectedIndex].getAttribute("val")); + $.ajax({ + url: this.form.getAttribute("action") + "?session=" + session, + type: "POST", + dataType: "json", + data: { "newTheme": this.options[this.selectedIndex].getAttribute("val"), isJs: "1" }, + success: function (data, status, xhr) { + console.log("Theme successfully switched"); + console.log("data",data); + console.log("status",status); + window.location.reload(); + }, + // TODO: Use a standard error handler for the AJAX calls in here which throws up the response (if JSON) in a .notice? Might be difficult to trace errors in the console, if we reuse the same function every-time + error: function(xhr,status,errstr) { + console.log("The AJAX request failed"); + console.log("xhr",xhr); + console.log("status",status); + console.log("errstr",errstr); + if(status=="parsererror") { + console.log("The server didn't respond with a valid JSON response"); + } + } + }); + }); + this.onkeyup = function(event) { if(event.which == 37) this.querySelectorAll("#prevFloat a")[0].click(); if(event.which == 39) this.querySelectorAll("#nextFloat a")[0].click(); diff --git a/query_gen/lib/install.go b/query_gen/lib/install.go index befb5d05..2f2e8ce0 100644 --- a/query_gen/lib/install.go +++ b/query_gen/lib/install.go @@ -14,7 +14,7 @@ type DB_Install_Instruction struct { } // A set of wrappers around the generator methods, so we can use this in the installer -// TO-DO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter +// TODO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter type installer struct { adapter DB_Adapter instructions []DB_Install_Instruction diff --git a/query_gen/lib/mysql.go b/query_gen/lib/mysql.go index 402ca17b..216d7ed2 100644 --- a/query_gen/lib/mysql.go +++ b/query_gen/lib/mysql.go @@ -54,7 +54,7 @@ func (adapter *Mysql_Adapter) CreateTable(name string, table string, charset str } var end string - // TO-DO: Exclude the other variants of text like mediumtext and longtext too + // TODO: Exclude the other variants of text like mediumtext and longtext too if column.Default != "" && column.Type != "text" { end = " DEFAULT " if adapter.stringyType(column.Type) && column.Default != "''" { @@ -814,7 +814,7 @@ func (adapter *Mysql_Adapter) Write() error { var stmts, body string for _, name := range adapter.BufferOrder { stmt := adapter.Buffer[name] - // TO-DO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :( + // TODO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :( if stmt.Type != "create-table" { stmts += "var " + name + "_stmt *sql.Stmt\n" body += ` diff --git a/query_gen/lib/pgsql.go b/query_gen/lib/pgsql.go index b86de6ea..8832f5cd 100644 --- a/query_gen/lib/pgsql.go +++ b/query_gen/lib/pgsql.go @@ -29,7 +29,7 @@ func (adapter *Pgsql_Adapter) GetStmts() map[string]DB_Stmt { return adapter.Buffer } -// TO-DO: Implement this +// TODO: Implement this // We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up func (adapter *Pgsql_Adapter) CreateTable(name string, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (string, error) { if name == "" { @@ -93,7 +93,7 @@ func (adapter *Pgsql_Adapter) CreateTable(name string, table string, charset str return querystr, nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -110,7 +110,7 @@ func (adapter *Pgsql_Adapter) SimpleInsert(name string, table string, columns st return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -127,7 +127,7 @@ func (adapter *Pgsql_Adapter) SimpleReplace(name string, table string, columns s return "", nil } -// TO-DO: Implemented, but we need CreateTable and a better installer to *test* it +// TODO: Implemented, but we need CreateTable and a better installer to *test* it func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string, where string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -144,7 +144,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string for _, token := range item.Expr { switch token.Type { case "function": - // TO-DO: Write a more sophisticated function parser on the utils side. + // TODO: Write a more sophisticated function parser on the utils side. if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "LOCALTIMESTAMP()" } @@ -170,7 +170,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string for _, token := range loc.Expr { switch token.Type { case "function": - // TO-DO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity? + // TODO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity? if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" { token.Contents = "LOCALTIMESTAMP()" } @@ -194,7 +194,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string return querystr, nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleDelete(name string, table string, where string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -208,7 +208,7 @@ func (adapter *Pgsql_Adapter) SimpleDelete(name string, table string, where stri return "", nil } -// TO-DO: Implement this +// TODO: Implement this // We don't want to accidentally wipe tables, so we'll have a seperate method for purging tables instead func (adapter *Pgsql_Adapter) Purge(name string, table string) (string, error) { if name == "" { @@ -220,7 +220,7 @@ func (adapter *Pgsql_Adapter) Purge(name string, table string) (string, error) { return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -234,7 +234,7 @@ func (adapter *Pgsql_Adapter) SimpleSelect(name string, table string, columns st return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -254,7 +254,7 @@ func (adapter *Pgsql_Adapter) SimpleLeftJoin(name string, table1 string, table2 return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -274,22 +274,22 @@ func (adapter *Pgsql_Adapter) SimpleInnerJoin(name string, table1 string, table2 return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) { return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) { return "", nil } -// TO-DO: Implement this +// TODO: Implement this func (adapter *Pgsql_Adapter) SimpleCount(name string, table string, where string, limit string) (string, error) { if name == "" { return "", errors.New("You need a name for this statement") @@ -304,7 +304,7 @@ func (adapter *Pgsql_Adapter) Write() error { var stmts, body string for _, name := range adapter.BufferOrder { stmt := adapter.Buffer[name] - // TO-DO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :( + // TODO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :( if stmt.Type != "create-table" { stmts += "var " + name + "_stmt *sql.Stmt\n" body += ` diff --git a/query_gen/lib/querygen.go b/query_gen/lib/querygen.go index 5ea55d23..f9bbdd10 100644 --- a/query_gen/lib/querygen.go +++ b/query_gen/lib/querygen.go @@ -121,7 +121,7 @@ type DB_Adapter interface { SimpleCount(string,string,string,string) (string, error) Write() error - // TO-DO: Add a simple query builder + // TODO: Add a simple query builder } func GetAdapter(name string) (adap DB_Adapter, err error) { diff --git a/query_gen/main.go b/query_gen/main.go index 85dbb2d8..996bc430 100644 --- a/query_gen/main.go +++ b/query_gen/main.go @@ -15,465 +15,484 @@ func main() { } } +// nolint func write_statements(adapter qgen.DB_Adapter) error { err := create_tables(adapter) if err != nil { return err } - + err = seed_tables(adapter) if err != nil { return err } - + err = write_selects(adapter) 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 } +// nolint func create_tables(adapter qgen.DB_Adapter) error { - qgen.Install.CreateTable("users","utf8mb4","utf8mb4_general_ci", + qgen.Install.CreateTable("users", "utf8mb4", "utf8mb4_general_ci", []qgen.DB_Table_Column{ - qgen.DB_Table_Column{"uid","int",0,false,true,""}, - qgen.DB_Table_Column{"name","varchar",100,false,false,""}, - qgen.DB_Table_Column{"password","varchar",100,false,false,""}, - qgen.DB_Table_Column{"salt","varchar",80,false,false,"''"}, - qgen.DB_Table_Column{"group","int",0,false,false,""}, - qgen.DB_Table_Column{"active","boolean",0,false,false,"0"}, - qgen.DB_Table_Column{"is_super_admin","boolean",0,false,false,"0"}, - qgen.DB_Table_Column{"createdAt","createdAt",0,false,false,""}, - qgen.DB_Table_Column{"lastActiveAt","datetime",0,false,false,""}, - qgen.DB_Table_Column{"session","varchar",200,false,false,"''"}, - qgen.DB_Table_Column{"last_ip","varchar",200,false,false,"0.0.0.0.0"}, - qgen.DB_Table_Column{"email","varchar",200,false,false,"''"}, - qgen.DB_Table_Column{"avatar","varchar",100,false,false,"''"}, - qgen.DB_Table_Column{"message","text",0,false,false,"''"}, - qgen.DB_Table_Column{"url_prefix","varchar",20,false,false,"''"}, - qgen.DB_Table_Column{"url_name","varchar",100,false,false,"''"}, - qgen.DB_Table_Column{"level","smallint",0,false,false,"0"}, - qgen.DB_Table_Column{"score","int",0,false,false,"0"}, - qgen.DB_Table_Column{"posts","int",0,false,false,"0"}, - qgen.DB_Table_Column{"bigposts","int",0,false,false,"0"}, - qgen.DB_Table_Column{"megaposts","int",0,false,false,"0"}, - qgen.DB_Table_Column{"topics","int",0,false,false,"0"}, + qgen.DB_Table_Column{"uid", "int", 0, false, true, ""}, + qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""}, + qgen.DB_Table_Column{"password", "varchar", 100, false, false, ""}, + qgen.DB_Table_Column{"salt", "varchar", 80, false, false, "''"}, + qgen.DB_Table_Column{"group", "int", 0, false, false, ""}, + qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"}, + qgen.DB_Table_Column{"is_super_admin", "boolean", 0, false, false, "0"}, + qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""}, + qgen.DB_Table_Column{"lastActiveAt", "datetime", 0, false, false, ""}, + qgen.DB_Table_Column{"session", "varchar", 200, false, false, "''"}, + qgen.DB_Table_Column{"last_ip", "varchar", 200, false, false, "0.0.0.0.0"}, + qgen.DB_Table_Column{"email", "varchar", 200, false, false, "''"}, + qgen.DB_Table_Column{"avatar", "varchar", 100, false, false, "''"}, + qgen.DB_Table_Column{"message", "text", 0, false, false, "''"}, + qgen.DB_Table_Column{"url_prefix", "varchar", 20, false, false, "''"}, + qgen.DB_Table_Column{"url_name", "varchar", 100, false, false, "''"}, + qgen.DB_Table_Column{"level", "smallint", 0, false, false, "0"}, + qgen.DB_Table_Column{"score", "int", 0, false, false, "0"}, + qgen.DB_Table_Column{"posts", "int", 0, false, false, "0"}, + qgen.DB_Table_Column{"bigposts", "int", 0, false, false, "0"}, + 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_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"}, - qgen.DB_Table_Key{"name","unique"}, + qgen.DB_Table_Key{"uid", "primary"}, + qgen.DB_Table_Key{"name", "unique"}, }, ) - + // What should we do about global penalties? Put them on the users table for speed? Or keep them here? // 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. + // TODO: Perm overrides + // TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag + // TODO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups + // TODO: 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,""}, //forum, profile?, and social_group. Leave blank for global. qgen.DB_Table_Column{"overrides","text",0,false,false,"{}"}, - + 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? - + // 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{"expires_at","datetime",0,false,false,""}, }, []qgen.DB_Table_Key{}, )*/ - - qgen.Install.CreateTable("users_groups_scheduler","","", + + 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_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.DB_Table_Key{"uid", "primary"}, }, ) - - qgen.Install.CreateTable("word_filters","","", + + 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_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"}, + qgen.DB_Table_Key{"wfid", "primary"}, }, ) - + + qgen.Install.CreateTable("sync", "", "", + []qgen.DB_Table_Column{ + qgen.DB_Table_Column{"last_update", "datetime", 0, false, false, ""}, + }, + []qgen.DB_Table_Key{}, + ) + return nil } +// nolint func seed_tables(adapter qgen.DB_Adapter) error { return nil } +// nolint func write_selects(adapter qgen.DB_Adapter) error { // url_prefix and url_name will be removed from this query in a later commit - adapter.SimpleSelect("get_user","users","name, group, is_super_admin, avatar, message, url_prefix, url_name, level","uid = ?","","") - + adapter.SimpleSelect("get_user", "users", "name, group, is_super_admin, avatar, message, url_prefix, url_name, level", "uid = ?", "", "") + // Looking for get_topic? Your statement is in another castle - - adapter.SimpleSelect("get_reply","replies","tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","","") - - adapter.SimpleSelect("get_user_reply","users_replies","uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress","rid = ?","","") - - adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","") - - - adapter.SimpleSelect("get_settings","settings","name, content, type","","","") - - adapter.SimpleSelect("get_setting","settings","content, type","name = ?","","") - - adapter.SimpleSelect("get_full_setting","settings","name, type, constraints","name = ?","","") - - adapter.SimpleSelect("get_full_settings","settings","name, content, type, constraints","","","") - - adapter.SimpleSelect("get_groups","users_groups","gid, name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag","","","") - - adapter.SimpleSelect("get_forums","forums","fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","","fid ASC","") - - adapter.SimpleSelect("get_forums_permissions","forums_permissions","gid, fid, permissions","","gid ASC, fid ASC","") - - adapter.SimpleSelect("get_plugins","plugins","uname, active, installed","","","") - - 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_reply", "replies", "tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount", "rid = ?", "", "") + + adapter.SimpleSelect("get_user_reply", "users_replies", "uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress", "rid = ?", "", "") + + adapter.SimpleSelect("get_password", "users", "password,salt", "uid = ?", "", "") + + adapter.SimpleSelect("get_settings", "settings", "name, content, type", "", "", "") + + adapter.SimpleSelect("get_setting", "settings", "content, type", "name = ?", "", "") + + adapter.SimpleSelect("get_full_setting", "settings", "name, type, constraints", "name = ?", "", "") + + adapter.SimpleSelect("get_full_settings", "settings", "name, content, type, constraints", "", "", "") + + adapter.SimpleSelect("get_groups", "users_groups", "gid, name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag", "", "", "") + + adapter.SimpleSelect("get_forums", "forums", "fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime", "", "fid ASC", "") + + adapter.SimpleSelect("get_forums_permissions", "forums_permissions", "gid, fid, permissions", "", "gid ASC, fid ASC", "") + + adapter.SimpleSelect("get_plugins", "plugins", "uname, active, installed", "", "", "") + + 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("is_plugin_installed","plugins","installed","uname = ?","","") - - adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","") - - 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","","","") - - adapter.SimpleSelect("get_modlogs_offset","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","?,?") - - adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","","") - - adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","","") - - adapter.SimpleSelect("get_user_reply_uid","users_replies","uid","rid = ?","","") - - adapter.SimpleSelect("has_liked_topic","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'topics'","","") - - adapter.SimpleSelect("has_liked_reply","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'replies'","","") - - adapter.SimpleSelect("get_user_name","users","name","uid = ?","","") - - adapter.SimpleSelect("get_user_active","users","active","uid = ?","","") - - adapter.SimpleSelect("get_emails_by_user","emails","email, validated, token","uid = ?","","") - - adapter.SimpleSelect("get_topic_basic","topics","title, content","tid = ?","","") - - adapter.SimpleSelect("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","","") - - adapter.SimpleSelect("forum_entry_exists","forums","fid","name = ''","fid ASC","0,1") - - adapter.SimpleSelect("group_entry_exists","users_groups","gid","name = ''","gid ASC","0,1") - - 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","","") - + + adapter.SimpleSelect("get_users", "users", "uid, name, group, active, is_super_admin, avatar", "", "", "") + + 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", "", "", "") + + adapter.SimpleSelect("get_modlogs_offset", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "", "", "?,?") + + adapter.SimpleSelect("get_reply_tid", "replies", "tid", "rid = ?", "", "") + + adapter.SimpleSelect("get_topic_fid", "topics", "parentID", "tid = ?", "", "") + + adapter.SimpleSelect("get_user_reply_uid", "users_replies", "uid", "rid = ?", "", "") + + adapter.SimpleSelect("has_liked_topic", "likes", "targetItem", "sentBy = ? and targetItem = ? and targetType = 'topics'", "", "") + + adapter.SimpleSelect("has_liked_reply", "likes", "targetItem", "sentBy = ? and targetItem = ? and targetType = 'replies'", "", "") + + adapter.SimpleSelect("get_user_name", "users", "name", "uid = ?", "", "") + + adapter.SimpleSelect("get_user_active", "users", "active", "uid = ?", "", "") + + adapter.SimpleSelect("get_emails_by_user", "emails", "email, validated, token", "uid = ?", "", "") + + adapter.SimpleSelect("get_topic_basic", "topics", "title, content", "tid = ?", "", "") + + adapter.SimpleSelect("get_activity_entry", "activity_stream", "actor, targetUser, event, elementType, elementID", "asid = ?", "", "") + + adapter.SimpleSelect("forum_entry_exists", "forums", "fid", "name = ''", "fid ASC", "0,1") + + adapter.SimpleSelect("group_entry_exists", "users_groups", "gid", "name = ''", "gid ASC", "0,1") + + 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", "", "") + + adapter.SimpleSelect("get_sync", "sync", "last_update", "", "", "") + return nil } +// nolint func write_left_joins(adapter qgen.DB_Adapter) error { - adapter.SimpleLeftJoin("get_topic_replies_offset","replies","users","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","replies.createdBy = users.uid","tid = ?","","?,?") - - adapter.SimpleLeftJoin("get_topic_list","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","") - - adapter.SimpleLeftJoin("get_topic_user","topics","users","topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level","topics.createdBy = users.uid","tid = ?","","") - - adapter.SimpleLeftJoin("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data","replies.tid = topics.tid","rid = ?","","") - - adapter.SimpleLeftJoin("get_topic_replies","replies","users","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.createdBy = users.uid","tid = ?","","") - - adapter.SimpleLeftJoin("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc","") - - adapter.SimpleLeftJoin("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","","") - + adapter.SimpleLeftJoin("get_topic_replies_offset", "replies", "users", "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", "replies.createdBy = users.uid", "tid = ?", "", "?,?") + + adapter.SimpleLeftJoin("get_topic_list", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC", "") + + adapter.SimpleLeftJoin("get_topic_user", "topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", "") + + adapter.SimpleLeftJoin("get_topic_by_reply", "replies", "topics", "topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data", "replies.tid = topics.tid", "rid = ?", "", "") + + adapter.SimpleLeftJoin("get_topic_replies", "replies", "users", "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.createdBy = users.uid", "tid = ?", "", "") + + adapter.SimpleLeftJoin("get_forum_topics", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "topics.parentID = ?", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc", "") + + adapter.SimpleLeftJoin("get_profile_replies", "users_replies", "users", "users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group", "users_replies.createdBy = users.uid", "users_replies.uid = ?", "", "") + return nil } +// nolint func write_inner_joins(adapter qgen.DB_Adapter) error { - adapter.SimpleInnerJoin("get_watchers","activity_stream","activity_subscriptions","activity_subscriptions.user","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","","") - + adapter.SimpleInnerJoin("get_watchers", "activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", "") + return nil } +// nolint func write_inserts(adapter qgen.DB_Adapter) error { - adapter.SimpleInsert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,lastReplyBy,ipaddress,words,createdBy","?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?") - - adapter.SimpleInsert("create_report","topics","title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class","?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report'") + adapter.SimpleInsert("create_topic", "topics", "parentID,title,content,parsed_content,createdAt,lastReplyAt,lastReplyBy,ipaddress,words,createdBy", "?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?") - adapter.SimpleInsert("create_reply","replies","tid,content,parsed_content,createdAt,ipaddress,words,createdBy","?,?,?,UTC_TIMESTAMP(),?,?,?") - - adapter.SimpleInsert("create_action_reply","replies","tid,actionType,ipaddress,createdBy","?,?,?,?") - - adapter.SimpleInsert("create_like","likes","weight, targetItem, targetType, sentBy","?,?,?,?") - - adapter.SimpleInsert("add_activity","activity_stream","actor,targetUser,event,elementType,elementID","?,?,?,?,?") - - adapter.SimpleInsert("notify_one","activity_stream_matches","watcher,asid","?,?") - - adapter.SimpleInsert("add_email","emails","email, uid, validated, token","?,?,?,?") - - adapter.SimpleInsert("create_profile_reply","users_replies","uid, content, parsed_content, createdAt, createdBy, ipaddress","?,?,?,UTC_TIMESTAMP(),?,?") - - adapter.SimpleInsert("add_subscription","activity_subscriptions","user,targetID,targetType,level","?,?,?,2") - - adapter.SimpleInsert("create_forum","forums","name, desc, active, preset","?,?,?,?") - - adapter.SimpleInsert("add_forum_perms_to_forum","forums_permissions","gid,fid,preset,permissions","?,?,?,?") - - adapter.SimpleInsert("add_plugin","plugins","uname, active, installed","?,?,?") - - adapter.SimpleInsert("add_theme","themes","uname,default","?,?") + adapter.SimpleInsert("create_report", "topics", "title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class", "?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report'") + + adapter.SimpleInsert("create_reply", "replies", "tid,content,parsed_content,createdAt,ipaddress,words,createdBy", "?,?,?,UTC_TIMESTAMP(),?,?,?") + + adapter.SimpleInsert("create_action_reply", "replies", "tid,actionType,ipaddress,createdBy", "?,?,?,?") + + adapter.SimpleInsert("create_like", "likes", "weight, targetItem, targetType, sentBy", "?,?,?,?") + + adapter.SimpleInsert("add_activity", "activity_stream", "actor,targetUser,event,elementType,elementID", "?,?,?,?,?") + + adapter.SimpleInsert("notify_one", "activity_stream_matches", "watcher,asid", "?,?") + + adapter.SimpleInsert("add_email", "emails", "email, uid, validated, token", "?,?,?,?") + + adapter.SimpleInsert("create_profile_reply", "users_replies", "uid, content, parsed_content, createdAt, createdBy, ipaddress", "?,?,?,UTC_TIMESTAMP(),?,?") + + adapter.SimpleInsert("add_subscription", "activity_subscriptions", "user,targetID,targetType,level", "?,?,?,2") + + adapter.SimpleInsert("create_forum", "forums", "name, desc, active, preset", "?,?,?,?") + + adapter.SimpleInsert("add_forum_perms_to_forum", "forums_permissions", "gid,fid,preset,permissions", "?,?,?,?") + + adapter.SimpleInsert("add_plugin", "plugins", "uname, active, installed", "?,?,?") + + adapter.SimpleInsert("add_theme", "themes", "uname,default", "?,?") + + adapter.SimpleInsert("create_group", "users_groups", "name, tag, is_admin, is_mod, is_banned, permissions", "?,?,?,?,?,?") + + adapter.SimpleInsert("add_modlog_entry", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()") + + adapter.SimpleInsert("add_adminlog_entry", "administration_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()") + + adapter.SimpleInsert("create_word_filter", "word_filters", "find, replacement", "?,?") - - adapter.SimpleInsert("create_group","users_groups","name, tag, is_admin, is_mod, is_banned, permissions","?,?,?,?,?,?") - - adapter.SimpleInsert("add_modlog_entry","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,UTC_TIMESTAMP()") - - 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 } +// nolint 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(),?,?") - + 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 } +// nolint func write_updates(adapter qgen.DB_Adapter) error { - adapter.SimpleUpdate("add_replies_to_topic","topics","postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()","tid = ?") - - adapter.SimpleUpdate("remove_replies_from_topic","topics","postCount = postCount - ?","tid = ?") - - adapter.SimpleUpdate("add_topics_to_forum","forums","topicCount = topicCount + ?","fid = ?") - - adapter.SimpleUpdate("remove_topics_from_forum","forums","topicCount = topicCount - ?","fid = ?") - - adapter.SimpleUpdate("update_forum_cache","forums","lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = UTC_TIMESTAMP()","fid = ?") + adapter.SimpleUpdate("add_replies_to_topic", "topics", "postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()", "tid = ?") - adapter.SimpleUpdate("add_likes_to_topic","topics","likeCount = likeCount + ?","tid = ?") - - adapter.SimpleUpdate("add_likes_to_reply","replies","likeCount = likeCount + ?","rid = ?") - - adapter.SimpleUpdate("edit_topic","topics","title = ?, content = ?, parsed_content = ?, is_closed = ?","tid = ?") - - adapter.SimpleUpdate("edit_reply","replies","content = ?, parsed_content = ?","rid = ?") - - adapter.SimpleUpdate("stick_topic","topics","sticky = 1","tid = ?") - - adapter.SimpleUpdate("unstick_topic","topics","sticky = 0","tid = ?") - - adapter.SimpleUpdate("update_last_ip","users","last_ip = ?","uid = ?") + adapter.SimpleUpdate("remove_replies_from_topic", "topics", "postCount = postCount - ?", "tid = ?") - adapter.SimpleUpdate("update_session","users","session = ?","uid = ?") + adapter.SimpleUpdate("add_topics_to_forum", "forums", "topicCount = topicCount + ?", "fid = ?") - adapter.SimpleUpdate("set_password","users","password = ?, salt = ?","uid = ?") - - adapter.SimpleUpdate("set_avatar","users","avatar = ?","uid = ?") - - adapter.SimpleUpdate("set_username","users","name = ?","uid = ?") - - adapter.SimpleUpdate("change_group","users","group = ?","uid = ?") - - adapter.SimpleUpdate("activate_user","users","active = 1","uid = ?") - - adapter.SimpleUpdate("update_user_level","users","level = ?","uid = ?") - - adapter.SimpleUpdate("increment_user_score","users","score = score + ?","uid = ?") - - adapter.SimpleUpdate("increment_user_posts","users","posts = posts + ?","uid = ?") - - adapter.SimpleUpdate("increment_user_bigposts","users","posts = posts + ?, bigposts = bigposts + ?","uid = ?") - - adapter.SimpleUpdate("increment_user_megaposts","users","posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?","uid = ?") - - adapter.SimpleUpdate("increment_user_topics","users","topics = topics + ?","uid = ?") + adapter.SimpleUpdate("remove_topics_from_forum", "forums", "topicCount = topicCount - ?", "fid = ?") - adapter.SimpleUpdate("edit_profile_reply","users_replies","content = ?, parsed_content = ?","rid = ?") - - //delete_forum_stmt, err = db.Prepare("delete from forums where fid = ?") - adapter.SimpleUpdate("delete_forum","forums","name= '', active = 0","fid = ?") - - adapter.SimpleUpdate("update_forum","forums","name = ?, desc = ?, active = ?, preset = ?","fid = ?") - - adapter.SimpleUpdate("update_setting","settings","content = ?","name = ?") - - adapter.SimpleUpdate("update_plugin","plugins","active = ?","uname = ?") - - adapter.SimpleUpdate("update_plugin_install","plugins","installed = ?","uname = ?") - - adapter.SimpleUpdate("update_theme","themes","default = ?","uname = ?") - - adapter.SimpleUpdate("update_user","users","name = ?, email = ?, group = ?","uid = ?") + adapter.SimpleUpdate("update_forum_cache", "forums", "lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = UTC_TIMESTAMP()", "fid = ?") + + adapter.SimpleUpdate("add_likes_to_topic", "topics", "likeCount = likeCount + ?", "tid = ?") + + adapter.SimpleUpdate("add_likes_to_reply", "replies", "likeCount = likeCount + ?", "rid = ?") + + adapter.SimpleUpdate("edit_topic", "topics", "title = ?, content = ?, parsed_content = ?, is_closed = ?", "tid = ?") + + adapter.SimpleUpdate("edit_reply", "replies", "content = ?, parsed_content = ?", "rid = ?") + + adapter.SimpleUpdate("stick_topic", "topics", "sticky = 1", "tid = ?") + + adapter.SimpleUpdate("unstick_topic", "topics", "sticky = 0", "tid = ?") + + adapter.SimpleUpdate("update_last_ip", "users", "last_ip = ?", "uid = ?") + + adapter.SimpleUpdate("update_session", "users", "session = ?", "uid = ?") + + adapter.SimpleUpdate("set_password", "users", "password = ?, salt = ?", "uid = ?") + + adapter.SimpleUpdate("set_avatar", "users", "avatar = ?", "uid = ?") + + adapter.SimpleUpdate("set_username", "users", "name = ?", "uid = ?") + + adapter.SimpleUpdate("change_group", "users", "group = ?", "uid = ?") + + adapter.SimpleUpdate("activate_user", "users", "active = 1", "uid = ?") + + adapter.SimpleUpdate("update_user_level", "users", "level = ?", "uid = ?") + + adapter.SimpleUpdate("increment_user_score", "users", "score = score + ?", "uid = ?") + + adapter.SimpleUpdate("increment_user_posts", "users", "posts = posts + ?", "uid = ?") + + adapter.SimpleUpdate("increment_user_bigposts", "users", "posts = posts + ?, bigposts = bigposts + ?", "uid = ?") + + adapter.SimpleUpdate("increment_user_megaposts", "users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", "uid = ?") + + adapter.SimpleUpdate("increment_user_topics", "users", "topics = topics + ?", "uid = ?") + + adapter.SimpleUpdate("edit_profile_reply", "users_replies", "content = ?, parsed_content = ?", "rid = ?") + + adapter.SimpleUpdate("update_forum", "forums", "name = ?, desc = ?, active = ?, preset = ?", "fid = ?") + + adapter.SimpleUpdate("update_setting", "settings", "content = ?", "name = ?") + + adapter.SimpleUpdate("update_plugin", "plugins", "active = ?", "uname = ?") + + adapter.SimpleUpdate("update_plugin_install", "plugins", "installed = ?", "uname = ?") + + adapter.SimpleUpdate("update_theme", "themes", "default = ?", "uname = ?") + + adapter.SimpleUpdate("update_user", "users", "name = ?, email = ?, group = ?", "uid = ?") + + adapter.SimpleUpdate("update_group_perms", "users_groups", "permissions = ?", "gid = ?") + + adapter.SimpleUpdate("update_group_rank", "users_groups", "is_admin = ?, is_mod = ?, is_banned = ?", "gid = ?") + + adapter.SimpleUpdate("update_group", "users_groups", "name = ?, tag = ?", "gid = ?") + + 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 -- Has this been fixed? + + adapter.SimpleUpdate("set_temp_group", "users", "temp_group = ?", "uid = ?") + + adapter.SimpleUpdate("update_word_filter", "word_filters", "find = ?, replacement = ?", "wfid = ?") + + adapter.SimpleUpdate("bump_sync", "sync", "last_update = UTC_TIMESTAMP()", "") - adapter.SimpleUpdate("update_group_perms","users_groups","permissions = ?","gid = ?") - - adapter.SimpleUpdate("update_group_rank","users_groups","is_admin = ?, is_mod = ?, is_banned = ?","gid = ?") - - adapter.SimpleUpdate("update_group","users_groups","name = ?, tag = ?","gid = ?") - - 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 -- 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 } +// nolint func write_deletes(adapter qgen.DB_Adapter) error { - adapter.SimpleDelete("delete_reply","replies","rid = ?") - - adapter.SimpleDelete("delete_topic","topics","tid = ?") - - - adapter.SimpleDelete("delete_profile_reply","users_replies","rid = ?") - - 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_reply", "replies", "rid = ?") + + adapter.SimpleDelete("delete_topic", "topics", "tid = ?") + + adapter.SimpleDelete("delete_profile_reply", "users_replies", "rid = ?") + + 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 = ?") - + + adapter.SimpleDelete("delete_word_filter", "word_filters", "wfid = ?") + return nil } +// nolint func write_simple_counts(adapter qgen.DB_Adapter) error { - adapter.SimpleCount("report_exists","topics","data = ? AND data != '' AND parentID = 1","") - - adapter.SimpleCount("group_count","users_groups","","") - - adapter.SimpleCount("modlog_count","moderation_logs","","") - + adapter.SimpleCount("report_exists", "topics", "data = ? AND data != '' AND parentID = 1", "") + + adapter.SimpleCount("group_count", "users_groups", "", "") + + adapter.SimpleCount("modlog_count", "moderation_logs", "", "") + return nil } +// nolint 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","",""}, + qgen.DB_Insert{"forums_permissions", "gid,fid,preset,permissions", ""}, + qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""}, ) - + adapter.SimpleInsertSelect("add_forum_perms_to_forum_staff", - qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""}, - qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 1","",""}, + qgen.DB_Insert{"forums_permissions", "gid,fid,preset,permissions", ""}, + qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""}, ) - + adapter.SimpleInsertSelect("add_forum_perms_to_forum_members", - qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""}, - qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 0 AND is_banned = 0","",""}, + qgen.DB_Insert{"forums_permissions", "gid,fid,preset,permissions", ""}, + qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""}, ) - + return nil } +// nolint func write_insert_left_joins(adapter qgen.DB_Adapter) error { return nil } +// nolint 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 = ?","",""}, + 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/router.go b/router.go index 5d957d1e..9ee471ed 100644 --- a/router.go +++ b/router.go @@ -6,30 +6,35 @@ import "strings" import "sync" import "net/http" -// TO-DO: Support the new handler signatures created by our efforts to move the PreRoute middleware into the generated router +// TODO: Support the new handler signatures created by our efforts to move the PreRoute middleware into the generated router +// nolint Stop linting the uselessness of this file, we never know when we might need this file again type Router struct { sync.RWMutex routes map[string]func(http.ResponseWriter, *http.Request) } +// nolint func NewRouter() *Router { return &Router{ routes: make(map[string]func(http.ResponseWriter, *http.Request)), } } +// nolint func (router *Router) Handle(pattern string, handle http.Handler) { router.Lock() router.routes[pattern] = handle.ServeHTTP router.Unlock() } +// nolint func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request)) { router.Lock() router.routes[pattern] = handle router.Unlock() } +// nolint func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' { w.WriteHeader(405) @@ -37,10 +42,10 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } - var /*extra_data, */prefix string - if req.URL.Path[len(req.URL.Path) - 1] != '/' { - //extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] - prefix = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] + var /*extraData, */ prefix string + if req.URL.Path[len(req.URL.Path)-1] != '/' { + //extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] + prefix = req.URL.Path[:strings.LastIndexByte(req.URL.Path, '/')+1] } else { prefix = req.URL.Path } @@ -50,9 +55,9 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { router.RUnlock() if ok { - handle(w,req) + handle(w, req) return } //log.Print("req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')]",req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')]) - NotFound(w,req) + NotFound(w, req) } diff --git a/router_gen/main.go b/router_gen/main.go index b8fb93a5..32f1fd3c 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -2,6 +2,7 @@ package main import "log" + //import "strings" import "os" @@ -10,13 +11,13 @@ var route_groups []RouteGroup func main() { log.Println("Generating the router...") - + // Load all the routes... routes() - + var out string - var fdata string = "// Code generated by. DO NOT EDIT.\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n" - + var fdata = "// Code generated by. DO NOT EDIT.\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n" + for _, route := range route_list { var end int if route.Path[len(route.Path)-1] == '/' { @@ -34,7 +35,7 @@ func main() { } out += ")\n\t\t\treturn" } - + for _, group := range route_groups { var end int if group.Path[len(group.Path)-1] == '/' { @@ -51,7 +52,7 @@ func main() { default_route = route continue } - + out += "\n\t\t\t\tcase \"" + route.Path + "\":" if route.Before != "" { out += "\n\t\t\t\t\t" + route.Before @@ -62,7 +63,7 @@ func main() { } out += ")\n\t\t\t\t\treturn" } - + if default_route.Name != "" { out += "\n\t\t\t\tdefault:" if default_route.Before != "" { @@ -76,7 +77,7 @@ func main() { } out += "\n\t\t\t}" } - + fdata += `package main import "log" @@ -142,9 +143,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { if dev.SuperDebug { log.Print("before route_static") - log.Print("prefix:", prefix) - log.Print("req.URL.Path:", req.URL.Path) - log.Print("extra_data:", extra_data) + log.Print("prefix: ", prefix) + log.Print("req.URL.Path: ", req.URL.Path) + log.Print("extra_data: ", extra_data) + log.Print("req.Referer(): ", req.Referer()) } if prefix == "/static" { @@ -178,7 +180,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return case "": // Stop the favicons, robots.txt file, etc. resolving to the topics list - // TO-DO: Add support for favicons and robots.txt files + // TODO: Add support for favicons and robots.txt files switch(extra_data) { case "robots.txt": route_robots_txt(w,req) @@ -207,7 +209,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { NotFound(w,req) } ` - write_file("./gen_router.go",fdata) + write_file("./gen_router.go", fdata) log.Println("Successfully generated the router") } diff --git a/router_gen/routes.go b/router_gen/routes.go index 262b54f9..3aeaaacb 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -30,6 +30,7 @@ func routes() { addRoute("route_forum", "/forum/", "", "extra_data") //addRoute("route_topic_create","/topics/create/","","extra_data") //addRoute("route_topics","/topics/",""/*,"&groups","&forums"*/) + addRoute("route_change_theme", "/theme/", "") addRouteGroup("/report/", Route{"route_report_submit", "/report/submit/", "", []string{"extra_data"}}, diff --git a/routes.go b/routes.go index 503f9281..e131ef12 100644 --- a/routes.go +++ b/routes.go @@ -35,10 +35,10 @@ func init() { hvars = &HeaderVars{Site: site} } -type HttpsRedirect struct { +type HTTPSRedirect struct { } -func (red *HttpsRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) { dest := "https://" + req.Host + req.URL.Path if len(req.URL.RawQuery) > 0 { dest += "?" + req.URL.RawQuery @@ -54,13 +54,14 @@ func route_static(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) return } + h := w.Header() // Surely, there's a more efficient way of doing this? - if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && file.Info.ModTime().Before(t.Add(1*time.Second)) { + t, err := time.Parse(http.TimeFormat, h.Get("If-Modified-Since")) + if err == nil && file.Info.ModTime().Before(t.Add(1*time.Second)) { w.WriteHeader(http.StatusNotModified) return } - h := w.Header() h.Set("Last-Modified", file.FormattedModTime) h.Set("Content-Type", file.Mimetype) //Cache-Control: max-age=31536000 @@ -68,7 +69,7 @@ func route_static(w http.ResponseWriter, r *http.Request) { h.Set("Vary", "Accept-Encoding") //http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file) //w.Write(file.Data) - if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { + if strings.Contains(h.Get("Accept-Encoding"), "gzip") { h.Set("Content-Encoding", "gzip") h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10)) io.Copy(w, bytes.NewReader(file.GzipData)) // Use w.Write instead? @@ -90,9 +91,9 @@ func route_fstatic(w http.ResponseWriter, r *http.Request){ http.ServeFile(w,r,r.URL.Path) }*/ -// TO-DO: Make this a static file somehow? Is it possible for us to put this file somewhere else? -// TO-DO: Add a sitemap -// TO-DO: Add an API so that plugins can register disallowed areas. E.g. /groups/join for plugin_socialgroups +// TODO: Make this a static file somehow? Is it possible for us to put this file somewhere else? +// TODO: Add a sitemap +// TODO: Add an API so that plugins can register disallowed areas. E.g. /groups/join for plugin_socialgroups func route_robots_txt(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`User-agent: * Disallow: /panel/ @@ -148,7 +149,7 @@ func route_custom_page(w http.ResponseWriter, r *http.Request, user User) { } } -// TO-DO: Paginate this +// TODO: Paginate this func route_topics(w http.ResponseWriter, r *http.Request, user User) { headerVars, ok := SessionCheck(w, r, &user) if !ok { @@ -232,7 +233,7 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User) { i++ } - // TO-DO: What if a user is deleted via the Control Panel? + // TODO: What if a user is deleted via the Control Panel? userList, err := users.BulkCascadeGetMap(idSlice) if err != nil { InternalError(err, w) @@ -240,7 +241,7 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User) { } // Second pass to the add the user data - // TO-DO: Use a pointer to TopicsRow instead of TopicsRow itself? + // TODO: Use a pointer to TopicsRow instead of TopicsRow itself? for _, topicItem := range topicList { topicItem.Creator = userList[topicItem.CreatedBy] topicItem.LastUser = userList[topicItem.LastReplyBy] @@ -279,7 +280,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string) return } - // TO-DO: Fix this double-check + // TODO: Fix this double-check forum, err := fstore.CascadeGet(fid) if err == ErrNoRows { NotFound(w, r) @@ -309,7 +310,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string) } defer rows.Close() - // TO-DO: Use something other than TopicsRow as we don't need to store the forum name and link on each and every topic item? + // TODO: Use something other than TopicsRow as we don't need to store the forum name and link on each and every topic item? var topicList []*TopicsRow var reqUserList = make(map[int]bool) for rows.Next() { @@ -347,7 +348,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string) i++ } - // TO-DO: What if a user is deleted via the Control Panel? + // TODO: What if a user is deleted via the Control Panel? userList, err := users.BulkCascadeGetMap(idSlice) if err != nil { InternalError(err, w) @@ -355,7 +356,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string) } // Second pass to the add the user data - // TO-DO: Use a pointer to TopicsRow instead of TopicsRow itself? + // TODO: Use a pointer to TopicsRow instead of TopicsRow itself? for _, topicItem := range topicList { topicItem.Creator = userList[topicItem.CreatedBy] topicItem.LastUser = userList[topicItem.LastReplyBy] @@ -381,7 +382,7 @@ func route_forums(w http.ResponseWriter, r *http.Request, user User) { var forumList []Forum var canSee []int if user.IsSuperAdmin { - canSee, err = fstore.GetAllIDs() + canSee, err = fstore.GetAllVisibleIDs() if err != nil { InternalError(err, w) return @@ -396,7 +397,7 @@ func route_forums(w http.ResponseWriter, r *http.Request, user User) { for _, fid := range canSee { //log.Print(forums[fid]) var forum = *fstore.DirtyGet(fid) - if forum.Active && forum.Name != "" && forum.ParentID == 0 { + if forum.ParentID == 0 { if forum.LastTopicID != 0 { forum.LastTopicTime, err = relativeTime(forum.LastTopicTime) if err != nil { @@ -582,7 +583,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request, user User) { } replyItem.Liked = false - // TO-DO: Rename this to topic_rrow_assign + // TODO: Rename this to topic_rrow_assign if hooks["rrow_assign"] != nil { runHook("rrow_assign", &replyItem) } @@ -682,7 +683,7 @@ func route_profile(w http.ResponseWriter, r *http.Request, user User) { replyLiked := false replyLikeCount := 0 - // TO-DO: Add a hook here + // TODO: Add a hook here replyList = append(replyList, Reply{rid, puser.ID, replyContent, parseMessage(replyContent), replyCreatedBy, buildProfileURL(nameToSlug(replyCreatedByName), replyCreatedBy), replyCreatedByName, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, replyAvatar, replyClassName, replyLines, replyTag, "", "", "", 0, "", replyLiked, replyLikeCount, "", ""}) } @@ -731,10 +732,11 @@ func route_topic_create(w http.ResponseWriter, r *http.Request, user User, sfid runVhook("topic_create_pre_loop", w, r, fid, &headerVars, &user, &strictmode) } + // TODO: Re-add support for plugin_socialgroups var forumList []Forum var canSee []int if user.IsSuperAdmin { - canSee, err = fstore.GetAllIDs() + canSee, err = fstore.GetAllVisibleIDs() if err != nil { InternalError(err, w) return @@ -744,25 +746,23 @@ func route_topic_create(w http.ResponseWriter, r *http.Request, user User, sfid canSee = group.CanSee } - // TO-DO: plugin_superadmin needs to be able to override this loop. Skip flag on topic_create_pre_loop? + // TODO: plugin_superadmin needs to be able to override this loop. Skip flag on topic_create_pre_loop? for _, ffid := range canSee { - // TO-DO: Surely, there's a better way of doing this. I've added it in for now to support plugin_socialgroups, but we really need to clean this up + // TODO: Surely, there's a better way of doing this. I've added it in for now to support plugin_socialgroups, but we really need to clean this up if strictmode && ffid != fid { continue } // Do a bulk forum fetch, just in case it's the SqlForumStore? forum := fstore.DirtyGet(ffid) - if forum.Active && forum.Name != "" { - fcopy := *forum - if hooks["topic_create_frow_assign"] != nil { - // TO-DO: Add the skip feature to all the other row based hooks? - if runHook("topic_create_frow_assign", &fcopy).(bool) { - continue - } + fcopy := *forum + if hooks["topic_create_frow_assign"] != nil { + // TODO: Add the skip feature to all the other row based hooks? + if runHook("topic_create_frow_assign", &fcopy).(bool) { + continue } - forumList = append(forumList, fcopy) } + forumList = append(forumList, fcopy) } ctpage := CreateTopicPage{"Create Topic", user, headerVars, forumList, fid} @@ -789,7 +789,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, fid) if !ok { return @@ -799,7 +799,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User return } - topic_name := html.EscapeString(r.PostFormValue("topic-name")) + topicName := html.EscapeString(r.PostFormValue("topic-name")) content := html.EscapeString(preparseMessage(r.PostFormValue("topic-content"))) ipaddress, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { @@ -808,7 +808,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User } wcount := wordCount(content) - res, err := create_topic_stmt.Exec(fid, topic_name, content, parseMessage(content), user.ID, ipaddress, wcount, user.ID) + res, err := create_topic_stmt.Exec(fid, topicName, content, parseMessage(content), user.ID, ipaddress, wcount, user.ID) if err != nil { InternalError(err, w) return @@ -832,13 +832,13 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User } http.Redirect(w, r, "/topic/"+strconv.FormatInt(lastID, 10), http.StatusSeeOther) - err = increase_post_user_stats(wcount, user.ID, true, user) + err = user.increasePostStats(wcount, true) if err != nil { InternalError(err, w) return } - err = fstore.UpdateLastTopic(topic_name, int(lastID), user.Name, user.ID, time.Now().Format("2006-01-02 15:04:05"), fid) + err = fstore.UpdateLastTopic(topicName, int(lastID), user.Name, user.ID, time.Now().Format("2006-01-02 15:04:05"), fid) if err != nil && err != ErrNoRows { InternalError(err, w) } @@ -865,7 +865,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request, user User) { return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID) if !ok { return @@ -933,7 +933,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request, user User) { } http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) - err = increase_post_user_stats(wcount, user.ID, false, user) + err = user.increasePostStats(wcount, false) if err != nil { InternalError(err, w) return @@ -962,7 +962,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request, user User) { return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID) if !ok { return @@ -1073,7 +1073,7 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request, user User) return } - // TO-DO: Add hooks to make use of headerLite + // TODO: Add hooks to make use of headerLite _, ok := SimpleForumSessionCheck(w, r, &user, fid) if !ok { return @@ -1171,8 +1171,8 @@ func route_profile_reply_create(w http.ResponseWriter, r *http.Request, user Use return } - var user_name string - err = get_user_name_stmt.QueryRow(uid).Scan(&user_name) + var userName string + err = get_user_name_stmt.QueryRow(uid).Scan(&userName) if err == ErrNoRows { LocalError("The profile you're trying to post on doesn't exist.", w, r, user) return @@ -1184,7 +1184,7 @@ func route_profile_reply_create(w http.ResponseWriter, r *http.Request, user Use http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther) } -func route_report_submit(w http.ResponseWriter, r *http.Request, user User, sitem_id string) { +func route_report_submit(w http.ResponseWriter, r *http.Request, user User, sitemID string) { if !user.Loggedin { LoginRequired(w, r, user) return @@ -1204,18 +1204,18 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site return } - item_id, err := strconv.Atoi(sitem_id) + itemID, err := strconv.Atoi(sitemID) if err != nil { LocalError("Bad ID", w, r, user) return } - item_type := r.FormValue("type") + itemType := r.FormValue("type") - var fid int = 1 + var fid = 1 var title, content string - if item_type == "reply" { - reply, err := getReply(item_id) + if itemType == "reply" { + reply, err := getReply(itemID) if err == ErrNoRows { LocalError("We were unable to find the reported post", w, r, user) return @@ -1234,9 +1234,9 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site } title = "Reply: " + topic.Title - content = reply.Content + "\n\nOriginal Post: #rid-" + strconv.Itoa(item_id) - } else if item_type == "user-reply" { - userReply, err := getUserReply(item_id) + content = reply.Content + "\n\nOriginal Post: #rid-" + strconv.Itoa(itemID) + } else if itemType == "user-reply" { + userReply, err := getUserReply(itemID) if err == ErrNoRows { LocalError("We weren't able to find the reported post", w, r, user) return @@ -1255,8 +1255,8 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site } title = "Profile: " + title content = userReply.Content + "\n\nOriginal Post: @" + strconv.Itoa(userReply.ParentID) - } else if item_type == "topic" { - err = get_topic_basic_stmt.QueryRow(item_id).Scan(&title, &content) + } else if itemType == "topic" { + err = get_topic_basic_stmt.QueryRow(itemID).Scan(&title, &content) if err == ErrNoRows { NotFound(w, r) return @@ -1265,10 +1265,10 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site return } title = "Topic: " + title - content = content + "\n\nOriginal Post: #tid-" + strconv.Itoa(item_id) + content = content + "\n\nOriginal Post: #tid-" + strconv.Itoa(itemID) } else { if vhooks["report_preassign"] != nil { - runVhookNoreturn("report_preassign", &item_id, &item_type) + runVhookNoreturn("report_preassign", &itemID, &itemType) return } // Don't try to guess the type @@ -1277,7 +1277,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site } var count int - rows, err := report_exists_stmt.Query(item_type + "_" + strconv.Itoa(item_id)) + rows, err := report_exists_stmt.Query(itemType + "_" + strconv.Itoa(itemID)) if err != nil && err != ErrNoRows { InternalError(err, w) return @@ -1295,7 +1295,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site return } - res, err := create_report_stmt.Exec(title, content, parseMessage(content), user.ID, item_type+"_"+strconv.Itoa(item_id)) + res, err := create_report_stmt.Exec(title, content, parseMessage(content), user.ID, itemType+"_"+strconv.Itoa(itemID)) if err != nil { InternalError(err, w) return @@ -1546,14 +1546,15 @@ func route_account_own_edit_username_submit(w http.ResponseWriter, r *http.Reque return } - new_username := html.EscapeString(r.PostFormValue("account-new-username")) - _, err = set_username_stmt.Exec(new_username, strconv.Itoa(user.ID)) + newUsername := html.EscapeString(r.PostFormValue("account-new-username")) + _, err = set_username_stmt.Exec(newUsername, strconv.Itoa(user.ID)) if err != nil { LocalError("Unable to change the username. Does someone else already have this name?", w, r, user) return } - user.Name = new_username + // TODO: Use the reloaded data instead for the name? + user.Name = newUsername err = users.Load(user.ID) if err != nil { LocalError("Your account doesn't exist!", w, r, user) @@ -1704,6 +1705,7 @@ func route_account_own_edit_email_token_submit(w http.ResponseWriter, r *http.Re templates.ExecuteTemplate(w, "account-own-edit-email.html", pi) } +// TODO: Move this into member_routes.go func route_logout(w http.ResponseWriter, r *http.Request, user User) { if !user.Loggedin { LocalError("You can't logout without logging in first.", w, r, user) @@ -1731,9 +1733,9 @@ func route_login(w http.ResponseWriter, r *http.Request, user User) { templates.ExecuteTemplate(w, "login.html", pi) } -// TO-DO: Log failed attempted logins? -// TO-DO: Lock IPS out if they have too many failed attempts? -// TO-DO: Log unusual countries in comparison to the country a user usually logs in from? Alert the user about this? +// TODO: Log failed attempted logins? +// TODO: Lock IPS out if they have too many failed attempts? +// TODO: Log unusual countries in comparison to the country a user usually logs in from? Alert the user about this? func route_login_submit(w http.ResponseWriter, r *http.Request, user User) { if user.Loggedin { LocalError("You're already logged in.", w, r, user) @@ -1842,11 +1844,11 @@ func route_register_submit(w http.ResponseWriter, r *http.Request, user User) { return } - confirm_password := r.PostFormValue("confirm_password") - log.Print("Registration Attempt! Username: " + username) + confirmPassword := r.PostFormValue("confirm_password") + log.Print("Registration Attempt! Username: " + username) // TODO: Add controls over what is logged when? // Do the two inputted passwords match..? - if password != confirm_password { + if password != confirmPassword { LocalError("The two passwords don't match.", w, r, user) return } @@ -1898,8 +1900,48 @@ func route_register_submit(w http.ResponseWriter, r *http.Request, user User) { http.Redirect(w, r, "/", http.StatusSeeOther) } -// TO-DO: We don't need support XML here to support sitemaps, we could handle those elsewhere -var phrase_login_alerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`) +// TODO: Set the cookie domain +func route_change_theme(w http.ResponseWriter, r *http.Request, user User) { + //headerLite, _ := SimpleSessionCheck(w, r, &user) + err := r.ParseForm() + if err != nil { + PreError("Bad Form", w, r) + return + } + + // TODO: Rename isJs to something else, just in case we rewrite the JS side in WebAssembly? + isJs := (r.PostFormValue("isJs") == "1") + + newTheme := html.EscapeString(r.PostFormValue("newTheme")) + + theme, ok := themes[newTheme] + if !ok || theme.HideFromThemes { + log.Print("Bad Theme: ", newTheme) + LocalErrorJSQ("That theme doesn't exist", w, r, user, isJs) + return + } + + // TODO: Store the current theme in the user's account? + /*if user.Loggedin { + _, err = change_theme_stmt.Exec(newTheme, user.ID) + if err != nil { + InternalError(err, w) + return + } + }*/ + + cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: year} + http.SetCookie(w, &cookie) + + if !isJs { + http.Redirect(w, r, "/", http.StatusSeeOther) + } else { + _, _ = w.Write(successJSONBytes) + } +} + +// TODO: We don't need support XML here to support sitemaps, we could handle those elsewhere +var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`) func route_api(w http.ResponseWriter, r *http.Request, user User) { w.Header().Set("Content-Type", "application/json") @@ -1931,7 +1973,7 @@ func route_api(w http.ResponseWriter, r *http.Request, user User) { } case "alerts": // A feed of events tailored for a specific user if !user.Loggedin { - w.Write(phrase_login_alerts) + w.Write(phraseLoginAlerts) return } diff --git a/routes_common.go b/routes_common.go new file mode 100644 index 00000000..99443f65 --- /dev/null +++ b/routes_common.go @@ -0,0 +1,3 @@ +package main + +// TODO: Move PreRoute and it's friends here in the next commit, I'm avoiding doing it in this one as I don't want the diffs to be lost diff --git a/schema/mysql/query_sync.sql b/schema/mysql/query_sync.sql new file mode 100644 index 00000000..156d1390 --- /dev/null +++ b/schema/mysql/query_sync.sql @@ -0,0 +1,3 @@ +CREATE TABLE `sync` ( + `last_update` datetime not null +); \ No newline at end of file diff --git a/schema/pgsql/query_sync.sql b/schema/pgsql/query_sync.sql new file mode 100644 index 00000000..ac13e7c9 --- /dev/null +++ b/schema/pgsql/query_sync.sql @@ -0,0 +1,3 @@ +CREATE TABLE `sync` ( + `last_update` timestamp not null +); \ No newline at end of file diff --git a/setting.go b/setting.go index b026585c..d7406342 100644 --- a/setting.go +++ b/setting.go @@ -24,7 +24,6 @@ type Setting struct { func init() { settingBox.Store(SettingBox(make(map[string]interface{}))) - //settingBox.Store(make(map[string]interface{})) } func LoadSettings() error { @@ -34,8 +33,7 @@ func LoadSettings() error { } defer rows.Close() - sBox := settingBox.Load().(SettingBox) - //sBox := settingBox.Load().(map[string]interface{}) + var sBox = SettingBox(make(map[string]interface{})) var sname, scontent, stype, sconstraints string for rows.Next() { err = rows.Scan(&sname, &scontent, &stype, &sconstraints) @@ -56,10 +54,10 @@ func LoadSettings() error { return nil } -// TO-DO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions. +// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions. func (sBox SettingBox) ParseSetting(sname string, scontent string, stype string, constraint string) string { var err error - var ssBox map[string]interface{} = map[string]interface{}(sBox) + var ssBox = map[string]interface{}(sBox) if stype == "bool" { ssBox[sname] = (scontent == "1") } else if stype == "int" { diff --git a/site.go b/site.go index 5dc82273..2125078a 100644 --- a/site.go +++ b/site.go @@ -2,61 +2,57 @@ package main import "net/http" -var site *Site = &Site{Name:"Magical Fairy Land"} -var db_config DB_Config = DB_Config{Host:"localhost"} +var site = &Site{Name: "Magical Fairy Land"} +var db_config = DB_Config{Host: "localhost"} var config Config var dev DevConfig -type Site struct -{ - Name string - Email string - Url string - Port string - EnableSsl bool +type Site struct { + Name string + Email string + Url string + Port string + EnableSsl bool EnableEmails bool - HasProxy bool + HasProxy bool } -type DB_Config struct -{ - Host string +type DB_Config struct { + Host string Username string Password string - Dbname string - Port string + Dbname string + Port string } -type Config struct -{ - SslPrivkey string +type Config struct { + SslPrivkey string SslFullchain string - MaxRequestSize int - CacheTopicUser int - UserCacheCapacity int + MaxRequestSize int + CacheTopicUser int + UserCacheCapacity int TopicCacheCapacity int - SmtpServer string + SmtpServer string SmtpUsername string SmtpPassword string - SmtpPort string + SmtpPort string - DefaultRoute func(http.ResponseWriter, *http.Request, User) - DefaultGroup int - ActivationGroup int - StaffCss string + DefaultRoute func(http.ResponseWriter, *http.Request, User) + DefaultGroup int + ActivationGroup int + StaffCss string UncategorisedForumVisible bool - MinifyTemplates bool - MultiServer bool + MinifyTemplates bool + MultiServer bool - Noavatar string + Noavatar string ItemsPerPage int } -type DevConfig struct -{ - DebugMode bool +type DevConfig struct { + DebugMode bool SuperDebug bool - Profiling bool + Profiling bool } diff --git a/tasks.go b/tasks.go index 02dfd82e..c81bdf08 100644 --- a/tasks.go +++ b/tasks.go @@ -2,6 +2,12 @@ package main import "time" +var lastSync time.Time + +func init() { + lastSync = time.Now() +} + func handleExpiredScheduledGroups() error { rows, err := get_expired_scheduled_groups_stmt.Query() if err != nil { @@ -27,3 +33,37 @@ func handleExpiredScheduledGroups() error { } return rows.Err() } + +func handleServerSync() error { + var lastUpdate time.Time + var lastUpdateStr string + err := get_sync_stmt.QueryRow().Scan(&lastUpdateStr) + if err != nil { + return err + } + + layout := "2006-01-02 15:04:05" + lastUpdate, err = time.Parse(layout, lastUpdateStr) + if err != nil { + return err + } + + if lastUpdate.After(lastSync) { + // TODO: A more granular sync + err = fstore.LoadForums() + if err != nil { + return err + } + // TODO: Resync the groups + // TODO: Resync the permissions + err = LoadSettings() + if err != nil { + return err + } + err = LoadWordFilters() + if err != nil { + return err + } + } + return nil +} diff --git a/template_forum.go b/template_forum.go index 2045f858..ab115c63 100644 --- a/template_forum.go +++ b/template_forum.go @@ -166,10 +166,26 @@ w.Write(forum_42) } w.Write(forum_43) w.Write(footer_0) -if tmpl_forum_vars.Header.Widgets.RightSidebar != "" { +if len(tmpl_forum_vars.Header.Themes) != 0 { +for _, item := range tmpl_forum_vars.Header.Themes { +if !item.HideFromThemes { w.Write(footer_1) -w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar))) +w.Write([]byte(item.Name)) w.Write(footer_2) -} +if tmpl_forum_vars.Header.ThemeName == item.Name { w.Write(footer_3) } +w.Write(footer_4) +w.Write([]byte(item.FriendlyName)) +w.Write(footer_5) +} +} +} +w.Write(footer_6) +if tmpl_forum_vars.Header.Widgets.RightSidebar != "" { +w.Write(footer_7) +w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar))) +w.Write(footer_8) +} +w.Write(footer_9) +} diff --git a/template_forums.go b/template_forums.go index aa36fdb8..de35f365 100644 --- a/template_forums.go +++ b/template_forums.go @@ -109,10 +109,26 @@ w.Write(forums_17) } w.Write(forums_18) w.Write(footer_0) -if tmpl_forums_vars.Header.Widgets.RightSidebar != "" { +if len(tmpl_forums_vars.Header.Themes) != 0 { +for _, item := range tmpl_forums_vars.Header.Themes { +if !item.HideFromThemes { w.Write(footer_1) -w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar))) +w.Write([]byte(item.Name)) w.Write(footer_2) -} +if tmpl_forums_vars.Header.ThemeName == item.Name { w.Write(footer_3) } +w.Write(footer_4) +w.Write([]byte(item.FriendlyName)) +w.Write(footer_5) +} +} +} +w.Write(footer_6) +if tmpl_forums_vars.Header.Widgets.RightSidebar != "" { +w.Write(footer_7) +w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar))) +w.Write(footer_8) +} +w.Write(footer_9) +} diff --git a/template_init.go b/template_init.go index c283e73c..b21bd368 100644 --- a/template_init.go +++ b/template_init.go @@ -6,8 +6,9 @@ import "net/http" var templates = template.New("") +// nolint func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["topic"] + mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["topic"] if !ok { mapping = "topic" } @@ -17,11 +18,13 @@ func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) { } } +// nolint var template_topic_handle func(TopicPage, http.ResponseWriter) = interpreted_topic_template var template_topic_alt_handle func(TopicPage, http.ResponseWriter) = interpreted_topic_template +// nolint var template_topics_handle func(TopicsPage, http.ResponseWriter) = func(pi TopicsPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["topics"] + mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["topics"] if !ok { mapping = "topics" } @@ -31,8 +34,9 @@ var template_topics_handle func(TopicsPage, http.ResponseWriter) = func(pi Topic } } +// nolint var template_forum_handle func(ForumPage, http.ResponseWriter) = func(pi ForumPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["forum"] + mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["forum"] if !ok { mapping = "forum" } @@ -42,8 +46,9 @@ var template_forum_handle func(ForumPage, http.ResponseWriter) = func(pi ForumPa } } +// nolint var template_forums_handle func(ForumsPage, http.ResponseWriter) = func(pi ForumsPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["forums"] + mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["forums"] if !ok { mapping = "forums" } @@ -53,8 +58,9 @@ var template_forums_handle func(ForumsPage, http.ResponseWriter) = func(pi Forum } } +// nolint var template_profile_handle func(ProfilePage, http.ResponseWriter) = func(pi ProfilePage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["profile"] + mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["profile"] if !ok { mapping = "profile" } @@ -64,8 +70,9 @@ var template_profile_handle func(ProfilePage, http.ResponseWriter) = func(pi Pro } } +// nolint var template_create_topic_handle func(CreateTopicPage, http.ResponseWriter) = func(pi CreateTopicPage, w http.ResponseWriter) { - mapping, ok := themes[defaultTheme].TemplatesMap["create-topic"] + mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["create-topic"] if !ok { mapping = "create-topic" } @@ -77,12 +84,18 @@ var template_create_topic_handle func(CreateTopicPage, http.ResponseWriter) = fu func compileTemplates() error { var c CTemplateSet + + // Schemas to train the template compiler on what to expect + // TODO: Add support for interface{}s user := User{62, buildProfileURL("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? + // TODO: Do a more accurate level calculation for this? user2 := User{1, buildProfileURL("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, buildProfileURL("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, + Settings: settingBox.Load().(SettingBox), + Themes: themes, + ThemeName: defaultThemeBox.Load().(string), NoticeList: []string{"test"}, Stylesheets: []string{"panel"}, Scripts: []string{"whatever"}, @@ -97,66 +110,64 @@ func compileTemplates() error { 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) + var varList = make(map[string]VarItem) tpage := TopicPage{"Title", user, headerVars, replyList, topic, 1, 1} - topic_id_tmpl, err := c.compileTemplate("topic.html", "templates/", "TopicPage", tpage, varList) + topicIDTmpl, err := c.compileTemplate("topic.html", "templates/", "TopicPage", tpage, varList) if err != nil { return err } - topic_id_alt_tmpl, err := c.compileTemplate("topic_alt.html", "templates/", "TopicPage", tpage, varList) + topicIDAltTmpl, err := c.compileTemplate("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} - profile_tmpl, err := c.compileTemplate("profile.html", "templates/", "ProfilePage", ppage, varList) + profileTmpl, err := c.compileTemplate("profile.html", "templates/", "ProfilePage", ppage, varList) if err != nil { return err } var forumList []Forum - forums, err := fstore.GetAll() + forums, err := fstore.GetAllVisible() if err != nil { return err } for _, forum := range forums { - if forum.Active { - forumList = append(forumList, *forum) - } + forumList = append(forumList, *forum) } varList = make(map[string]VarItem) - forums_page := ForumsPage{"Forum List", user, headerVars, forumList} - forums_tmpl, err := c.compileTemplate("forums.html", "templates/", "ForumsPage", forums_page, varList) + forumsPage := ForumsPage{"Forum List", user, headerVars, forumList} + forumsTmpl, err := c.compileTemplate("forums.html", "templates/", "ForumsPage", forumsPage, 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} - topics_tmpl, err := c.compileTemplate("topics.html", "templates/", "TopicsPage", topics_page, varList) + topicsPage := TopicsPage{"Topic List", user, headerVars, topicsList} + topicsTmpl, err := c.compileTemplate("topics.html", "templates/", "TopicsPage", topicsPage, 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} - forum_tmpl, err := c.compileTemplate("forum.html", "templates/", "ForumPage", forum_page, varList) + forumItem := Forum{1, "general", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0, "", "", 0, "", 0, ""} + forumPage := ForumPage{"General Forum", user, headerVars, topicsList, forumItem, 1, 1} + forumTmpl, err := c.compileTemplate("forum.html", "templates/", "ForumPage", forumPage, varList) if err != nil { return err } log.Print("Writing the templates") - go writeTemplate("topic", topic_id_tmpl) - go writeTemplate("topic_alt", topic_id_alt_tmpl) - go writeTemplate("profile", profile_tmpl) - go writeTemplate("forums", forums_tmpl) - go writeTemplate("topics", topics_tmpl) - go writeTemplate("forum", forum_tmpl) + go writeTemplate("topic", topicIDTmpl) + go writeTemplate("topic_alt", topicIDAltTmpl) + go writeTemplate("profile", profileTmpl) + go writeTemplate("forums", forumsTmpl) + go writeTemplate("topics", topicsTmpl) + go writeTemplate("forum", forumTmpl) go func() { err := writeFile("./template_list.go", "package main\n\n// nolint\n"+c.FragOut) if err != nil { @@ -180,66 +191,62 @@ func initTemplates() { } compileTemplates() - // TO-DO: Add support for 64-bit integers - // TO-DO: Add support for floats + // TODO: Add support for 64-bit integers + // TODO: 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 + var leftInt, rightInt int switch left := left.(type) { case uint, uint8, uint16, int, int32: - left_int = left.(int) + leftInt = left.(int) } switch right := right.(type) { case uint, uint8, uint16, int, int32: - right_int = right.(int) + rightInt = right.(int) } - return left_int + right_int + return leftInt + rightInt } fmap["subtract"] = func(left interface{}, right interface{}) interface{} { - var left_int int - var right_int int + var leftInt, rightInt int switch left := left.(type) { case uint, uint8, uint16, int, int32: - left_int = left.(int) + leftInt = left.(int) } switch right := right.(type) { case uint, uint8, uint16, int, int32: - right_int = right.(int) + rightInt = right.(int) } - return left_int - right_int + return leftInt - rightInt } fmap["multiply"] = func(left interface{}, right interface{}) interface{} { - var left_int int - var right_int int + var leftInt, rightInt int switch left := left.(type) { case uint, uint8, uint16, int, int32: - left_int = left.(int) + leftInt = left.(int) } switch right := right.(type) { case uint, uint8, uint16, int, int32: - right_int = right.(int) + rightInt = right.(int) } - return left_int * right_int + return leftInt * rightInt } fmap["divide"] = func(left interface{}, right interface{}) interface{} { - var left_int int - var right_int int + var leftInt, rightInt int switch left := left.(type) { case uint, uint8, uint16, int, int32: - left_int = left.(int) + leftInt = left.(int) } switch right := right.(type) { case uint, uint8, uint16, int, int32: - right_int = right.(int) + rightInt = right.(int) } - if left_int == 0 || right_int == 0 { + if leftInt == 0 || rightInt == 0 { return 0 } - return left_int / right_int + return leftInt / rightInt } // The interpreted templates... diff --git a/template_list.go b/template_list.go index 0be2e907..cc0e824c 100644 --- a/template_list.go +++ b/template_list.go @@ -245,11 +245,27 @@ var topic_91 = []byte(` `) -var footer_0 = []byte(` +var footer_0 = []byte(` + + `) +var footer_7 = []byte(``) +var footer_9 = []byte(`
@@ -471,7 +487,7 @@ var profile_14 = []byte(`&type=user" class="profile_menu_item report_item">Repor
`) var profile_15 = []byte(` - + @@ -675,23 +691,26 @@ var topics_19 = []byte(` +
`) -var topics_25 = []byte(`
+var topics_27 = []byte(`" class="lastName" style="font-size: 14px;">`) +var topics_28 = []byte(`
Last: `) -var topics_26 = []byte(` +var topics_29 = []byte(`
`) -var topics_27 = []byte(`
There aren't any topics yet.`) -var topics_28 = []byte(` Start one?`) -var topics_29 = []byte(`
`) -var topics_30 = []byte(` +var topics_30 = []byte(`
There aren't any topics yet.`) +var topics_31 = []byte(` Start one?`) +var topics_32 = []byte(`
`) +var topics_33 = []byte(`
diff --git a/template_profile.go b/template_profile.go index 1d41b53e..7bece59e 100644 --- a/template_profile.go +++ b/template_profile.go @@ -158,10 +158,26 @@ w.Write(profile_41) w.Write(profile_42) w.Write(profile_43) w.Write(footer_0) -if tmpl_profile_vars.Header.Widgets.RightSidebar != "" { +if len(tmpl_profile_vars.Header.Themes) != 0 { +for _, item := range tmpl_profile_vars.Header.Themes { +if !item.HideFromThemes { w.Write(footer_1) -w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar))) +w.Write([]byte(item.Name)) w.Write(footer_2) -} +if tmpl_profile_vars.Header.ThemeName == item.Name { w.Write(footer_3) } +w.Write(footer_4) +w.Write([]byte(item.FriendlyName)) +w.Write(footer_5) +} +} +} +w.Write(footer_6) +if tmpl_profile_vars.Header.Widgets.RightSidebar != "" { +w.Write(footer_7) +w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar))) +w.Write(footer_8) +} +w.Write(footer_9) +} diff --git a/template_topic.go b/template_topic.go index f98c769c..53458c8c 100644 --- a/template_topic.go +++ b/template_topic.go @@ -273,10 +273,26 @@ w.Write(topic_90) } w.Write(topic_91) w.Write(footer_0) -if tmpl_topic_vars.Header.Widgets.RightSidebar != "" { +if len(tmpl_topic_vars.Header.Themes) != 0 { +for _, item := range tmpl_topic_vars.Header.Themes { +if !item.HideFromThemes { w.Write(footer_1) -w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar))) +w.Write([]byte(item.Name)) w.Write(footer_2) -} +if tmpl_topic_vars.Header.ThemeName == item.Name { w.Write(footer_3) } +w.Write(footer_4) +w.Write([]byte(item.FriendlyName)) +w.Write(footer_5) +} +} +} +w.Write(footer_6) +if tmpl_topic_vars.Header.Widgets.RightSidebar != "" { +w.Write(footer_7) +w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar))) +w.Write(footer_8) +} +w.Write(footer_9) +} diff --git a/template_topic_alt.go b/template_topic_alt.go index a2f2a86a..ee0e9853 100644 --- a/template_topic_alt.go +++ b/template_topic_alt.go @@ -266,10 +266,26 @@ w.Write(topic_alt_87) } w.Write(topic_alt_88) w.Write(footer_0) -if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" { +if len(tmpl_topic_alt_vars.Header.Themes) != 0 { +for _, item := range tmpl_topic_alt_vars.Header.Themes { +if !item.HideFromThemes { w.Write(footer_1) -w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar))) +w.Write([]byte(item.Name)) w.Write(footer_2) -} +if tmpl_topic_alt_vars.Header.ThemeName == item.Name { w.Write(footer_3) } +w.Write(footer_4) +w.Write([]byte(item.FriendlyName)) +w.Write(footer_5) +} +} +} +w.Write(footer_6) +if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" { +w.Write(footer_7) +w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar))) +w.Write(footer_8) +} +w.Write(footer_9) +} diff --git a/template_topics.go b/template_topics.go index 3bf2fe00..331b350a 100644 --- a/template_topics.go +++ b/template_topics.go @@ -3,8 +3,8 @@ // Code generated by Gosora. More below: /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ package main -import "strconv" import "net/http" +import "strconv" // nolint func init() { @@ -115,32 +115,56 @@ if item.Sticky { w.Write(topics_19) } w.Write(topics_20) -if item.LastUser.Avatar != "" { +if item.Sticky { w.Write(topics_21) -w.Write([]byte(item.LastUser.Avatar)) +} else { +if item.IsClosed { w.Write(topics_22) } +} w.Write(topics_23) -w.Write([]byte(item.LastUser.Link)) +if item.LastUser.Avatar != "" { w.Write(topics_24) -w.Write([]byte(item.LastUser.Name)) +w.Write([]byte(item.LastUser.Avatar)) w.Write(topics_25) -w.Write([]byte(item.LastReplyAt)) +} w.Write(topics_26) -} -} else { +w.Write([]byte(item.LastUser.Link)) w.Write(topics_27) -if tmpl_topics_vars.CurrentUser.Perms.CreateTopic { +w.Write([]byte(item.LastUser.Name)) w.Write(topics_28) -} +w.Write([]byte(item.LastReplyAt)) w.Write(topics_29) } +} else { w.Write(topics_30) -w.Write(footer_0) -if tmpl_topics_vars.Header.Widgets.RightSidebar != "" { -w.Write(footer_1) -w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar))) -w.Write(footer_2) +if tmpl_topics_vars.CurrentUser.Perms.CreateTopic { +w.Write(topics_31) } +w.Write(topics_32) +} +w.Write(topics_33) +w.Write(footer_0) +if len(tmpl_topics_vars.Header.Themes) != 0 { +for _, item := range tmpl_topics_vars.Header.Themes { +if !item.HideFromThemes { +w.Write(footer_1) +w.Write([]byte(item.Name)) +w.Write(footer_2) +if tmpl_topics_vars.Header.ThemeName == item.Name { w.Write(footer_3) } +w.Write(footer_4) +w.Write([]byte(item.FriendlyName)) +w.Write(footer_5) +} +} +} +w.Write(footer_6) +if tmpl_topics_vars.Header.Widgets.RightSidebar != "" { +w.Write(footer_7) +w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar))) +w.Write(footer_8) +} +w.Write(footer_9) +} diff --git a/templates.go b/templates.go index b9cd549b..5d993162 100644 --- a/templates.go +++ b/templates.go @@ -13,7 +13,7 @@ import ( "text/template/parse" ) -// TO-DO: Turn this file into a library +// TODO: Turn this file into a library var ctemplates []string var tmplPtrMap = make(map[string]interface{}) var textOverlapList = make(map[string]int) @@ -263,6 +263,11 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value for _, key := range outVal.MapKeys() { item = outVal.MapIndex(key) } + fmt.Println("Range item:", item) + + if !item.IsValid() { + panic("item" + "^\n" + "Invalid map. Maybe, it doesn't have any entries for the template engine to analyse?") + } if node.ElseList != nil { out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n} else {\n" + c.compileSwitch("item", item, templateName, node.ElseList) + "}\n" @@ -352,13 +357,23 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va } if !cur.IsValid() { + if dev.DebugMode { + fmt.Println("Debug Data:") + fmt.Println("Holdreflect:", holdreflect) + fmt.Println("Holdreflect.Kind()", holdreflect.Kind()) + if !dev.SuperDebug { + fmt.Println("cur.Kind():", cur.Kind().String()) + } + fmt.Println("") + } + panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?") } cur = cur.FieldByName(id) if cur.Kind() == reflect.Interface { cur = cur.Elem() - // TO-DO: Surely, there's a better way of detecting this? + // TODO: Surely, there's a better way of detecting this? /*if cur.Kind() == reflect.String && cur.Type().Name() != "string" { varbit = "string(" + varbit + "." + id + ")"*/ //if cur.Kind() == reflect.String && cur.Type().Name() != "string" { @@ -795,7 +810,7 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat continue } - // TO-DO: Fix this up so that it works for regular pointers and not just struct pointers. Ditto for the other cur.Kind() == reflect.Ptr we have in this file + // TODO: Fix this up so that it works for regular pointers and not just struct pointers. Ditto for the other cur.Kind() == reflect.Ptr we have in this file if cur.Kind() == reflect.Ptr { if dev.SuperDebug { fmt.Println("Looping over pointer") @@ -950,7 +965,7 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec } } - // TO-DO: Cascade errors back up the tree to the caller? + // TODO: Cascade errors back up the tree to the caller? res, err := ioutil.ReadFile(c.dir + node.Name) if err != nil { log.Fatal(err) @@ -998,7 +1013,7 @@ func (c *CTemplateSet) compileCommand(*parse.CommandNode) (out string) { panic("Uh oh! Something went wrong!") } -// TO-DO: Write unit tests for this +// TODO: Write unit tests for this func minify(data string) string { data = strings.Replace(data, "\t", "", -1) data = strings.Replace(data, "\v", "", -1) @@ -1008,30 +1023,30 @@ func minify(data string) string { return data } -// TO-DO: Strip comments -// TO-DO: Handle CSS nested in