Added the topic and user memory caches. Currently used in an extremely limited way, we plan to expand this!

Added a template minifier which strips out many types of whitespace.
Stop the router from blocking the other goroutines.
Content is no longer an interface{} in the TopicUser and Reply structs
Allow plugins to add new routes without any potential race coditions occuring
Use non-breaking spaces in the topic view to make it harder for the minifier to break things.
This commit is contained in:
Azareal 2017-02-11 14:51:16 +00:00
parent 534ef10194
commit 399128c208
20 changed files with 561 additions and 84 deletions

23
cache.go Normal file
View File

@ -0,0 +1,23 @@
package main
import "errors"
const CACHE_STATIC int = 0
const CACHE_DYNAMIC int = 1
const CACHE_SQL int = 2
var ErrStoreCapacityOverflow = errors.New("This datastore has already reached it's max capacity")
var users UserStore
var topics TopicStore
type DataStore interface {
Get(id int) (interface{}, error)
GetUnsafe(id int) (interface{}, error)
CascadeGet(id int) (interface{}, error)
Add(item interface{}) error
AddUnsafe(item interface{}) error
Remove(id int) error
RemoveUnsafe(id int) error
GetLength() int
GetCapacity() int
}

View File

@ -11,6 +11,9 @@ var dbport = "3306" // You probably won't need to change this
var max_request_size = 5 * megabyte var max_request_size = 5 * megabyte
// Misc // Misc
var cache_topicuser = CACHE_STATIC
var user_cache_capacity = 100 // The max number of users held in memory
var topic_cache_capacity = 100 // The max number of topics held in memory
var default_route = route_topics var default_route = route_topics
var default_group = 3 // Should be a setting var default_group = 3 // Should be a setting
var activation_group = 5 // Should be a setting var activation_group = 5 // Should be a setting

View File

@ -112,6 +112,7 @@ CREATE TABLE `users_replies`(
CREATE TABLE `likes`( CREATE TABLE `likes`(
`weight` tinyint DEFAULT 1 not null, `weight` tinyint DEFAULT 1 not null,
/*`type` tinyint not null, /* Regular Post = 1, Big Post = 2, Mega Post = 3, etc.*/
`targetItem` int not null, `targetItem` int not null,
`targetType` varchar(50) DEFAULT 'replies' not null, `targetType` varchar(50) DEFAULT 'replies' not null,
`sentBy` int not null, `sentBy` int not null,

View File

@ -149,6 +149,9 @@ var dbport = "` + db_port + `" // You probably won't need to change this
var max_request_size = 5 * megabyte var max_request_size = 5 * megabyte
// Misc // Misc
var cache_topicuser = CACHE_STATIC
var user_cache_capacity = 100 // The max number of users held in memory
var topic_cache_capacity = 100 // The max number of topics held in memory
var default_route = route_topics var default_route = route_topics
var default_group = 3 // Should be a setting var default_group = 3 // Should be a setting
var activation_group = 5 // Should be a setting var activation_group = 5 // Should be a setting

14
main.go
View File

@ -51,9 +51,9 @@ func compile_templates() {
log.Print("Compiling the templates") log.Print("Compiling the templates")
topic := TopicUser{1,"Blah",template.HTML("Hey there!"),0,false,false,"Date","Date",0,"","127.0.0.1",0,1,"","",no_css_tmpl,0,"","","","",58,false} topic := TopicUser{1,"Blah","Hey there!",0,false,false,"Date","Date",0,"","127.0.0.1",0,1,"",default_group,"",no_css_tmpl,0,"","","","",58,false}
var replyList []Reply var replyList []Reply
replyList = append(replyList, Reply{0,0,"",template.HTML("Yo!"),0,"","",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1}) replyList = append(replyList, Reply{0,0,"","Yo!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1})
var varList map[string]VarItem = make(map[string]VarItem) var varList map[string]VarItem = make(map[string]VarItem)
tpage := TopicPage{"Title",user,noticeList,replyList,topic,1,1,false} tpage := TopicPage{"Title",user,noticeList,replyList,topic,1,1,false}
@ -80,7 +80,7 @@ func compile_templates() {
topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList)
var topicList []TopicUser var topicList []TopicUser
topicList = append(topicList,TopicUser{1,"Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"Admin","","",0,"","","","",58,false}) topicList = append(topicList,TopicUser{1,"Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"Admin",default_group,"","",0,"","","","",58,false})
forum_item := Forum{1,"General Forum",true,"all",0,"",0,"",0,""} forum_item := Forum{1,"General Forum",true,"all",0,"",0,"",0,""}
forum_page := ForumPage{"General Forum",user,noticeList,topicList,forum_item,1,1,nil} forum_page := ForumPage{"General Forum",user,noticeList,topicList,forum_item,1,1,nil}
forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList) forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList)
@ -143,6 +143,14 @@ func main(){
log.Fatal(err) log.Fatal(err)
} }
if cache_topicuser == CACHE_STATIC {
users = NewStaticUserStore(user_cache_capacity)
topics = NewStaticTopicStore(topic_cache_capacity)
} else {
users = NewSqlUserStore()
topics = NewSqlTopicStore()
}
log.Print("Loading the static files.") log.Print("Loading the static files.")
err = filepath.Walk("./public", func(path string, f os.FileInfo, err error) error { err = filepath.Walk("./public", func(path string, f os.FileInfo, err error) error {
if f.IsDir() { if f.IsDir() {

View File

@ -9,9 +9,11 @@ import "strconv"
import "encoding/json" import "encoding/json"
var db *sql.DB var db *sql.DB
var get_session_stmt *sql.Stmt var get_user_stmt *sql.Stmt
var get_full_user_stmt *sql.Stmt
var get_topic_list_stmt *sql.Stmt var get_topic_list_stmt *sql.Stmt
var get_topic_user_stmt *sql.Stmt var get_topic_user_stmt *sql.Stmt
var get_topic_stmt *sql.Stmt
var get_topic_replies_stmt *sql.Stmt var get_topic_replies_stmt *sql.Stmt
var get_topic_replies_offset_stmt *sql.Stmt var get_topic_replies_offset_stmt *sql.Stmt
var get_forum_topics_stmt *sql.Stmt var get_forum_topics_stmt *sql.Stmt
@ -90,8 +92,20 @@ func init_database(err error) {
log.Fatal(err) log.Fatal(err)
} }
log.Print("Preparing get_session statement.") /*log.Print("Preparing get_session statement.")
get_session_stmt, err = db.Prepare("select `uid`,`name`,`group`,`is_super_admin`,`session`,`email`,`avatar`,`message`,`url_prefix`,`url_name`,`level`,`score`,`last_ip` from `users` where `uid` = ? and `session` = ? AND `session` <> ''") get_session_stmt, err = db.Prepare("select `uid`,`name`,`group`,`is_super_admin`,`session`,`email`,`avatar`,`message`,`url_prefix`,`url_name`,`level`,`score`,`last_ip` from `users` where `uid` = ? and `session` = ? AND `session` <> ''")
if err != nil {
log.Fatal(err)
}*/
log.Print("Preparing get_user statement.")
get_user_stmt, err = db.Prepare("select `name`,`group`,`is_super_admin`,`avatar`,`message`,`url_prefix`,`url_name`,`level` from `users` where `uid` = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing get_full_user statement.")
get_full_user_stmt, err = db.Prepare("select `name`,`group`,`is_super_admin`,`session`,`email`,`avatar`,`message`,`url_prefix`,`url_name`,`level`,`score`,`last_ip` from `users` where `uid` = ?")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -108,6 +122,12 @@ func init_database(err error) {
log.Fatal(err) log.Fatal(err)
} }
log.Print("Preparing get_topic statement.")
get_topic_stmt, err = db.Prepare("select title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount from topics where tid = ?")
if err != nil {
log.Fatal(err)
}
log.Print("Preparing get_topic_replies statement.") log.Print("Preparing get_topic_replies statement.")
get_topic_replies_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 from replies left join users ON replies.createdBy = users.uid where tid = ?") get_topic_replies_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 from replies left join users ON replies.createdBy = users.uid where tid = ?")
if err != nil { if err != nil {

View File

@ -1,5 +1,4 @@
package main package main
import "html/template"
func init() { func init() {
plugins["helloworld"] = NewPlugin("helloworld","Hello World","Azareal","http://github.com/Azareal","","","",init_helloworld,nil,deactivate_helloworld) plugins["helloworld"] = NewPlugin("helloworld","Hello World","Azareal","http://github.com/Azareal","","","",init_helloworld,nil,deactivate_helloworld)
@ -17,7 +16,7 @@ func deactivate_helloworld() {
func helloworld_reply(data interface{}) interface{} { func helloworld_reply(data interface{}) interface{} {
reply := data.(Reply) reply := data.(Reply)
reply.Content = "Hello World!" reply.Content = "Hello World!"
reply.ContentHtml = template.HTML("Hello World!") reply.ContentHtml = "Hello World!"
reply.Tag = "Auto" reply.Tag = "Auto"
return reply return reply
} }

View File

@ -7,9 +7,10 @@ type Reply struct
ID int ID int
ParentID int ParentID int
Content string Content string
ContentHtml template.HTML ContentHtml string
CreatedBy int CreatedBy int
CreatedByName string CreatedByName string
Group int
CreatedAt string CreatedAt string
LastEdit int LastEdit int
LastEditBy int LastEditBy int

View File

@ -16,38 +16,45 @@ func NewRouter() *Router {
} }
func (router *Router) Handle(pattern string, handle http.Handler) { func (router *Router) Handle(pattern string, handle http.Handler) {
router.mu.Lock()
router.routes[pattern] = handle.ServeHTTP router.routes[pattern] = handle.ServeHTTP
router.mu.Unlock()
} }
func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request)) { func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request)) {
router.mu.Lock()
router.routes[pattern] = handle router.routes[pattern] = handle
router.mu.Unlock()
} }
func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
router.mu.RLock() router.mu.RLock()
defer router.mu.RUnlock()
if req.URL.Path[0] != '/' { if req.URL.Path[0] != '/' {
router.mu.RUnlock()
w.WriteHeader(405) w.WriteHeader(405)
w.Write([]byte("")) w.Write([]byte(""))
return return
} }
// Do something on the path to turn slashes facing the wrong way "\" into "/" slashes. If it's bytes, then alter the bytes in place for the maximum speed // Do something on the path to turn slashes facing the wrong way "\" into "/" slashes. Like what? Wouldn't that be slow? Might need to move to fasthttp for this
handle, ok := router.routes[req.URL.Path] handle, ok := router.routes[req.URL.Path]
if ok { if ok {
router.mu.RUnlock()
handle(w,req) handle(w,req)
return return
} }
if req.URL.Path[len(req.URL.Path) - 1] == '/' { if req.URL.Path[len(req.URL.Path) - 1] == '/' {
router.mu.RUnlock()
NotFound(w,req) NotFound(w,req)
return return
} }
handle, ok = router.routes[req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]] handle, ok = router.routes[req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]]
if ok { if ok {
router.mu.RUnlock()
handle(w,req) handle(w,req)
return return
} }
@ -55,10 +62,12 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
handle, ok = router.routes[req.URL.Path + "/"] handle, ok = router.routes[req.URL.Path + "/"]
if ok { if ok {
router.mu.RUnlock()
handle(w,req) handle(w,req)
return return
} }
router.mu.RUnlock()
NotFound(w,req) NotFound(w,req)
return return
} }

View File

@ -288,23 +288,20 @@ func route_forums(w http.ResponseWriter, r *http.Request){
func route_topic_id(w http.ResponseWriter, r *http.Request){ func route_topic_id(w http.ResponseWriter, r *http.Request){
var( var(
err error err error
content string
group int
page int page int
offset int offset int
replyList []Reply replyList []Reply
) )
page, _ = strconv.Atoi(r.FormValue("page")) page, _ = strconv.Atoi(r.FormValue("page"))
topic := TopicUser{Css: no_css_tmpl} tid, err := strconv.Atoi(r.URL.Path[len("/topic/"):])
topic.ID, err = strconv.Atoi(r.URL.Path[len("/topic/"):])
if err != nil { if err != nil {
PreError("The provided TopicID is not a valid number.",w,r) PreError("The provided TopicID is not a valid number.",w,r)
return return
} }
// Get the topic.. // Get the topic..
err = get_topic_user_stmt.QueryRow(topic.ID).Scan(&topic.Title, &content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.CreatedByName, &topic.Avatar, &group, &topic.URLPrefix, &topic.URLName, &topic.Level) topic, err := get_topicuser(tid)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
NotFound(w,r) NotFound(w,r)
return return
@ -312,6 +309,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
topic.Css = no_css_tmpl
user, noticeList, ok := ForumSessionCheck(w,r,topic.ParentID) user, noticeList, ok := ForumSessionCheck(w,r,topic.ParentID)
if !ok { if !ok {
@ -322,8 +320,8 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
return return
} }
topic.Content = template.HTML(parse_message(content)) topic.ContentLines = strings.Count(topic.Content,"\n")
topic.ContentLines = strings.Count(content,"\n") topic.Content = parse_message(topic.Content)
// We don't want users posting in locked topics... // We don't want users posting in locked topics...
if topic.Is_Closed && !user.Is_Mod { if topic.Is_Closed && !user.Is_Mod {
@ -337,12 +335,12 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
} else { } else {
topic.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topic.CreatedBy),1) topic.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(topic.CreatedBy),1)
} }
if groups[group].Is_Mod || groups[group].Is_Admin { if groups[topic.Group].Is_Mod || groups[topic.Group].Is_Admin {
topic.Css = staff_css_tmpl topic.Css = staff_css_tmpl
topic.Level = -1 topic.Level = -1
} }
topic.Tag = groups[group].Tag topic.Tag = groups[topic.Group].Tag
if settings["url_tags"] == false { if settings["url_tags"] == false {
topic.URLName = "" topic.URLName = ""
@ -382,16 +380,16 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
replyItem := Reply{Css: no_css_tmpl} replyItem := Reply{Css: no_css_tmpl}
for rows.Next() { for rows.Next() {
err := rows.Scan(&replyItem.ID, &replyItem.Content, &replyItem.CreatedBy, &replyItem.CreatedAt, &replyItem.LastEdit, &replyItem.LastEditBy, &replyItem.Avatar, &replyItem.CreatedByName, &group, &replyItem.URLPrefix, &replyItem.URLName, &replyItem.Level, &replyItem.IpAddress, &replyItem.LikeCount) err := rows.Scan(&replyItem.ID, &replyItem.Content, &replyItem.CreatedBy, &replyItem.CreatedAt, &replyItem.LastEdit, &replyItem.LastEditBy, &replyItem.Avatar, &replyItem.CreatedByName, &replyItem.Group, &replyItem.URLPrefix, &replyItem.URLName, &replyItem.Level, &replyItem.IpAddress, &replyItem.LikeCount)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
replyItem.ParentID = topic.ID replyItem.ParentID = topic.ID
replyItem.ContentHtml = template.HTML(parse_message(replyItem.Content)) replyItem.ContentHtml = parse_message(replyItem.Content)
replyItem.ContentLines = strings.Count(replyItem.Content,"\n") replyItem.ContentLines = strings.Count(replyItem.Content,"\n")
if groups[group].Is_Mod || groups[group].Is_Admin { if groups[replyItem.Group].Is_Mod || groups[replyItem.Group].Is_Admin {
replyItem.Css = staff_css_tmpl replyItem.Css = staff_css_tmpl
replyItem.Level = -1 replyItem.Level = -1
} else { } else {
@ -405,7 +403,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
replyItem.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyItem.CreatedBy),1) replyItem.Avatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyItem.CreatedBy),1)
} }
replyItem.Tag = groups[group].Tag replyItem.Tag = groups[replyItem.Group].Tag
/*if settings["url_tags"] == false { /*if settings["url_tags"] == false {
replyItem.URLName = "" replyItem.URLName = ""
@ -462,7 +460,7 @@ func route_profile(w http.ResponseWriter, r *http.Request){
replyCss template.CSS replyCss template.CSS
replyLines int replyLines int
replyTag string replyTag string
group int replyGroup int
replyList []Reply replyList []Reply
) )
@ -479,7 +477,7 @@ func route_profile(w http.ResponseWriter, r *http.Request){
puser = user puser = user
} else { } else {
// Fetch the user data // Fetch the user data
err = db.QueryRow("select `name`,`group`,`is_super_admin`,`avatar`,`message`,`url_prefix`,`url_name`,`level` from `users` where `uid` = ?", puser.ID).Scan(&puser.Name, &puser.Group, &puser.Is_Super_Admin, &puser.Avatar, &puser.Message, &puser.URLPrefix, &puser.URLName, &puser.Level) err = get_user_stmt.QueryRow(puser.ID).Scan(&puser.Name, &puser.Group, &puser.Is_Super_Admin, &puser.Avatar, &puser.Message, &puser.URLPrefix, &puser.URLName, &puser.Level)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
NotFound(w,r) NotFound(w,r)
return return
@ -516,14 +514,14 @@ func route_profile(w http.ResponseWriter, r *http.Request){
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &group) err := rows.Scan(&rid, &replyContent, &replyCreatedBy, &replyCreatedAt, &replyLastEdit, &replyLastEditBy, &replyAvatar, &replyCreatedByName, &replyGroup)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
replyLines = strings.Count(replyContent,"\n") replyLines = strings.Count(replyContent,"\n")
if groups[group].Is_Mod || groups[group].Is_Admin { if groups[replyGroup].Is_Mod || groups[replyGroup].Is_Admin {
replyCss = staff_css_tmpl replyCss = staff_css_tmpl
} else { } else {
replyCss = no_css_tmpl replyCss = no_css_tmpl
@ -536,8 +534,8 @@ func route_profile(w http.ResponseWriter, r *http.Request){
replyAvatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyCreatedBy),1) replyAvatar = strings.Replace(noavatar,"{id}",strconv.Itoa(replyCreatedBy),1)
} }
if groups[group].Tag != "" { if groups[replyGroup].Tag != "" {
replyTag = groups[group].Tag replyTag = groups[replyGroup].Tag
} else if puser.ID == replyCreatedBy { } else if puser.ID == replyCreatedBy {
replyTag = "Profile Owner" replyTag = "Profile Owner"
} else { } else {
@ -547,7 +545,7 @@ func route_profile(w http.ResponseWriter, r *http.Request){
replyLiked := false replyLiked := false
replyLikeCount := 0 replyLikeCount := 0
replyList = append(replyList, Reply{rid,puser.ID,replyContent,template.HTML(parse_message(replyContent)),replyCreatedBy,replyCreatedByName,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss,replyLines,replyTag,"","","",0,"",replyLiked,replyLikeCount}) replyList = append(replyList, Reply{rid,puser.ID,replyContent,parse_message(replyContent),replyCreatedBy,replyCreatedByName,replyGroup,replyCreatedAt,replyLastEdit,replyLastEditBy,replyAvatar,replyCss,replyLines,replyTag,"","","",0,"",replyLiked,replyLikeCount})
} }
err = rows.Err() err = rows.Err()
if err != nil { if err != nil {

View File

@ -1,7 +1,7 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "strconv"
import "io" import "io"
import "strconv"
func init() { func init() {
template_forum_handle = template_forum template_forum_handle = template_forum

View File

@ -97,25 +97,25 @@ var topic_23 []byte = []byte(`</p>
var topic_24 []byte = []byte(`</textarea><br /><br /> var topic_24 []byte = []byte(`</textarea><br /><br />
<a href="/user/`) <a href="/user/`)
var topic_25 []byte = []byte(`" class="username real_username">`) var topic_25 []byte = []byte(`" class="username real_username">`)
var topic_26 []byte = []byte(`</a> var topic_26 []byte = []byte(`</a>&nbsp;
<a href="/topic/like/submit/`) <a href="/topic/like/submit/`)
var topic_27 []byte = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="`) var topic_27 []byte = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="`)
var topic_28 []byte = []byte(`background-color:/*#eaffea*/#D6FFD6;`) var topic_28 []byte = []byte(`background-color:/*#eaffea*/#D6FFD6;`)
var topic_29 []byte = []byte(`">😀</button></a> var topic_29 []byte = []byte(`">😀</button></a>&nbsp;
`) `)
var topic_30 []byte = []byte(`<a href='/topic/edit/`) var topic_30 []byte = []byte(`<a href='/topic/edit/`)
var topic_31 []byte = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic"><button class="username">🖊</button></a> var topic_31 []byte = []byte(`' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic"><button class="username">🖊</button></a>&nbsp;
<a href='/topic/delete/submit/`) <a href='/topic/delete/submit/`)
var topic_32 []byte = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username">🗑</button></a> var topic_32 []byte = []byte(`' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username">🗑</button></a>&nbsp;
`) `)
var topic_33 []byte = []byte(`<a class="mod_button" href='/topic/unstick/submit/`) var topic_33 []byte = []byte(`<a class="mod_button" href='/topic/unstick/submit/`)
var topic_34 []byte = []byte(`' style="font-weight:normal;" title="Unpin Topic"><button class="username" style="background-color:/*#eaffea*/#D6FFD6;">📌</button></a>`) var topic_34 []byte = []byte(`' style="font-weight:normal;" title="Unpin Topic"><button class="username" style="background-color:/*#eaffea*/#D6FFD6;">📌</button></a>`)
var topic_35 []byte = []byte(`<a href='/topic/stick/submit/`) var topic_35 []byte = []byte(`<a href='/topic/stick/submit/`)
var topic_36 []byte = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username">📌</button></a>`) var topic_36 []byte = []byte(`' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username">📌</button></a>`)
var topic_37 []byte = []byte(` var topic_37 []byte = []byte(`&nbsp;
<a href="/report/submit/`) <a href="/report/submit/`)
var topic_38 []byte = []byte(`?session=`) var topic_38 []byte = []byte(`?session=`)
var topic_39 []byte = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username">🚩</button></a> var topic_39 []byte = []byte(`&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username">🚩</button></a>&nbsp;
`) `)
var topic_40 []byte = []byte(`<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">`) var topic_40 []byte = []byte(`<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">`)
var topic_41 []byte = []byte(`</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>`) var topic_41 []byte = []byte(`</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>`)
@ -138,20 +138,20 @@ var topic_52 []byte = []byte(`">
var topic_53 []byte = []byte(`</p><br /><br /> var topic_53 []byte = []byte(`</p><br /><br />
<a href="/user/`) <a href="/user/`)
var topic_54 []byte = []byte(`" class="username real_username">`) var topic_54 []byte = []byte(`" class="username real_username">`)
var topic_55 []byte = []byte(`</a> var topic_55 []byte = []byte(`</a>&nbsp;
<a href="/reply/like/submit/`) <a href="/reply/like/submit/`)
var topic_56 []byte = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="`) var topic_56 []byte = []byte(`" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="`)
var topic_57 []byte = []byte(`background-color:/*#eaffea*/#D6FFD6;`) var topic_57 []byte = []byte(`background-color:/*#eaffea*/#D6FFD6;`)
var topic_58 []byte = []byte(`">😀</button></a> var topic_58 []byte = []byte(`">😀</button></a>&nbsp;
`) `)
var topic_59 []byte = []byte(`<a href="/reply/edit/submit/`) var topic_59 []byte = []byte(`<a href="/reply/edit/submit/`)
var topic_60 []byte = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item">🖊️</button></a> `) var topic_60 []byte = []byte(`" class="mod_button" title="Edit Reply"><button class="username edit_item">🖊️</button></a>&nbsp;`)
var topic_61 []byte = []byte(`<a href="/reply/delete/submit/`) var topic_61 []byte = []byte(`<a href="/reply/delete/submit/`)
var topic_62 []byte = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item">🗑️</button></a> `) var topic_62 []byte = []byte(`" class="mod_button" title="Delete Reply"><button class="username delete_item">🗑️</button></a>&nbsp;`)
var topic_63 []byte = []byte(` var topic_63 []byte = []byte(`
<a href="/report/submit/`) <a href="/report/submit/`)
var topic_64 []byte = []byte(`?session=`) var topic_64 []byte = []byte(`?session=`)
var topic_65 []byte = []byte(`&type=reply" class="mod_button" title="Flag Reply"><button class="username report_item">🚩</button></a> var topic_65 []byte = []byte(`&type=reply" class="mod_button" title="Flag Reply"><button class="username report_item">🚩</button></a>&nbsp;
`) `)
var topic_66 []byte = []byte(`<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">`) var topic_66 []byte = []byte(`<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">`)
var topic_67 []byte = []byte(`</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>`) var topic_67 []byte = []byte(`</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>`)

View File

@ -85,7 +85,7 @@ w.Write(profile_19)
w.Write([]byte(string(item.Css))) w.Write([]byte(string(item.Css)))
} }
w.Write(profile_20) w.Write(profile_20)
w.Write([]byte(string(item.ContentHtml))) w.Write([]byte(item.ContentHtml))
w.Write(profile_21) w.Write(profile_21)
w.Write([]byte(strconv.Itoa(item.CreatedBy))) w.Write([]byte(strconv.Itoa(item.CreatedBy)))
w.Write(profile_22) w.Write(profile_22)

View File

@ -1,7 +1,6 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "strconv" import "strconv"
import "html/template"
import "io" import "io"
func init() { func init() {
@ -92,9 +91,9 @@ w.Write(topic_21)
w.Write([]byte(string(tmpl_topic_vars.Topic.Css))) w.Write([]byte(string(tmpl_topic_vars.Topic.Css)))
} }
w.Write(topic_22) w.Write(topic_22)
w.Write([]byte(string(tmpl_topic_vars.Topic.Content.(template.HTML)))) w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_23) w.Write(topic_23)
w.Write([]byte(string(tmpl_topic_vars.Topic.Content.(template.HTML)))) w.Write([]byte(tmpl_topic_vars.Topic.Content))
w.Write(topic_24) w.Write(topic_24)
w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.CreatedBy))) w.Write([]byte(strconv.Itoa(tmpl_topic_vars.Topic.CreatedBy)))
w.Write(topic_25) w.Write(topic_25)
@ -156,7 +155,7 @@ w.Write(topic_51)
w.Write([]byte(string(item.Css))) w.Write([]byte(string(item.Css)))
} }
w.Write(topic_52) w.Write(topic_52)
w.Write([]byte(string(item.ContentHtml))) w.Write([]byte(item.ContentHtml))
w.Write(topic_53) w.Write(topic_53)
w.Write([]byte(strconv.Itoa(item.CreatedBy))) w.Write([]byte(strconv.Itoa(item.CreatedBy)))
w.Write(topic_54) w.Write(topic_54)

View File

@ -2,7 +2,6 @@
package main package main
import "io" import "io"
import "strconv" import "strconv"
import "html/template"
func init() { func init() {
template_topic_alt_handle = template_topic_alt template_topic_alt_handle = template_topic_alt
@ -117,9 +116,9 @@ w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.Level)))
w.Write(topic_alt_34) w.Write(topic_alt_34)
} }
w.Write(topic_alt_35) w.Write(topic_alt_35)
w.Write([]byte(string(tmpl_topic_alt_vars.Topic.Content.(template.HTML)))) w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_36) w.Write(topic_alt_36)
w.Write([]byte(string(tmpl_topic_alt_vars.Topic.Content.(template.HTML)))) w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_37) w.Write(topic_alt_37)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs { if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_38) w.Write(topic_alt_38)
@ -146,7 +145,7 @@ w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_48) w.Write(topic_alt_48)
} }
w.Write(topic_alt_49) w.Write(topic_alt_49)
w.Write([]byte(string(item.ContentHtml))) w.Write([]byte(item.ContentHtml))
w.Write(topic_alt_50) w.Write(topic_alt_50)
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply { if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_51) w.Write(topic_alt_51)

View File

@ -1,7 +1,7 @@
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "io"
import "strconv" import "strconv"
import "io"
func init() { func init() {
template_topics_handle = template_topics template_topics_handle = template_topics

View File

@ -12,6 +12,11 @@ import "text/template/parse"
var ctemplates []string var ctemplates []string
var tmpl_ptr_map map[string]interface{} = make(map[string]interface{}) var tmpl_ptr_map map[string]interface{} = make(map[string]interface{})
var text_overlap_list map[string]int
func init() {
text_overlap_list = make(map[string]int)
}
type VarItem struct type VarItem struct
{ {
@ -80,7 +85,11 @@ func (c *CTemplateSet) compile_template(name string, dir string, expects string,
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
content := string(res) content := string(res)
if !debug {
content = minify(content)
}
tree := parse.New(name, c.funcMap) tree := parse.New(name, c.funcMap)
var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree) var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree)
@ -822,7 +831,11 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
content := string(res) content := string(res)
if !debug {
content = minify(content)
}
tree := parse.New(node.Name, c.funcMap) tree := parse.New(node.Name, c.funcMap)
var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree) var treeSet map[string]*parse.Tree = make(map[string]*parse.Tree)
@ -859,4 +872,13 @@ func (c *CTemplateSet) compile_subtemplate(pvarholder string, pholdreflect refle
func (c *CTemplateSet) compile_command(*parse.CommandNode) (out string) { func (c *CTemplateSet) compile_command(*parse.CommandNode) (out string) {
return "" return ""
} }
func minify(data string) string {
data = strings.Replace(data,"\t","",-1)
data = strings.Replace(data,"\v","",-1)
data = strings.Replace(data,"\n","",-1)
data = strings.Replace(data,"\r","",-1)
data = strings.Replace(data," "," ",-1)
return data
}

View File

@ -22,13 +22,13 @@
<div class="rowitem passive editable_parent post_item" style="border-bottom: none;{{if .Topic.Avatar}}background-image:url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;background-size:128px;padding-left:136px;{{.Topic.Css}}{{end}}"> <div class="rowitem passive editable_parent post_item" style="border-bottom: none;{{if .Topic.Avatar}}background-image:url({{.Topic.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;background-size:128px;padding-left:136px;{{.Topic.Css}}{{end}}">
<p class="hide_on_edit topic_content user_content" style="margin:0;padding:0;">{{.Topic.Content}}</p> <p class="hide_on_edit topic_content user_content" style="margin:0;padding:0;">{{.Topic.Content}}</p>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea><br /><br /> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea><br /><br />
<a href="/user/{{.Topic.CreatedBy}}" class="username real_username">{{.Topic.CreatedByName}}</a> <a href="/user/{{.Topic.CreatedBy}}" class="username real_username">{{.Topic.CreatedByName}}</a>&nbsp;
<a href="/topic/like/submit/{{.Topic.ID}}" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="{{if .Topic.Liked}}background-color:/*#eaffea*/#D6FFD6;{{end}}">😀</button></a> <a href="/topic/like/submit/{{.Topic.ID}}" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="{{if .Topic.Liked}}background-color:/*#eaffea*/#D6FFD6;{{end}}">😀</button></a>&nbsp;
{{if .CurrentUser.Is_Mod}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic"><button class="username">🖊️</button></a> {{if .CurrentUser.Is_Mod}}<a href='/topic/edit/{{.Topic.ID}}' class="mod_button open_edit" style="font-weight:normal;" title="Edit Topic"><button class="username">🖊️</button></a>&nbsp;
<a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username">🗑️</button></a> <a href='/topic/delete/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Delete Topic"><button class="username">🗑️</button></a>&nbsp;
{{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic"><button class="username" style="background-color:/*#eaffea*/#D6FFD6;">📌</button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username">📌</button></a>{{end}} {{if .Topic.Sticky}}<a class="mod_button" href='/topic/unstick/submit/{{.Topic.ID}}' style="font-weight:normal;" title="Unpin Topic"><button class="username" style="background-color:/*#eaffea*/#D6FFD6;">📌</button></a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="mod_button" style="font-weight:normal;" title="Pin Topic"><button class="username">📌</button></a>{{end}}
{{end}} {{end}}&nbsp;
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username">🚩</button></a> <a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="mod_button report_item" style="font-weight:normal;" title="Flag Topic"><button class="username">🚩</button></a>&nbsp;
{{if .Topic.LikeCount}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.Topic.LikeCount}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>{{end}} {{if .Topic.LikeCount}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.Topic.LikeCount}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>{{end}}
{{if .Topic.Tag}}<a class="username hide_on_micro" style="float:right;color:#505050;font-size:16px;">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.Topic.Level}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;" title="Level">👑</a>{{end}} {{if .Topic.Tag}}<a class="username hide_on_micro" style="float:right;color:#505050;font-size:16px;">{{.Topic.Tag}}</a>{{else}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.Topic.Level}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;" title="Level">👑</a>{{end}}
</div> </div>
@ -36,11 +36,11 @@
<div class="rowblock post_container" style="overflow: hidden;">{{range .ItemList}} <div class="rowblock post_container" style="overflow: hidden;">{{range .ItemList}}
<div class="rowitem rowhead passive deletable_block editable_parent post_item" style="{{if .Avatar}}background-image:url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;background-size:128px;padding-left:136px;{{.Css}}{{end}}"> <div class="rowitem rowhead passive deletable_block editable_parent post_item" style="{{if .Avatar}}background-image:url({{.Avatar}}), url(/static/white-dot.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;background-size:128px;padding-left:136px;{{.Css}}{{end}}">
<p class="editable_block user_content" style="margin: 0;padding: 0;">{{.ContentHtml}}</p><br /><br /> <p class="editable_block user_content" style="margin: 0;padding: 0;">{{.ContentHtml}}</p><br /><br />
<a href="/user/{{.CreatedBy}}" class="username real_username">{{.CreatedByName}}</a> <a href="/user/{{.CreatedBy}}" class="username real_username">{{.CreatedByName}}</a>&nbsp;
<a href="/reply/like/submit/{{.ID}}" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="{{if .Liked}}background-color:/*#eaffea*/#D6FFD6;{{end}}">😀</button></a> <a href="/reply/like/submit/{{.ID}}" class="mod_button" title="Love it" style="color:#202020;"><button class="username" style="{{if .Liked}}background-color:/*#eaffea*/#D6FFD6;{{end}}">😀</button></a>&nbsp;
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="mod_button" title="Edit Reply"><button class="username edit_item">🖊️</button></a> {{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="mod_button" title="Edit Reply"><button class="username edit_item">🖊️</button></a>&nbsp;{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Reply"><button class="username delete_item">🗑️</button></a> {{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="mod_button" title="Delete Reply"><button class="username delete_item">🗑️</button></a>&nbsp;{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button" title="Flag Reply"><button class="username report_item">🚩</button></a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="mod_button" title="Flag Reply"><button class="username report_item">🚩</button></a>&nbsp;
{{if .LikeCount}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.LikeCount}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>{{end}} {{if .LikeCount}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.LikeCount}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;margin-left:5px;" title="Like Count">😀</a>{{end}}
{{if .Tag}}<a class="username hide_on_micro" style="float: right;color:#505050;font-size:16px;">{{.Tag}}</a>{{else}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.Level}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;" title="Level">👑{{end}}</a> {{if .Tag}}<a class="username hide_on_micro" style="float: right;color:#505050;font-size:16px;">{{.Tag}}</a>{{else}}<a class="username hide_on_micro" style="float: right;color:#505050;border-left:none;padding-left:5px;padding-right:5px;font-size:17px;">{{.Level}}</a><a class="username hide_on_micro" style="color:#505050;float:right;opacity:0.85;" title="Level">👑{{end}}</a>
</div> </div>

238
topic.go
View File

@ -1,5 +1,8 @@
package main package main
//import "fmt"
import "sync"
import "html/template" import "html/template"
import "database/sql"
type Topic struct type Topic struct
{ {
@ -10,17 +13,19 @@ type Topic struct
Is_Closed bool Is_Closed bool
Sticky bool Sticky bool
CreatedAt string CreatedAt string
LastReplyAt string
ParentID int ParentID int
Status string // Deprecated. Marked for removal. Status string // Deprecated. Marked for removal.
IpAddress string IpAddress string
PostCount int PostCount int
LikeCount int
} }
type TopicUser struct type TopicUser struct
{ {
ID int ID int
Title string Title string
Content interface{} Content string
CreatedBy int CreatedBy int
Is_Closed bool Is_Closed bool
Sticky bool Sticky bool
@ -33,6 +38,7 @@ type TopicUser struct
LikeCount int LikeCount int
CreatedByName string CreatedByName string
Group int
Avatar string Avatar string
Css template.CSS Css template.CSS
ContentLines int ContentLines int
@ -48,7 +54,7 @@ type TopicsRow struct
{ {
ID int ID int
Title string Title string
Content interface{} Content string
CreatedBy int CreatedBy int
Is_Closed bool Is_Closed bool
Sticky bool Sticky bool
@ -72,3 +78,231 @@ type TopicsRow struct
ForumName string //TopicsRow ForumName string //TopicsRow
} }
type TopicStore interface {
Get(id int) (*Topic, error)
GetUnsafe(id int) (*Topic, error)
CascadeGet(id int) (*Topic, error)
Add(item *Topic) error
AddUnsafe(item *Topic) error
Remove(id int) error
RemoveUnsafe(id int) error
AddLastTopic(item *Topic, fid int) error
GetLength() int
GetCapacity() int
}
type StaticTopicStore struct {
items map[int]*Topic
length int
capacity int
mu sync.RWMutex
}
func NewStaticTopicStore(capacity int) *StaticTopicStore {
return &StaticTopicStore{items:make(map[int]*Topic),capacity:capacity}
}
func (sts *StaticTopicStore) Get(id int) (*Topic, error) {
sts.mu.RLock()
item, ok := sts.items[id]
sts.mu.RUnlock()
if ok {
return item, nil
}
return item, sql.ErrNoRows
}
func (sts *StaticTopicStore) GetUnsafe(id int) (*Topic, error) {
item, ok := sts.items[id]
if ok {
return item, nil
}
return item, sql.ErrNoRows
}
func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) {
sts.mu.RLock()
topic, ok := sts.items[id]
sts.mu.RUnlock()
if ok {
return topic, nil
}
topic = &Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
if err == nil {
sts.Add(topic)
}
return topic, err
}
func (sts *StaticTopicStore) Add(item *Topic) error {
if sts.length >= sts.capacity {
return ErrStoreCapacityOverflow
}
sts.mu.Lock()
sts.items[item.ID] = item
sts.mu.Unlock()
sts.length++
return nil
}
func (sts *StaticTopicStore) AddUnsafe(item *Topic) error {
if sts.length >= sts.capacity {
return ErrStoreCapacityOverflow
}
sts.items[item.ID] = item
sts.length++
return nil
}
func (sts *StaticTopicStore) Remove(id int) error {
sts.mu.Lock()
delete(sts.items,id)
sts.mu.Unlock()
sts.length--
return nil
}
func (sts *StaticTopicStore) RemoveUnsafe(id int) error {
delete(sts.items,id)
sts.length--
return nil
}
func (sts *StaticTopicStore) AddLastTopic(item *Topic, fid int) error {
// Coming Soon...
return nil
}
func (sts *StaticTopicStore) GetLength() int {
return sts.length
}
func (sts *StaticTopicStore) SetCapacity(capacity int) {
sts.capacity = capacity
}
func (sts *StaticTopicStore) GetCapacity() int {
return sts.capacity
}
//type DynamicTopicStore struct {
// items_expiries list.List
// items map[int]*Topic
//}
type SqlTopicStore struct {
}
func NewSqlTopicStore() *SqlTopicStore {
return &SqlTopicStore{}
}
func (sus *SqlTopicStore) Get(id int) (*Topic, error) {
topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
return &topic, err
}
func (sus *SqlTopicStore) GetUnsafe(id int) (*Topic, error) {
topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
return &topic, err
}
func (sus *SqlTopicStore) CascadeGet(id int) (*Topic, error) {
topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount)
return &topic, err
}
// Placeholder methods, the actual queries are done elsewhere
func (sus *SqlTopicStore) Add(item *Topic) error {
return nil
}
func (sus *SqlTopicStore) AddUnsafe(item *Topic) error {
return nil
}
func (sus *SqlTopicStore) Remove(id int) error {
return nil
}
func (sus *SqlTopicStore) RemoveUnsafe(id int) error {
return nil
}
func (sts *SqlTopicStore) AddLastTopic(item *Topic, fid int) error {
// Coming Soon...
return nil
}
func (sts *SqlTopicStore) GetCapacity() int {
return 0
}
func (sus *SqlTopicStore) GetLength() int {
// Return the total number of topics on the forums
return 0
}
func get_topicuser(tid int) (TopicUser,error) {
if cache_topicuser != CACHE_SQL {
topic, err := topics.Get(tid)
if err == nil {
user, err := users.CascadeGet(topic.CreatedBy)
if err != nil {
return TopicUser{ID:tid}, err
}
// We might be better off just passing seperate topic and user structs to the caller?
return copy_topic_to_topicuser(topic, user), nil
} else if users.GetLength() < users.GetCapacity() {
topic, err = topics.CascadeGet(tid)
if err != nil {
return TopicUser{ID:tid}, err
}
user, err := users.CascadeGet(topic.CreatedBy)
if err != nil {
return TopicUser{ID:tid}, err
}
tu := copy_topic_to_topicuser(topic, user)
//fmt.Printf("%+v\n", topic)
//fmt.Println("")
//fmt.Printf("%+v\n", user)
//fmt.Println("")
//fmt.Printf("%+v\n", tu)
//fmt.Println("")
return tu, nil
}
}
tu := TopicUser{ID:tid}
err := get_topic_user_stmt.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.Is_Closed, &tu.Sticky, &tu.ParentID, &tu.IpAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
the_topic := Topic{ID:tu.ID, Title:tu.Title, Content:tu.Content, CreatedBy:tu.CreatedBy, Is_Closed:tu.Is_Closed, Sticky:tu.Sticky, CreatedAt:tu.CreatedAt, LastReplyAt:tu.LastReplyAt, ParentID:tu.ParentID, IpAddress:tu.IpAddress, PostCount:tu.PostCount, LikeCount:tu.LikeCount}
//fmt.Printf("%+v\n", the_topic)
topics.Add(&the_topic)
return tu, err
}
func copy_topic_to_topicuser(topic *Topic, user *User) (tu TopicUser) {
tu.CreatedByName = user.Name
tu.Group = user.Group
tu.Avatar = user.Avatar
tu.URLPrefix = user.URLPrefix
tu.URLName = user.URLName
tu.Level = user.Level
tu.ID = topic.ID
tu.Title = topic.Title
tu.Content = topic.Content
tu.CreatedBy = topic.CreatedBy
tu.Is_Closed = topic.Is_Closed
tu.Sticky = topic.Sticky
tu.CreatedAt = topic.CreatedAt
tu.LastReplyAt = topic.LastReplyAt
tu.ParentID = topic.ParentID
tu.IpAddress = topic.IpAddress
tu.PostCount = topic.PostCount
tu.LikeCount = topic.LikeCount
return tu
}

194
user.go
View File

@ -1,5 +1,6 @@
package main package main
//import "fmt" //import "fmt"
import "sync"
import "strings" import "strings"
import "strconv" import "strconv"
import "net" import "net"
@ -42,6 +43,161 @@ type Email struct
Token string Token string
} }
type UserStore interface {
Get(id int) (*User, error)
GetUnsafe(id int) (*User, error)
CascadeGet(id int) (*User, error)
Add(item *User) error
AddUnsafe(item *User) error
Remove(id int) error
RemoveUnsafe(id int) error
GetLength() int
GetCapacity() int
}
type StaticUserStore struct {
items map[int]*User
length int
capacity int
mu sync.RWMutex
}
func NewStaticUserStore(capacity int) *StaticUserStore {
return &StaticUserStore{items:make(map[int]*User),capacity:capacity}
}
func (sts *StaticUserStore) Get(id int) (*User, error) {
sts.mu.RLock()
item, ok := sts.items[id]
sts.mu.RUnlock()
if ok {
return item, nil
}
return item, sql.ErrNoRows
}
func (sts *StaticUserStore) GetUnsafe(id int) (*User, error) {
item, ok := sts.items[id]
if ok {
return item, nil
}
return item, sql.ErrNoRows
}
func (sts *StaticUserStore) CascadeGet(id int) (*User, error) {
sts.mu.RLock()
user, ok := sts.items[id]
sts.mu.RUnlock()
if ok {
return user, nil
}
user = &User{ID:id}
err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP)
if err == nil {
sts.Add(user)
}
return user, err
}
func (sts *StaticUserStore) Add(item *User) error {
if sts.length >= sts.capacity {
return ErrStoreCapacityOverflow
}
sts.mu.Lock()
sts.items[item.ID] = item
sts.mu.Unlock()
sts.length++
return nil
}
func (sts *StaticUserStore) AddUnsafe(item *User) error {
if sts.length >= sts.capacity {
return ErrStoreCapacityOverflow
}
sts.items[item.ID] = item
sts.length++
return nil
}
func (sts *StaticUserStore) Remove(id int) error {
sts.mu.Lock()
delete(sts.items,id)
sts.mu.Unlock()
sts.length--
return nil
}
func (sts *StaticUserStore) RemoveUnsafe(id int) error {
delete(sts.items,id)
sts.length--
return nil
}
func (sts *StaticUserStore) GetLength() int {
return sts.length
}
func (sts *StaticUserStore) SetCapacity(capacity int) {
sts.capacity = capacity
}
func (sts *StaticUserStore) GetCapacity() int {
return sts.capacity
}
//type DynamicUserStore struct {
// items_expiries list.List
// items map[int]*User
//}
type SqlUserStore struct {
}
func NewSqlUserStore() *SqlUserStore {
return &SqlUserStore{}
}
func (sus *SqlUserStore) Get(id int) (*User, error) {
user := User{ID:id}
err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP)
return &user, err
}
func (sus *SqlUserStore) GetUnsafe(id int) (*User, error) {
user := User{ID:id}
err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP)
return &user, err
}
func (sus *SqlUserStore) CascadeGet(id int) (*User, error) {
user := User{ID:id}
err := get_full_user_stmt.QueryRow(id).Scan(&user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP)
return &user, err
}
// Placeholder methods, the actual queries are done elsewhere
func (sus *SqlUserStore) Add(item *User) error {
return nil
}
func (sus *SqlUserStore) AddUnsafe(item *User) error {
return nil
}
func (sus *SqlUserStore) Remove(id int) error {
return nil
}
func (sus *SqlUserStore) RemoveUnsafe(id int) error {
return nil
}
func (sus *SqlUserStore) GetCapacity() int {
return 0
}
func (sus *SqlUserStore) GetLength() int {
// Return the total number of users registered on the forums
return 0
}
func SetPassword(uid int, password string) (error) { func SetPassword(uid int, password string) (error) {
salt, err := GenerateSafeString(saltLength) salt, err := GenerateSafeString(saltLength)
if err != nil { if err != nil {
@ -128,38 +284,40 @@ func SessionCheck(w http.ResponseWriter, r *http.Request) (user User, noticeList
return user, noticeList, success return user, noticeList, success
} }
func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (user User, success bool) { func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (User,bool) {
// Are there any session cookies..? // Are there any session cookies..?
cookie, err := r.Cookie("uid") cookie, err := r.Cookie("uid")
if err != nil { if err != nil {
user.Group = 6 return User{ID:0,Group:6,Perms:GuestPerms}, true
user.Perms = GuestPerms
return user, true
} }
user.ID, err = strconv.Atoi(cookie.Value) uid, err := strconv.Atoi(cookie.Value)
if err != nil { if err != nil {
user.Group = 6 return User{ID:0,Group:6,Perms:GuestPerms}, true
user.Perms = GuestPerms
return user, true
} }
cookie, err = r.Cookie("session") cookie, err = r.Cookie("session")
if err != nil { if err != nil {
user.Group = 6 return User{ID:0,Group:6,Perms:GuestPerms}, true
user.Perms = GuestPerms
return user, true
} }
// Is this session valid..? // Is this session valid..?
err = get_session_stmt.QueryRow(user.ID,cookie.Value).Scan(&user.ID, &user.Name, &user.Group, &user.Is_Super_Admin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.Last_IP) user, err := users.CascadeGet(uid)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
user.ID = 0 user.ID = 0
user.Session = "" user.Session = ""
user.Group = 6 user.Group = 6
user.Perms = GuestPerms user.Perms = GuestPerms
return user, true return *user, true
} else if err != nil { } else if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return user, false return *user, false
}
if user.Session == "" || cookie.Value != user.Session {
user.ID = 0
user.Session = ""
user.Group = 6
user.Perms = GuestPerms
return *user, true
} }
user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin
@ -187,17 +345,17 @@ func SimpleSessionCheck(w http.ResponseWriter, r *http.Request) (user User, succ
host, _, err := net.SplitHostPort(r.RemoteAddr) host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil { if err != nil {
LocalError("Bad IP",w,r,user) PreError("Bad IP",w,r)
return user, false return *user, false
} }
if host != user.Last_IP { if host != user.Last_IP {
_, err = update_last_ip_stmt.Exec(host, user.ID) _, err = update_last_ip_stmt.Exec(host, user.ID)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return user, false return *user, false
} }
} }
return user, true return *user, true
} }
func words_to_score(wcount int, topic bool) (score int) { func words_to_score(wcount int, topic bool) (score int) {