Revamped the configuration system. If you have trouble installing this, try installing my fork of gopsutil with `go get` and delete the users_penalties table from the schema folder. That stuff will be cleaned up in the next commit. Fixed an issue with the links for the Uncategorised forum being broken. Swapped out NOW() for UTC_TIMESTAMP() in MySQL queries. We now get an error message when the server goes down for whatever reason. The template compiler now supports pointers. Swapped out shirou/gopsutil for a fork locked on an older and more stable commit of the same library. Fixed a bug where the preprocessor didn't play nice with empty CSS files. Added the site name to the navbar. Added more things to .gitignore Laid the foundations for the next commit.
328 lines
9.0 KiB
Go
328 lines
9.0 KiB
Go
/* Work in progress. Check back later! */
|
|
package main
|
|
|
|
import "log"
|
|
import "sync"
|
|
//import "sync/atomic"
|
|
import "database/sql"
|
|
import "./query_gen/lib"
|
|
|
|
var forum_update_mutex sync.Mutex
|
|
var forum_create_mutex sync.Mutex
|
|
var forum_perms map[int]map[int]ForumPerms // [gid][fid]Perms
|
|
var fstore ForumStore
|
|
|
|
type ForumStore interface
|
|
{
|
|
LoadForums() error
|
|
DirtyGet(id int) *Forum
|
|
Get(id int) (*Forum, error)
|
|
CascadeGet(id int) (*Forum, error)
|
|
CascadeGetCopy(id int) (Forum, error)
|
|
BypassGet(id int) (*Forum, error)
|
|
Load(id int) error
|
|
Set(forum *Forum) error
|
|
//Update(Forum) error
|
|
//CascadeUpdate(Forum) error
|
|
Delete(id int) error
|
|
CascadeDelete(id int) error
|
|
IncrementTopicCount(id int) error
|
|
DecrementTopicCount(id int) error
|
|
UpdateLastTopic(topic_name string, tid int, username string, uid int, time string, fid int) error
|
|
Exists(id int) bool
|
|
GetAll() ([]*Forum,error)
|
|
GetAllIDs() ([]int,error)
|
|
//GetChildren(parentID int, parentType string) ([]*Forum,error)
|
|
//GetFirstChild(parentID int, parentType string) (*Forum,error)
|
|
CreateForum(forum_name string, forum_desc string, active bool, preset string) (int, error)
|
|
}
|
|
|
|
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
|
|
//fids []int
|
|
forumCapCount int
|
|
|
|
get *sql.Stmt
|
|
get_all *sql.Stmt
|
|
}
|
|
|
|
func NewStaticForumStore() *StaticForumStore {
|
|
get_stmt, err := qgen.Builder.SimpleSelect("forums","name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","fid = ?","","")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
get_all_stmt, err := qgen.Builder.SimpleSelect("forums","fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","","fid ASC","")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return &StaticForumStore{
|
|
get: get_stmt,
|
|
get_all: get_all_stmt,
|
|
}
|
|
}
|
|
|
|
func (sfs *StaticForumStore) LoadForums() error {
|
|
log.Print("Adding the uncategorised forum")
|
|
var forums []*Forum = []*Forum{
|
|
&Forum{0,build_forum_url(name_to_slug("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 int = 1
|
|
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)
|
|
if err != nil {
|
|
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.fill_forum_id_gap(i, forum.ID)
|
|
}
|
|
|
|
if forum.Name == "" {
|
|
if dev.DebugMode {
|
|
log.Print("Adding a placeholder forum")
|
|
}
|
|
} else {
|
|
log.Print("Adding the " + forum.Name + " forum")
|
|
}
|
|
|
|
forum.Link = build_forum_url(name_to_slug(forum.Name),forum.ID)
|
|
forum.LastTopicSlug = build_slug(name_to_slug(forum.LastTopic),forum.LastTopicID)
|
|
forums = append(forums,&forum)
|
|
}
|
|
err = rows.Err()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sfs.forums = forums
|
|
sfs.forumCapCount = i
|
|
|
|
return nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) DirtyGet(id int) *Forum {
|
|
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
|
return &Forum{ID:-1,Name:""}
|
|
}
|
|
return sfs.forums[id]
|
|
}
|
|
|
|
func (sfs *StaticForumStore) Get(id int) (*Forum, error) {
|
|
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
|
return nil, ErrNoRows
|
|
}
|
|
return sfs.forums[id], nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) CascadeGet(id int) (*Forum, error) {
|
|
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
|
return nil, ErrNoRows
|
|
}
|
|
return sfs.forums[id], nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) CascadeGetCopy(id int) (forum Forum, err error) {
|
|
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
|
return forum, ErrNoRows
|
|
}
|
|
return *sfs.forums[id], nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) BypassGet(id int) (*Forum, error) {
|
|
var forum 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)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &forum, nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) Load(id int) error {
|
|
var forum 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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sfs.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 {
|
|
forum_update_mutex.Lock()
|
|
if !sfs.Exists(forum.ID) {
|
|
forum_update_mutex.Unlock()
|
|
return ErrNoRows
|
|
}
|
|
sfs.forums[forum.ID] = forum
|
|
forum_update_mutex.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) GetAll() ([]*Forum,error) {
|
|
return sfs.forums, nil
|
|
}
|
|
|
|
// TO-DO: Implement sub-forums.
|
|
/*func (sfs *StaticForumStore) GetChildren(parentID int, parentType string) ([]*Forum,error) {
|
|
return nil, 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 int = sfs.forumCapCount
|
|
var ids []int = make([]int,max)
|
|
for i := 0; i < max; i++ {
|
|
ids[i] = i
|
|
}
|
|
return ids, nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) Exists(id int) bool {
|
|
return (id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != ""
|
|
}
|
|
|
|
func (sfs *StaticForumStore) Delete(id int) error {
|
|
forum_update_mutex.Lock()
|
|
if !sfs.Exists(id) {
|
|
forum_update_mutex.Unlock()
|
|
return nil
|
|
}
|
|
sfs.forums[id].Name = ""
|
|
forum_update_mutex.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) CascadeDelete(id int) error {
|
|
forum, err := sfs.CascadeGet(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
forum_update_mutex.Lock()
|
|
_, err = delete_forum_stmt.Exec(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
forum.Name = ""
|
|
forum_update_mutex.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) IncrementTopicCount(id int) error {
|
|
forum, err := sfs.CascadeGet(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = add_topics_to_forum_stmt.Exec(1,id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
forum.TopicCount += 1
|
|
return nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) DecrementTopicCount(id int) error {
|
|
forum, err := sfs.CascadeGet(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = remove_topics_from_forum_stmt.Exec(1, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
forum.TopicCount -= 1
|
|
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)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = update_forum_cache_stmt.Exec(topic_name,tid,username,uid,fid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
forum.LastTopic = topic_name
|
|
forum.LastTopicID = tid
|
|
forum.LastReplyer = username
|
|
forum.LastReplyerID = uid
|
|
forum.LastTopicTime = time
|
|
|
|
return nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) CreateForum(forum_name string, forum_desc 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 {
|
|
forum_update_mutex.Lock()
|
|
_, err = update_forum_stmt.Exec(forum_name, forum_desc, active, preset, fid)
|
|
if err != nil {
|
|
return fid, err
|
|
}
|
|
forum, err := sfs.Get(fid)
|
|
if err != nil {
|
|
return 0, ErrCacheDesync
|
|
}
|
|
forum.Name = forum_name
|
|
forum.Desc = forum_desc
|
|
forum.Active = active
|
|
forum.Preset = preset
|
|
forum_update_mutex.Unlock()
|
|
return fid, nil
|
|
}
|
|
|
|
forum_create_mutex.Lock()
|
|
res, err := create_forum_stmt.Exec(forum_name, forum_desc, active, preset)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
fid64, err := res.LastInsertId()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
fid = int(fid64)
|
|
|
|
sfs.forums = append(sfs.forums, &Forum{fid,name_to_slug(forum_name),forum_name,forum_desc,active,preset,0,"",0,"","",0,"",0,""})
|
|
sfs.forumCapCount++
|
|
|
|
// TO-DO: Add a GroupStore. How would it interact with the ForumStore?
|
|
permmap_to_query(preset_to_permmap(preset),fid)
|
|
forum_create_mutex.Unlock()
|
|
return fid, nil
|
|
}
|
|
|
|
func (sfs *StaticForumStore) fill_forum_id_gap(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: Work on MapForumStore
|
|
|
|
// TO-DO: Work on SqlForumStore
|