Added support for HTTP/2 Push.
The themes are still going to be a little broken for a while, but here's the progress I've made. Renamed the topics_trow_assign hook to topics_topic_row_assign. Added more debug code to the generated router. Added a robots.txt file. Gosora now responds to favicon.ico requests with a 404 rather than the topic list. Fixed the tests and some of the benchmarks. Changed the default UserCacheCapacity from 100 to 120. Changed the default TopicCacheCapacity from 100 to 200. Added the last replyer to the topics list and the forum pages. Added the BulkCascadeGetMap method to the UserStore. Refactored the topics list and forum page to load the users with a call to the UserStore rather than via a join. WebSockets now work on SSL. Fixed a race condition when the user permissions are initialised at the start of a request. Fixed a race condition when the user permissions for the OP of a topic are initialised. The rows.Close() calls are deferred once more, so that we can catch problems with recover() Improved support for struct pointers in the template compiler. Added a pin emoji to pinned topics to make them stand-out on the Shadow theme, we have some other ideas in mind for this, but I'd like to get Shadow fully functional for this commit. Fixed a bug an issue with Chrome not detecting XHTML style closes on <form>s. Fixed a visual issue with `color` not being set for textarea elements for the Shadow theme. Fixed a function which wasn't getting renamed for PGSQL. Added seven new UserStore tests.
This commit is contained in:
parent
41c620b988
commit
f41c281e43
|
@ -1,5 +1,7 @@
|
||||||
tmp/*
|
tmp/*
|
||||||
tmp2/*
|
tmp2/*
|
||||||
|
cert_test/*
|
||||||
|
|
||||||
uploads/avatar_*
|
uploads/avatar_*
|
||||||
uploads/socialgroup_*
|
uploads/socialgroup_*
|
||||||
bin/*
|
bin/*
|
||||||
|
|
|
@ -18,7 +18,8 @@ var vhooks map[string]func(...interface{})interface{} = map[string]func(...inter
|
||||||
"forum_check_pre_perms": nil,
|
"forum_check_pre_perms": nil,
|
||||||
"intercept_build_widgets": nil,
|
"intercept_build_widgets": nil,
|
||||||
"forum_trow_assign": nil,
|
"forum_trow_assign": nil,
|
||||||
"topics_trow_assign": nil,
|
"topics_topic_row_assign": nil,
|
||||||
|
//"topics_user_row_assign": nil,
|
||||||
"create_group_preappend": nil, // What is this? Investigate!
|
"create_group_preappend": nil, // What is this? Investigate!
|
||||||
"topic_create_pre_loop": nil,
|
"topic_create_pre_loop": nil,
|
||||||
}
|
}
|
||||||
|
|
14
gen_mysql.go
14
gen_mysql.go
|
@ -39,8 +39,8 @@ var get_topic_basic_stmt *sql.Stmt
|
||||||
var get_activity_entry_stmt *sql.Stmt
|
var get_activity_entry_stmt *sql.Stmt
|
||||||
var forum_entry_exists_stmt *sql.Stmt
|
var forum_entry_exists_stmt *sql.Stmt
|
||||||
var group_entry_exists_stmt *sql.Stmt
|
var group_entry_exists_stmt *sql.Stmt
|
||||||
var get_topic_replies_offset_stmt *sql.Stmt
|
|
||||||
var get_forum_topics_offset_stmt *sql.Stmt
|
var get_forum_topics_offset_stmt *sql.Stmt
|
||||||
|
var get_topic_replies_offset_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_by_reply_stmt *sql.Stmt
|
var get_topic_by_reply_stmt *sql.Stmt
|
||||||
|
@ -310,14 +310,14 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing get_topic_replies_offset statement.")
|
log.Print("Preparing get_forum_topics_offset statement.")
|
||||||
get_topic_replies_offset_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ? LIMIT ?,?")
|
get_forum_topics_offset_stmt, err = db.Prepare("SELECT `tid`,`title`,`content`,`createdBy`,`is_closed`,`sticky`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`parentID`,`postCount`,`likeCount` FROM `topics` WHERE `parentID` = ? ORDER BY sticky DESC,lastReplyAt DESC,createdBy DESC LIMIT ?,?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing get_forum_topics_offset statement.")
|
log.Print("Preparing get_topic_replies_offset statement.")
|
||||||
get_forum_topics_offset_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`topics`.`postCount`,`topics`.`likeCount`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC LIMIT ?,?")
|
get_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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ func _gen_mysql() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing create_topic statement.")
|
log.Print("Preparing create_topic statement.")
|
||||||
create_topic_stmt, err = db.Prepare("INSERT INTO `topics`(`parentID`,`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`ipaddress`,`words`,`createdBy`) VALUES (?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?)")
|
create_topic_stmt, err = db.Prepare("INSERT INTO `topics`(`parentID`,`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`ipaddress`,`words`,`createdBy`) VALUES (?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?)")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ func _gen_mysql() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing add_replies_to_topic statement.")
|
log.Print("Preparing add_replies_to_topic statement.")
|
||||||
add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?")
|
add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyBy` = ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func _gen_pgsql() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing add_replies_to_topic statement.")
|
log.Print("Preparing add_replies_to_topic statement.")
|
||||||
add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyAt` = UTC_TIMESTAMP() WHERE `tid` = ?")
|
add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyBy` = ?,`lastReplyAt` = LOCALTIMESTAMP() WHERE `tid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ func _gen_pgsql() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing update_forum_cache statement.")
|
log.Print("Preparing update_forum_cache statement.")
|
||||||
update_forum_cache_stmt, err = db.Prepare("UPDATE `forums` SET `lastTopic` = ?,`lastTopicID` = ?,`lastReplyer` = ?,`lastReplyerID` = ?,`lastTopicTime` = UTC_TIMESTAMP() WHERE `fid` = ?")
|
update_forum_cache_stmt, err = db.Prepare("UPDATE `forums` SET `lastTopic` = ?,`lastTopicID` = ?,`lastReplyer` = ?,`lastReplyerID` = ?,`lastTopicTime` = LOCALTIMESTAMP() WHERE `fid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,9 +62,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
||||||
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
||||||
}
|
}
|
||||||
//fmt.Println("prefix:",prefix)
|
|
||||||
//fmt.Println("req.URL.Path:",req.URL.Path)
|
if dev.SuperDebug {
|
||||||
//fmt.Println("extra_data:",extra_data)
|
fmt.Println("before route_static")
|
||||||
|
fmt.Println("prefix:",prefix)
|
||||||
|
fmt.Println("req.URL.Path:",req.URL.Path)
|
||||||
|
fmt.Println("extra_data:",extra_data)
|
||||||
|
}
|
||||||
|
|
||||||
if prefix == "/static" {
|
if prefix == "/static" {
|
||||||
req.URL.Path += extra_data
|
req.URL.Path += extra_data
|
||||||
|
@ -207,6 +211,18 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
router.UploadHandler(w,req)
|
router.UploadHandler(w,req)
|
||||||
return
|
return
|
||||||
case "":
|
case "":
|
||||||
|
// Stop the favicons, robots.txt file, etc. resolving to the topics list
|
||||||
|
// TO-DO: Add support for favicons and robots.txt files
|
||||||
|
switch(extra_data) {
|
||||||
|
case "robots.txt":
|
||||||
|
route_robots_txt(w,req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra_data != "" {
|
||||||
|
NotFound(w,req)
|
||||||
|
return
|
||||||
|
}
|
||||||
config.DefaultRoute(w,req,user)
|
config.DefaultRoute(w,req,user)
|
||||||
return
|
return
|
||||||
//default: NotFound(w,req)
|
//default: NotFound(w,req)
|
||||||
|
|
|
@ -57,16 +57,16 @@ func gloinit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.CacheTopicUser == CACHE_STATIC {
|
if config.CacheTopicUser == CACHE_STATIC {
|
||||||
users = NewMemoryUserStore(user_cache_capacity)
|
users = NewMemoryUserStore(config.UserCacheCapacity)
|
||||||
topics = NewMemoryTopicStore(topic_cache_capacity)
|
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||||
} else {
|
} else {
|
||||||
users = NewSqlUserStore()
|
users = NewSqlUserStore()
|
||||||
topics = NewSqlTopicStore()
|
topics = NewSqlTopicStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
init_static_files()
|
init_static_files()
|
||||||
external_sites["YT"] = "https://www.youtube.com/"
|
|
||||||
//log.SetOutput(os.Stdout)
|
//log.SetOutput(os.Stdout)
|
||||||
|
auth = NewDefaultAuth()
|
||||||
|
|
||||||
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
router = NewGenRouter(http.FileServer(http.Dir("./uploads")))
|
||||||
gloinited = true
|
gloinited = true
|
||||||
|
@ -82,19 +82,19 @@ func BenchmarkTopicTemplateSerial(b *testing.B) {
|
||||||
user := User{0,"bob","Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"127.0.0.1"}
|
user := User{0,"bob","Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"127.0.0.1"}
|
||||||
admin := User{1,"admin-alice","Admin Alice","admin@localhost",0,true,true,true,true,true,false,AllPerms,make(map[string]bool),"",false,"","","","","",-1,58,"127.0.0.1"}
|
admin := User{1,"admin-alice","Admin Alice","admin@localhost",0,true,true,true,true,true,false,AllPerms,make(map[string]bool),"",false,"","","","","",-1,58,"127.0.0.1"}
|
||||||
|
|
||||||
topic := TopicUser{Title: "Lol",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}
|
topic := TopicUser{Title: "Lol",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,CreatedByName:"Admin",ClassName: "",Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"}
|
||||||
|
|
||||||
var replyList []Reply
|
var replyList []Reply
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry","Jerry",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry","Jerry",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry2","Jerry2",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry2","Jerry2",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry3","Jerry3",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry3","Jerry3",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry4","Jerry4",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry4","Jerry4",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry5","Jerry5",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry5","Jerry5",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry6","Jerry6",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry6","Jerry6",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry7","Jerry7",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry7","Jerry7",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry8","Jerry8",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry8","Jerry8",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry9","Jerry9",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry9","Jerry9",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry10","Jerry10",config.DefaultGroup,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
|
replyList = append(replyList, Reply{0,0,"Hey everyone!","Hey everyone!",0,"jerry10","Jerry10",config.DefaultGroup,"",0,0,"","",0,"","","","",0,"127.0.0.1",false,1,"",""})
|
||||||
|
|
||||||
headerVars := HeaderVars{
|
headerVars := HeaderVars{
|
||||||
NoticeList:[]string{"test"},
|
NoticeList:[]string{"test"},
|
||||||
|
@ -103,6 +103,7 @@ func BenchmarkTopicTemplateSerial(b *testing.B) {
|
||||||
Widgets:PageWidgets{
|
Widgets:PageWidgets{
|
||||||
LeftSidebar: template.HTML("lalala"),
|
LeftSidebar: template.HTML("lalala"),
|
||||||
},
|
},
|
||||||
|
Site:site,
|
||||||
}
|
}
|
||||||
|
|
||||||
tpage := TopicPage{"Topic Blah",user,headerVars,replyList,topic,1,1,extData}
|
tpage := TopicPage{"Topic Blah",user,headerVars,replyList,topic,1,1,extData}
|
||||||
|
@ -167,20 +168,20 @@ func BenchmarkTopicTemplateSerial(b *testing.B) {
|
||||||
func BenchmarkTopicsTemplateSerial(b *testing.B) {
|
func BenchmarkTopicsTemplateSerial(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
user := User{0,"bob","Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"127.0.0.1"}
|
user := User{0,build_profile_url("bob",0),"Bob","bob@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"127.0.0.1"}
|
||||||
admin := User{1,"admin-alice","Admin Alice","admin@localhost",0,true,true,true,true,true,false,AllPerms,make(map[string]bool),"",false,"","","","","",-1,58,"127.0.0.1"}
|
admin := User{1,build_profile_url("admin-alice",1),"Admin Alice","admin@localhost",0,true,true,true,true,true,false,AllPerms,make(map[string]bool),"",false,"","","","","Admin",58,580,"127.0.0.1"}
|
||||||
|
|
||||||
var topicList []TopicsRow
|
var topicList []*TopicsRow
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
topicList = append(topicList, TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,UserLink:build_profile_url("admin-alice",1),CreatedByName:"Admin Alice",Css: no_css_tmpl,Tag: "Admin", Level: 58, IpAddress: "127.0.0.1"})
|
topicList = append(topicList, &TopicsRow{Title: "Hey everyone!",Content: "Hey everyone!",CreatedBy: 1,CreatedAt: "0000-00-00 00:00:00",ParentID: 1,Creator:&admin,LastUser:&user,ClassName:"", IpAddress: "127.0.0.1"})
|
||||||
|
|
||||||
headerVars := HeaderVars{
|
headerVars := HeaderVars{
|
||||||
NoticeList:[]string{"test"},
|
NoticeList:[]string{"test"},
|
||||||
|
@ -189,6 +190,7 @@ func BenchmarkTopicsTemplateSerial(b *testing.B) {
|
||||||
Widgets:PageWidgets{
|
Widgets:PageWidgets{
|
||||||
LeftSidebar: template.HTML("lalala"),
|
LeftSidebar: template.HTML("lalala"),
|
||||||
},
|
},
|
||||||
|
Site:site,
|
||||||
}
|
}
|
||||||
|
|
||||||
w := ioutil.Discard
|
w := ioutil.Discard
|
||||||
|
@ -242,8 +244,7 @@ func BenchmarkStaticRouteParallel(b *testing.B) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Make these routes compatible with the changes to the router
|
// TO-DO: Swap out LocalError for a panic for this?
|
||||||
/*
|
|
||||||
func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
if !gloinited {
|
if !gloinited {
|
||||||
|
@ -253,10 +254,10 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
admin, err := users.CascadeGet(1)
|
admin, err := users.CascadeGet(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
if !admin.Is_Admin {
|
if !admin.Is_Admin {
|
||||||
panic("UID1 is not an admin")
|
b.Fatal("UID1 is not an admin")
|
||||||
}
|
}
|
||||||
admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year}
|
admin_uid_cookie := http.Cookie{Name:"uid",Value:"1",Path:"/",MaxAge: year}
|
||||||
admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year}
|
admin_session_cookie := http.Cookie{Name:"session",Value: admin.Session,Path:"/",MaxAge: year}
|
||||||
|
@ -266,11 +267,16 @@ func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
||||||
topic_req_admin := topic_req
|
topic_req_admin := topic_req
|
||||||
topic_req_admin.AddCookie(&admin_uid_cookie)
|
topic_req_admin.AddCookie(&admin_uid_cookie)
|
||||||
topic_req_admin.AddCookie(&admin_session_cookie)
|
topic_req_admin.AddCookie(&admin_session_cookie)
|
||||||
topic_handler := http.HandlerFunc(route_topic_id)
|
|
||||||
|
// Deal with the session stuff, etc.
|
||||||
|
user, ok := PreRoute(topic_w,topic_req_admin)
|
||||||
|
if !ok {
|
||||||
|
b.Fatal("Mysterious error!")
|
||||||
|
}
|
||||||
|
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
topic_w.Body.Reset()
|
topic_w.Body.Reset()
|
||||||
topic_handler.ServeHTTP(topic_w,topic_req_admin)
|
route_topic_id(topic_w,topic_req_admin,user)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -284,14 +290,15 @@ func BenchmarkTopicGuestRouteParallel(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
topic_w := httptest.NewRecorder()
|
topic_w := httptest.NewRecorder()
|
||||||
topic_req := httptest.NewRequest("get","/topic/1",bytes.NewReader(nil))
|
topic_req := httptest.NewRequest("get","/topic/1",bytes.NewReader(nil))
|
||||||
topic_handler := http.HandlerFunc(route_topic_id)
|
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
topic_w.Body.Reset()
|
topic_w.Body.Reset()
|
||||||
topic_handler.ServeHTTP(topic_w,topic_req)
|
route_topic_id(topic_w,topic_req,guest_user)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TO-DO: Make these routes compatible with the changes to the router
|
||||||
|
/*
|
||||||
func BenchmarkForumsAdminRouteParallel(b *testing.B) {
|
func BenchmarkForumsAdminRouteParallel(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
if !gloinited {
|
if !gloinited {
|
||||||
|
@ -619,7 +626,7 @@ func BenchmarkQueryTopicParallel(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
tu := TopicUser{Css: no_css_tmpl}
|
var tu TopicUser
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
err := db.QueryRow("select 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 from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).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)
|
err := db.QueryRow("select 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 from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).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)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
|
@ -640,7 +647,7 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
tu := TopicUser{Css: no_css_tmpl}
|
var tu TopicUser
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
err := get_topic_user_stmt.QueryRow(1).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)
|
err := get_topic_user_stmt.QueryRow(1).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)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
|
@ -656,7 +663,7 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) {
|
||||||
|
|
||||||
func BenchmarkQueriesSerial(b *testing.B) {
|
func BenchmarkQueriesSerial(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
tu := TopicUser{Css: no_css_tmpl}
|
var tu TopicUser
|
||||||
b.Run("topic", func(b *testing.B) {
|
b.Run("topic", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := db.QueryRow("select 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 from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).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)
|
err := db.QueryRow("select 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 from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).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)
|
||||||
|
@ -686,7 +693,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
replyItem := Reply{Css: no_css_tmpl}
|
var replyItem Reply
|
||||||
var is_super_admin bool
|
var is_super_admin bool
|
||||||
var group int
|
var group int
|
||||||
b.Run("topic_replies_scan", func(b *testing.B) {
|
b.Run("topic_replies_scan", func(b *testing.B) {
|
||||||
|
|
|
@ -160,8 +160,8 @@ config.MaxRequestSize = 5 * megabyte
|
||||||
|
|
||||||
// Caching
|
// Caching
|
||||||
config.CacheTopicUser = CACHE_STATIC
|
config.CacheTopicUser = CACHE_STATIC
|
||||||
config.UserCacheCapacity = 100 // The max number of users held in memory
|
config.UserCacheCapacity = 120 // The max number of users held in memory
|
||||||
config.TopicCacheCapacity = 100 // The max number of topics held in memory
|
config.TopicCacheCapacity = 200 // The max number of topics held in memory
|
||||||
|
|
||||||
// Email
|
// Email
|
||||||
config.SmtpServer = ""
|
config.SmtpServer = ""
|
||||||
|
|
19
main.go
19
main.go
|
@ -32,7 +32,9 @@ var startTime time.Time
|
||||||
var templates = template.New("")
|
var templates = template.New("")
|
||||||
//var no_css_tmpl template.CSS = template.CSS("")
|
//var no_css_tmpl template.CSS = template.CSS("")
|
||||||
var settings map[string]interface{} = make(map[string]interface{})
|
var settings map[string]interface{} = make(map[string]interface{})
|
||||||
var external_sites map[string]string = make(map[string]string)
|
var external_sites map[string]string = map[string]string{
|
||||||
|
"YT":"https://www.youtube.com/",
|
||||||
|
}
|
||||||
var groups []Group
|
var groups []Group
|
||||||
var groupCapCount int
|
var groupCapCount int
|
||||||
var static_files map[string]SFile = make(map[string]SFile)
|
var static_files map[string]SFile = make(map[string]SFile)
|
||||||
|
@ -48,6 +50,9 @@ var template_create_topic_handle func(CreateTopicPage,io.Writer) = nil
|
||||||
func compile_templates() error {
|
func compile_templates() error {
|
||||||
var c CTemplateSet
|
var c CTemplateSet
|
||||||
user := User{62,build_profile_url("fake-user",62),"Fake User","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"0.0.0.0.0"}
|
user := User{62,build_profile_url("fake-user",62),"Fake User","compiler@localhost",0,false,false,false,false,false,false,GuestPerms,make(map[string]bool),"",false,"","","","","",0,0,"0.0.0.0.0"}
|
||||||
|
// TO-DO: Do a more accurate level calculation for this?
|
||||||
|
user2 := User{1,build_profile_url("admin-alice",1),"Admin Alice","alice@localhost",1,true,true,true,true,false,false,AllPerms,make(map[string]bool),"",true,"","","","","",58,1000,"127.0.0.1"}
|
||||||
|
user3 := User{2,build_profile_url("admin-fred",62),"Admin Fred","fred@localhost",1,true,true,true,true,false,false,AllPerms,make(map[string]bool),"",true,"","","","","",42,900,"::1"}
|
||||||
headerVars := HeaderVars{
|
headerVars := HeaderVars{
|
||||||
Site:site,
|
Site:site,
|
||||||
NoticeList:[]string{"test"},
|
NoticeList:[]string{"test"},
|
||||||
|
@ -88,15 +93,15 @@ func compile_templates() error {
|
||||||
forums_page := ForumsPage{"Forum List",user,headerVars,forumList,extData}
|
forums_page := ForumsPage{"Forum List",user,headerVars,forumList,extData}
|
||||||
forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage",forums_page,varList)
|
forums_tmpl := c.compile_template("forums.html","templates/","ForumsPage",forums_page,varList)
|
||||||
|
|
||||||
var topicsList []TopicsRow
|
var topicsList []*TopicsRow
|
||||||
topicsList = append(topicsList,TopicsRow{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","admin-alice","Admin Alice","","",0,"","","","",58,"General","/forum/general.2"})
|
topicsList = append(topicsList,&TopicsRow{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",user3.ID,1,"","127.0.0.1",0,1,"classname","",&user2,"",0,&user3,"General","/forum/general.2"})
|
||||||
topics_page := TopicsPage{"Topic List",user,headerVars,topicsList,extData}
|
topics_page := TopicsPage{"Topic List",user,headerVars,topicsList,extData}
|
||||||
topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage",topics_page,varList)
|
topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage",topics_page,varList)
|
||||||
|
|
||||||
var topicList []TopicUser
|
//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})
|
//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_item := Forum{1,"general","General Forum","Where the general stuff happens",true,"all",0,"",0,"","",0,"",0,""}
|
||||||
forum_page := ForumPage{"General Forum",user,headerVars,topicList,forum_item,1,1,extData}
|
forum_page := ForumPage{"General Forum",user,headerVars,topicsList,forum_item,1,1,extData}
|
||||||
forum_tmpl := c.compile_template("forum.html","templates/","ForumPage",forum_page,varList)
|
forum_tmpl := c.compile_template("forum.html","templates/","ForumPage",forum_page,varList)
|
||||||
|
|
||||||
log.Print("Writing the templates")
|
log.Print("Writing the templates")
|
||||||
|
@ -191,7 +196,6 @@ func main(){
|
||||||
}
|
}
|
||||||
|
|
||||||
init_static_files()
|
init_static_files()
|
||||||
external_sites["YT"] = "https://www.youtube.com/"
|
|
||||||
|
|
||||||
log.Print("Initialising the widgets")
|
log.Print("Initialising the widgets")
|
||||||
err = init_widgets()
|
err = init_widgets()
|
||||||
|
@ -298,6 +302,7 @@ func main(){
|
||||||
// pprof.StopCPUProfile()
|
// pprof.StopCPUProfile()
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
// TO-DO: Let users run *both* HTTP and HTTPS
|
||||||
log.Print("Initialising the HTTP server")
|
log.Print("Initialising the HTTP server")
|
||||||
if !site.EnableSsl {
|
if !site.EnableSsl {
|
||||||
if site.Port == "" {
|
if site.Port == "" {
|
||||||
|
|
37
misc_test.go
37
misc_test.go
|
@ -37,7 +37,42 @@ func TestUserStore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.ID != 1 {
|
if user.ID != 1 {
|
||||||
t.Error("user.ID doesn't not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
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?
|
||||||
|
var userList map[int]*User
|
||||||
|
userList, err = users.BulkCascadeGetMap([]int{-1})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("UID #-1 shouldn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
userList, err = users.BulkCascadeGetMap([]int{0})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("UID #0 shouldn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
userList, err = users.BulkCascadeGetMap([]int{1})
|
||||||
|
if err == ErrNoRows {
|
||||||
|
t.Error("Couldn't find UID #1")
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(userList) == 0 {
|
||||||
|
t.Error("The returned map is empty for UID #0")
|
||||||
|
} else if len(userList) > 1 {
|
||||||
|
t.Error("Too many results were returned for UID #0")
|
||||||
|
}
|
||||||
|
|
||||||
|
user, ok := userList[1]
|
||||||
|
if !ok {
|
||||||
|
t.Error("We couldn't find UID #0 in the returned map")
|
||||||
|
t.Error("userList",userList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.ID != 1 {
|
||||||
|
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ CREATE TABLE `topics`(
|
||||||
`parsed_content` text not null,
|
`parsed_content` text not null,
|
||||||
`createdAt` datetime not null,
|
`createdAt` datetime not null,
|
||||||
`lastReplyAt` datetime not null,
|
`lastReplyAt` datetime not null,
|
||||||
/*`lastReplyBy` int not null,*/
|
`lastReplyBy` int not null,
|
||||||
`createdBy` int not null,
|
`createdBy` int not null,
|
||||||
`is_closed` tinyint DEFAULT 0 not null,
|
`is_closed` tinyint DEFAULT 0 not null,
|
||||||
`sticky` tinyint DEFAULT 0 not null,
|
`sticky` tinyint DEFAULT 0 not null,
|
||||||
|
@ -243,8 +243,8 @@ INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (3,1,'{}');
|
||||||
INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (4,1,'{}');
|
INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (4,1,'{}');
|
||||||
INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (5,1,'{}');
|
INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (5,1,'{}');
|
||||||
INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (6,1,'{}');
|
INSERT INTO forums_permissions(`gid`,`fid`,`permissions`) VALUES (6,1,'{}');
|
||||||
INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`createdBy`,`parentID`)
|
INSERT INTO topics(`title`,`content`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`createdBy`,`parentID`)
|
||||||
VALUES ('Test Topic','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,2);
|
VALUES ('Test Topic','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2);
|
||||||
|
|
||||||
INSERT INTO replies(`tid`,`content`,`createdAt`,`createdBy`,`lastEdit`,`lastEditBy`)
|
INSERT INTO replies(`tid`,`content`,`createdAt`,`createdBy`,`lastEdit`,`lastEditBy`)
|
||||||
VALUES (1,'Reply 1',UTC_TIMESTAMP(),1,0,0);
|
VALUES (1,'Reply 1',UTC_TIMESTAMP(),1,0,0);
|
||||||
|
|
4
pages.go
4
pages.go
|
@ -61,7 +61,7 @@ type TopicsPage struct
|
||||||
Title string
|
Title string
|
||||||
CurrentUser User
|
CurrentUser User
|
||||||
Header HeaderVars
|
Header HeaderVars
|
||||||
ItemList []TopicsRow
|
ItemList []*TopicsRow
|
||||||
ExtData ExtData
|
ExtData ExtData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ type ForumPage struct
|
||||||
Title string
|
Title string
|
||||||
CurrentUser User
|
CurrentUser User
|
||||||
Header HeaderVars
|
Header HeaderVars
|
||||||
ItemList []TopicUser
|
ItemList []*TopicsRow
|
||||||
Forum Forum
|
Forum Forum
|
||||||
Page int
|
Page int
|
||||||
LastPage int
|
LastPage int
|
||||||
|
|
|
@ -965,6 +965,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User){
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
|
// TO-DO: Add a UserStore method for iterating over global users
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
puser := User{ID: 0,}
|
puser := User{ID: 0,}
|
||||||
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.Is_Super_Admin, &puser.Avatar)
|
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.Is_Super_Admin, &puser.Avatar)
|
||||||
|
|
|
@ -55,7 +55,7 @@ type SocialGroupPage struct
|
||||||
Title string
|
Title string
|
||||||
CurrentUser User
|
CurrentUser User
|
||||||
Header HeaderVars
|
Header HeaderVars
|
||||||
ItemList []TopicUser
|
ItemList []*TopicsRow
|
||||||
Forum Forum
|
Forum Forum
|
||||||
SocialGroup SocialGroup
|
SocialGroup SocialGroup
|
||||||
Page int
|
Page int
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import "strconv"
|
import "strconv"
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
// TO-DO: Replace the soft tabs with hard ones
|
||||||
// go test -v
|
// go test -v
|
||||||
|
|
||||||
type ME_Pair struct
|
type ME_Pair struct
|
||||||
|
@ -16,6 +17,7 @@ func addMEPair(msgList []ME_Pair, msg string, expects string) []ME_Pair {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBBCodeRender(t *testing.T) {
|
func TestBBCodeRender(t *testing.T) {
|
||||||
|
//t.Skip()
|
||||||
var res string
|
var res string
|
||||||
var msgList []ME_Pair
|
var msgList []ME_Pair
|
||||||
msgList = addMEPair(msgList,"hi","hi")
|
msgList = addMEPair(msgList,"hi","hi")
|
||||||
|
@ -27,10 +29,12 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
msgList = addMEPair(msgList,"[i]hi[/i]","<i>hi</i>")
|
msgList = addMEPair(msgList,"[i]hi[/i]","<i>hi</i>")
|
||||||
msgList = addMEPair(msgList,"[s]hi[/s]","<s>hi</s>")
|
msgList = addMEPair(msgList,"[s]hi[/s]","<s>hi</s>")
|
||||||
msgList = addMEPair(msgList,"[c]hi[/c]","[c]hi[/c]")
|
msgList = addMEPair(msgList,"[c]hi[/c]","[c]hi[/c]")
|
||||||
|
if !testing.Short() {
|
||||||
msgList = addMEPair(msgList,"[b]hi[/i]","[b]hi[/i]")
|
msgList = addMEPair(msgList,"[b]hi[/i]","[b]hi[/i]")
|
||||||
msgList = addMEPair(msgList,"[/b]hi[b]","[/b]hi[b]")
|
msgList = addMEPair(msgList,"[/b]hi[b]","[/b]hi[b]")
|
||||||
msgList = addMEPair(msgList,"[/b]hi[/b]","[/b]hi[/b]")
|
msgList = addMEPair(msgList,"[/b]hi[/b]","[/b]hi[/b]")
|
||||||
msgList = addMEPair(msgList,"[b][b]hi[/b]","<b>hi</b>")
|
msgList = addMEPair(msgList,"[b][b]hi[/b]","<b>hi</b>")
|
||||||
|
}
|
||||||
msgList = addMEPair(msgList,"[b][b]hi","[b][b]hi")
|
msgList = addMEPair(msgList,"[b][b]hi","[b][b]hi")
|
||||||
msgList = addMEPair(msgList,"[b][b][b]hi","[b][b][b]hi")
|
msgList = addMEPair(msgList,"[b][b][b]hi","[b][b][b]hi")
|
||||||
msgList = addMEPair(msgList,"[/b]hi","[/b]hi")
|
msgList = addMEPair(msgList,"[/b]hi","[/b]hi")
|
||||||
|
@ -185,6 +189,7 @@ func TestBBCodeRender(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarkdownRender(t *testing.T) {
|
func TestMarkdownRender(t *testing.T) {
|
||||||
|
//t.Skip()
|
||||||
var res string
|
var res string
|
||||||
var msgList []ME_Pair
|
var msgList []ME_Pair
|
||||||
msgList = addMEPair(msgList,"hi","hi")
|
msgList = addMEPair(msgList,"hi","hi")
|
||||||
|
|
|
@ -95,7 +95,10 @@ $(document).ready(function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
if(window["WebSocket"]) {
|
if(window["WebSocket"]) {
|
||||||
conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
if(window.location.protocol == "https:")
|
||||||
|
conn = new WebSocket("wss://" + document.location.host + "/ws/");
|
||||||
|
else conn = new WebSocket("ws://" + document.location.host + "/ws/");
|
||||||
|
|
||||||
conn.onopen = function() {
|
conn.onopen = function() {
|
||||||
conn.send("page " + document.location.pathname + '\r');
|
conn.send("page " + document.location.pathname + '\r');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* WIP Under *Heavy* Construction */
|
/* WIP Under Really Heavy Construction */
|
||||||
package qgen
|
package qgen
|
||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
@ -144,7 +144,13 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string
|
||||||
querystr += "`" + item.Column + "` ="
|
querystr += "`" + item.Column + "` ="
|
||||||
for _, token := range item.Expr {
|
for _, token := range item.Expr {
|
||||||
switch(token.Type) {
|
switch(token.Type) {
|
||||||
case "function","operator","number","substitute":
|
case "function":
|
||||||
|
// TO-DO: Write a more sophisticated function parser on the utils side.
|
||||||
|
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
||||||
|
token.Contents = "LOCALTIMESTAMP()"
|
||||||
|
}
|
||||||
|
querystr += " " + token.Contents + ""
|
||||||
|
case "operator","number","substitute":
|
||||||
querystr += " " + token.Contents + ""
|
querystr += " " + token.Contents + ""
|
||||||
case "column":
|
case "column":
|
||||||
querystr += " `" + token.Contents + "`"
|
querystr += " `" + token.Contents + "`"
|
||||||
|
@ -166,8 +172,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string
|
||||||
switch(token.Type) {
|
switch(token.Type) {
|
||||||
case "function":
|
case "function":
|
||||||
// TO-DO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity?
|
// TO-DO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity?
|
||||||
// TO-DO: Change things on the MySQL and Gosora side to timestamps
|
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
||||||
if token.Contents == "UTC_TIMESTAMP()" {
|
|
||||||
token.Contents = "LOCALTIMESTAMP()"
|
token.Contents = "LOCALTIMESTAMP()"
|
||||||
}
|
}
|
||||||
querystr += " " + token.Contents + ""
|
querystr += " " + token.Contents + ""
|
||||||
|
|
|
@ -108,12 +108,12 @@ type DB_Adapter interface {
|
||||||
GetName() string
|
GetName() string
|
||||||
CreateTable(name string, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (string, error)
|
CreateTable(name string, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (string, error)
|
||||||
SimpleInsert(name string, table string, columns string, fields string) (string, error)
|
SimpleInsert(name string, table string, columns string, fields string) (string, error)
|
||||||
SimpleReplace(string,string,string,string) (string, error)
|
SimpleReplace(name string, table string, columns string, fields string) (string, error)
|
||||||
SimpleUpdate(string,string,string,string) (string, error)
|
SimpleUpdate(name string, table string, set string, where string) (string, error)
|
||||||
SimpleDelete(string,string,string) (string, error)
|
SimpleDelete(name string, table string, where string) (string, error)
|
||||||
Purge(string,string) (string, error)
|
Purge(name string, table string) (string, error)
|
||||||
SimpleSelect(string,string,string,string,string,string) (string, error)
|
SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error)
|
||||||
SimpleLeftJoin(string,string,string,string,string,string,string,string) (string, error)
|
SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error)
|
||||||
SimpleInnerJoin(string,string,string,string,string,string,string,string) (string, error)
|
SimpleInnerJoin(string,string,string,string,string,string,string,string) (string, error)
|
||||||
SimpleInsertSelect(string,DB_Insert,DB_Select) (string,error)
|
SimpleInsertSelect(string,DB_Insert,DB_Select) (string,error)
|
||||||
SimpleInsertLeftJoin(string,DB_Insert,DB_Join) (string,error)
|
SimpleInsertLeftJoin(string,DB_Insert,DB_Join) (string,error)
|
||||||
|
|
|
@ -223,14 +223,14 @@ func write_selects(adapter qgen.DB_Adapter) error {
|
||||||
|
|
||||||
adapter.SimpleSelect("group_entry_exists","users_groups","gid","name = ''","gid 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","?,?")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func write_left_joins(adapter qgen.DB_Adapter) error {
|
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_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_forum_topics_offset","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","?,?")
|
|
||||||
|
|
||||||
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_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_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 = ?","","")
|
||||||
|
@ -253,7 +253,7 @@ func write_inner_joins(adapter qgen.DB_Adapter) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func write_inserts(adapter qgen.DB_Adapter) error {
|
func write_inserts(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleInsert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,ipaddress,words,createdBy","?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?")
|
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_report","topics","title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class","?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report'")
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ func write_replaces(adapter qgen.DB_Adapter) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func write_updates(adapter qgen.DB_Adapter) error {
|
func write_updates(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleUpdate("add_replies_to_topic","topics","postCount = postCount + ?, lastReplyAt = UTC_TIMESTAMP()","tid = ?")
|
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("remove_replies_from_topic","topics","postCount = postCount - ?","tid = ?")
|
||||||
|
|
||||||
|
|
|
@ -140,9 +140,13 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
||||||
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
||||||
}
|
}
|
||||||
//fmt.Println("prefix:",prefix)
|
|
||||||
//fmt.Println("req.URL.Path:",req.URL.Path)
|
if dev.SuperDebug {
|
||||||
//fmt.Println("extra_data:",extra_data)
|
fmt.Println("before route_static")
|
||||||
|
fmt.Println("prefix:",prefix)
|
||||||
|
fmt.Println("req.URL.Path:",req.URL.Path)
|
||||||
|
fmt.Println("extra_data:",extra_data)
|
||||||
|
}
|
||||||
|
|
||||||
if prefix == "/static" {
|
if prefix == "/static" {
|
||||||
req.URL.Path += extra_data
|
req.URL.Path += extra_data
|
||||||
|
@ -174,6 +178,18 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
router.UploadHandler(w,req)
|
router.UploadHandler(w,req)
|
||||||
return
|
return
|
||||||
case "":
|
case "":
|
||||||
|
// Stop the favicons, robots.txt file, etc. resolving to the topics list
|
||||||
|
// TO-DO: Add support for favicons and robots.txt files
|
||||||
|
switch(extra_data) {
|
||||||
|
case "robots.txt":
|
||||||
|
route_robots_txt(w,req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra_data != "" {
|
||||||
|
NotFound(w,req)
|
||||||
|
return
|
||||||
|
}
|
||||||
config.DefaultRoute(w,req,user)
|
config.DefaultRoute(w,req,user)
|
||||||
return
|
return
|
||||||
//default: NotFound(w,req)
|
//default: NotFound(w,req)
|
||||||
|
|
117
routes.go
117
routes.go
|
@ -70,6 +70,18 @@ func route_fstatic(w http.ResponseWriter, r *http.Request){
|
||||||
http.ServeFile(w,r,r.URL.Path)
|
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
|
||||||
|
func route_robots_txt(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(`User-agent: *
|
||||||
|
Disallow: /panel/
|
||||||
|
Disallow: /topics/create/
|
||||||
|
Disallow: /user/edit/
|
||||||
|
Disallow: /accounts/
|
||||||
|
`))
|
||||||
|
}
|
||||||
|
|
||||||
func route_overview(w http.ResponseWriter, r *http.Request, user User){
|
func route_overview(w http.ResponseWriter, r *http.Request, user User){
|
||||||
headerVars, ok := SessionCheck(w,r,&user)
|
headerVars, ok := SessionCheck(w,r,&user)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -134,8 +146,9 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User){
|
||||||
}
|
}
|
||||||
qlist = qlist[0:len(qlist) - 1]
|
qlist = qlist[0:len(qlist) - 1]
|
||||||
|
|
||||||
var topicList []TopicsRow
|
var topicList []*TopicsRow
|
||||||
stmt, err := qgen.Builder.SimpleLeftJoin("topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar","topics.createdBy = users.uid","parentID IN("+qlist+")","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","")
|
//stmt, err := qgen.Builder.SimpleLeftJoin("topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, topics.postCount, topics.likeCount, users.name, users.avatar","topics.createdBy = users.uid","parentID IN("+qlist+")","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","")
|
||||||
|
stmt, err := qgen.Builder.SimpleSelect("topics","tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount","parentID IN("+qlist+")","sticky DESC, lastReplyAt DESC, createdBy DESC","")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
|
@ -146,25 +159,18 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User){
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
topicItem := TopicsRow{ID: 0}
|
var reqUserList map[int]bool = make(map[int]bool)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount, &topicItem.CreatedByName, &topicItem.Avatar)
|
topicItem := TopicsRow{ID: 0}
|
||||||
|
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
topicItem.Link = build_topic_url(name_to_slug(topicItem.Title),topicItem.ID)
|
topicItem.Link = build_topic_url(name_to_slug(topicItem.Title),topicItem.ID)
|
||||||
topicItem.UserLink = build_profile_url(name_to_slug(topicItem.CreatedByName),topicItem.CreatedBy)
|
|
||||||
|
|
||||||
if topicItem.Avatar != "" {
|
|
||||||
if topicItem.Avatar[0] == '.' {
|
|
||||||
topicItem.Avatar = "/uploads/avatar_" + strconv.Itoa(topicItem.CreatedBy) + topicItem.Avatar
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
topicItem.Avatar = strings.Replace(config.Noavatar,"{id}",strconv.Itoa(topicItem.CreatedBy),1)
|
|
||||||
}
|
|
||||||
|
|
||||||
forum := fstore.DirtyGet(topicItem.ParentID)
|
forum := fstore.DirtyGet(topicItem.ParentID)
|
||||||
if topicItem.ParentID >= 0 {
|
if topicItem.ParentID >= 0 {
|
||||||
|
@ -183,17 +189,40 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User){
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hooks["topics_trow_assign"] != nil {
|
if hooks["topics_topic_row_assign"] != nil {
|
||||||
run_vhook("topics_trow_assign", &topicItem, &forum)
|
run_vhook("topics_topic_row_assign", &topicItem, &forum)
|
||||||
}
|
}
|
||||||
topicList = append(topicList, topicItem)
|
topicList = append(topicList, &topicItem)
|
||||||
|
reqUserList[topicItem.CreatedBy] = true
|
||||||
|
reqUserList[topicItem.LastReplyBy] = true
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rows.Close()
|
|
||||||
|
// Convert the user ID map to a slice, then bulk load the users
|
||||||
|
var idSlice []int = 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?
|
||||||
|
userList, err := users.BulkCascadeGetMap(idSlice)
|
||||||
|
if err != nil {
|
||||||
|
InternalError(err,w,r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass to the add the user data
|
||||||
|
// TO-DO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||||
|
for _, topicItem := range topicList {
|
||||||
|
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||||
|
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||||
|
}
|
||||||
|
|
||||||
pi := TopicsPage{"Topic List",user,headerVars,topicList,extData}
|
pi := TopicsPage{"Topic List",user,headerVars,topicList,extData}
|
||||||
if pre_render_hooks["pre_render_topic_list"] != nil {
|
if pre_render_hooks["pre_render_topic_list"] != nil {
|
||||||
|
@ -268,27 +297,20 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
var topicList []TopicUser
|
// 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?
|
||||||
var topicItem TopicUser = TopicUser{ID: 0}
|
var topicList []*TopicsRow
|
||||||
|
var reqUserList map[int]bool = make(map[int]bool)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount, &topicItem.CreatedByName, &topicItem.Avatar)
|
var topicItem TopicsRow = TopicsRow{ID: 0}
|
||||||
|
err := rows.Scan(&topicItem.ID, &topicItem.Title, &topicItem.Content, &topicItem.CreatedBy, &topicItem.Is_Closed, &topicItem.Sticky, &topicItem.CreatedAt, &topicItem.LastReplyAt, &topicItem.LastReplyBy, &topicItem.ParentID, &topicItem.PostCount, &topicItem.LikeCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
topicItem.Link = build_topic_url(name_to_slug(topicItem.Title),topicItem.ID)
|
topicItem.Link = build_topic_url(name_to_slug(topicItem.Title),topicItem.ID)
|
||||||
topicItem.UserLink = build_profile_url(name_to_slug(topicItem.CreatedByName),topicItem.CreatedBy)
|
|
||||||
|
|
||||||
if topicItem.Avatar != "" {
|
|
||||||
if topicItem.Avatar[0] == '.' {
|
|
||||||
topicItem.Avatar = "/uploads/avatar_" + strconv.Itoa(topicItem.CreatedBy) + topicItem.Avatar
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
topicItem.Avatar = strings.Replace(config.Noavatar,"{id}",strconv.Itoa(topicItem.CreatedBy),1)
|
|
||||||
}
|
|
||||||
|
|
||||||
topicItem.LastReplyAt, err = relative_time(topicItem.LastReplyAt)
|
topicItem.LastReplyAt, err = relative_time(topicItem.LastReplyAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
|
@ -297,14 +319,37 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
if hooks["forum_trow_assign"] != nil {
|
if hooks["forum_trow_assign"] != nil {
|
||||||
run_vhook("forum_trow_assign", &topicItem, &forum)
|
run_vhook("forum_trow_assign", &topicItem, &forum)
|
||||||
}
|
}
|
||||||
topicList = append(topicList, topicItem)
|
topicList = append(topicList, &topicItem)
|
||||||
|
reqUserList[topicItem.CreatedBy] = true
|
||||||
|
reqUserList[topicItem.LastReplyBy] = true
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rows.Close()
|
|
||||||
|
// Convert the user ID map to a slice, then bulk load the users
|
||||||
|
var idSlice []int = 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?
|
||||||
|
userList, err := users.BulkCascadeGetMap(idSlice)
|
||||||
|
if err != nil {
|
||||||
|
InternalError(err,w,r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second pass to the add the user data
|
||||||
|
// TO-DO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||||
|
for _, topicItem := range topicList {
|
||||||
|
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||||
|
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||||
|
}
|
||||||
|
|
||||||
pi := ForumPage{forum.Name,user,headerVars,topicList,*forum,page,last_page,extData}
|
pi := ForumPage{forum.Name,user,headerVars,topicList,*forum,page,last_page,extData}
|
||||||
if pre_render_hooks["pre_render_view_forum"] != nil {
|
if pre_render_hooks["pre_render_view_forum"] != nil {
|
||||||
|
@ -482,6 +527,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request, user User){
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
replyItem := Reply{ClassName:""}
|
replyItem := Reply{ClassName:""}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
@ -561,7 +607,6 @@ func route_topic_id(w http.ResponseWriter, r *http.Request, user User){
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rows.Close()
|
|
||||||
|
|
||||||
tpage := TopicPage{topic.Title,user,headerVars,replyList,topic,page,last_page,extData}
|
tpage := TopicPage{topic.Title,user,headerVars,replyList,topic,page,last_page,extData}
|
||||||
if pre_render_hooks["pre_render_view_topic"] != nil {
|
if pre_render_hooks["pre_render_view_topic"] != nil {
|
||||||
|
@ -802,7 +847,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
}
|
}
|
||||||
|
|
||||||
wcount := word_count(content)
|
wcount := word_count(content)
|
||||||
res, err := create_topic_stmt.Exec(fid,topic_name,content,parse_message(content),ipaddress,wcount,user.ID)
|
res, err := create_topic_stmt.Exec(fid,topic_name,content,parse_message(content),user.ID,ipaddress,wcount,user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
|
@ -882,7 +927,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = add_replies_to_topic_stmt.Exec(1,tid)
|
_, err = add_replies_to_topic_stmt.Exec(1,user.ID,tid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err,w,r)
|
InternalError(err,w,r)
|
||||||
return
|
return
|
||||||
|
@ -1922,6 +1967,7 @@ func route_api(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
InternalErrorJS(err,w,r)
|
InternalErrorJS(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err = rows.Scan(&asid,&actor_id,&targetUser_id,&event,&elementType,&elementID)
|
err = rows.Scan(&asid,&actor_id,&targetUser_id,&event,&elementType,&elementID)
|
||||||
|
@ -1942,7 +1988,6 @@ func route_api(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
InternalErrorJS(err,w,r)
|
InternalErrorJS(err,w,r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rows.Close()
|
|
||||||
|
|
||||||
if len(msglist) != 0 {
|
if len(msglist) != 0 {
|
||||||
msglist = msglist[0:len(msglist)-1]
|
msglist = msglist[0:len(msglist)-1]
|
||||||
|
|
|
@ -111,9 +111,9 @@ w.Write(forum_19)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Write(forum_20)
|
w.Write(forum_20)
|
||||||
if item.Avatar != "" {
|
if item.Creator.Avatar != "" {
|
||||||
w.Write(forum_21)
|
w.Write(forum_21)
|
||||||
w.Write([]byte(item.Avatar))
|
w.Write([]byte(item.Creator.Avatar))
|
||||||
w.Write(forum_22)
|
w.Write(forum_22)
|
||||||
}
|
}
|
||||||
w.Write(forum_23)
|
w.Write(forum_23)
|
||||||
|
@ -125,25 +125,40 @@ w.Write([]byte(item.Link))
|
||||||
w.Write(forum_26)
|
w.Write(forum_26)
|
||||||
w.Write([]byte(item.Title))
|
w.Write([]byte(item.Title))
|
||||||
w.Write(forum_27)
|
w.Write(forum_27)
|
||||||
w.Write([]byte(item.UserLink))
|
w.Write([]byte(item.Creator.Link))
|
||||||
w.Write(forum_28)
|
w.Write(forum_28)
|
||||||
w.Write([]byte(item.CreatedByName))
|
w.Write([]byte(item.Creator.Name))
|
||||||
w.Write(forum_29)
|
w.Write(forum_29)
|
||||||
if item.Is_Closed {
|
if item.Is_Closed {
|
||||||
w.Write(forum_30)
|
w.Write(forum_30)
|
||||||
}
|
}
|
||||||
|
if item.Sticky {
|
||||||
w.Write(forum_31)
|
w.Write(forum_31)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
w.Write(forum_32)
|
w.Write(forum_32)
|
||||||
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
if item.LastUser.Avatar != "" {
|
||||||
w.Write(forum_33)
|
w.Write(forum_33)
|
||||||
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
w.Write([]byte(item.LastUser.Avatar))
|
||||||
w.Write(forum_34)
|
w.Write(forum_34)
|
||||||
}
|
}
|
||||||
w.Write(forum_35)
|
w.Write(forum_35)
|
||||||
}
|
w.Write([]byte(item.LastUser.Link))
|
||||||
w.Write(forum_36)
|
w.Write(forum_36)
|
||||||
|
w.Write([]byte(item.LastUser.Name))
|
||||||
|
w.Write(forum_37)
|
||||||
|
w.Write([]byte(item.LastReplyAt))
|
||||||
|
w.Write(forum_38)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Write(forum_39)
|
||||||
|
if tmpl_forum_vars.CurrentUser.Perms.CreateTopic {
|
||||||
|
w.Write(forum_40)
|
||||||
|
w.Write([]byte(strconv.Itoa(tmpl_forum_vars.Forum.ID)))
|
||||||
|
w.Write(forum_41)
|
||||||
|
}
|
||||||
|
w.Write(forum_42)
|
||||||
|
}
|
||||||
|
w.Write(forum_43)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
||||||
w.Write(footer_1)
|
w.Write(footer_1)
|
||||||
|
|
|
@ -77,7 +77,7 @@ var header_15 []byte = []byte(`</div>`)
|
||||||
var topic_0 []byte = []byte(`
|
var topic_0 []byte = []byte(`
|
||||||
|
|
||||||
<form id="edit_topic_form" action='/topic/edit/submit/`)
|
<form id="edit_topic_form" action='/topic/edit/submit/`)
|
||||||
var topic_1 []byte = []byte(`' method="post" />
|
var topic_1 []byte = []byte(`' method="post"></form>
|
||||||
`)
|
`)
|
||||||
var topic_2 []byte = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" href="/topic/`)
|
var topic_2 []byte = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" href="/topic/`)
|
||||||
var topic_3 []byte = []byte(`?page=`)
|
var topic_3 []byte = []byte(`?page=`)
|
||||||
|
@ -558,14 +558,14 @@ var topics_0 []byte = []byte(`
|
||||||
</div>
|
</div>
|
||||||
<div id="topic_list" class="rowblock topic_list">
|
<div id="topic_list" class="rowblock topic_list">
|
||||||
`)
|
`)
|
||||||
var topics_1 []byte = []byte(`<div class="rowitem passive datarow `)
|
var topics_1 []byte = []byte(`<div class="rowitem topic_left passive datarow `)
|
||||||
var topics_2 []byte = []byte(`topic_sticky`)
|
var topics_2 []byte = []byte(`topic_sticky`)
|
||||||
var topics_3 []byte = []byte(`topic_closed`)
|
var topics_3 []byte = []byte(`topic_closed`)
|
||||||
var topics_4 []byte = []byte(`" style="`)
|
var topics_4 []byte = []byte(`" style="`)
|
||||||
var topics_5 []byte = []byte(`background-image: url(`)
|
var topics_5 []byte = []byte(`background-image: url(`)
|
||||||
var topics_6 []byte = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
var topics_6 []byte = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||||
var topics_7 []byte = []byte(`">
|
var topics_7 []byte = []byte(`">
|
||||||
<span class="rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">`)
|
<span class="replyCount">`)
|
||||||
var topics_8 []byte = []byte(` replies</span><br />
|
var topics_8 []byte = []byte(` replies</span><br />
|
||||||
<span class="lastReplyAt">`)
|
<span class="lastReplyAt">`)
|
||||||
|
@ -583,15 +583,28 @@ var topics_15 []byte = []byte(`
|
||||||
var topics_16 []byte = []byte(`">Starter: `)
|
var topics_16 []byte = []byte(`">Starter: `)
|
||||||
var topics_17 []byte = []byte(`</a>
|
var topics_17 []byte = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var topics_18 []byte = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎`)
|
var topics_18 []byte = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||||
var topics_19 []byte = []byte(`</span>
|
var topics_19 []byte = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||||
|
var topics_20 []byte = []byte(`
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rowitem topic_right passive datarow" style="`)
|
||||||
|
var topics_21 []byte = []byte(`background-image: url(`)
|
||||||
|
var topics_22 []byte = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||||
|
var topics_23 []byte = []byte(`">
|
||||||
|
<span>
|
||||||
|
<a href="`)
|
||||||
|
var topics_24 []byte = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||||
|
var topics_25 []byte = []byte(`</a><br>
|
||||||
|
<span class="rowsmall lastReplyAt">Last: `)
|
||||||
|
var topics_26 []byte = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var topics_20 []byte = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
var topics_27 []byte = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||||
var topics_21 []byte = []byte(` <a href="/topics/create/">Start one?</a>`)
|
var topics_28 []byte = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||||
var topics_22 []byte = []byte(`</div>`)
|
var topics_29 []byte = []byte(`</div>`)
|
||||||
var topics_23 []byte = []byte(`
|
var topics_30 []byte = []byte(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var forum_0 []byte = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" href="/forum/`)
|
var forum_0 []byte = []byte(`<div id="prevFloat" class="prev_button"><a class="prev_link" href="/forum/`)
|
||||||
|
@ -624,14 +637,14 @@ var forum_16 []byte = []byte(`
|
||||||
</div>
|
</div>
|
||||||
<div id="forum_topic_list" class="rowblock topic_list">
|
<div id="forum_topic_list" class="rowblock topic_list">
|
||||||
`)
|
`)
|
||||||
var forum_17 []byte = []byte(`<div class="rowitem passive datarow `)
|
var forum_17 []byte = []byte(`<div class="rowitem topic_left passive datarow `)
|
||||||
var forum_18 []byte = []byte(`topic_sticky`)
|
var forum_18 []byte = []byte(`topic_sticky`)
|
||||||
var forum_19 []byte = []byte(`topic_closed`)
|
var forum_19 []byte = []byte(`topic_closed`)
|
||||||
var forum_20 []byte = []byte(`" style="`)
|
var forum_20 []byte = []byte(`" style="`)
|
||||||
var forum_21 []byte = []byte(`background-image: url(`)
|
var forum_21 []byte = []byte(`background-image: url(`)
|
||||||
var forum_22 []byte = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
var forum_22 []byte = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||||
var forum_23 []byte = []byte(`">
|
var forum_23 []byte = []byte(`">
|
||||||
<span class="rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">`)
|
<span class="replyCount">`)
|
||||||
var forum_24 []byte = []byte(` replies</span><br />
|
var forum_24 []byte = []byte(` replies</span><br />
|
||||||
<span class="lastReplyAt">`)
|
<span class="lastReplyAt">`)
|
||||||
|
@ -645,15 +658,28 @@ var forum_27 []byte = []byte(`</a>
|
||||||
var forum_28 []byte = []byte(`">Starter: `)
|
var forum_28 []byte = []byte(`">Starter: `)
|
||||||
var forum_29 []byte = []byte(`</a>
|
var forum_29 []byte = []byte(`</a>
|
||||||
`)
|
`)
|
||||||
var forum_30 []byte = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎`)
|
var forum_30 []byte = []byte(`<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>`)
|
||||||
var forum_31 []byte = []byte(`</span>
|
var forum_31 []byte = []byte(`<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>`)
|
||||||
|
var forum_32 []byte = []byte(`
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rowitem topic_right passive datarow" style="`)
|
||||||
|
var forum_33 []byte = []byte(`background-image: url(`)
|
||||||
|
var forum_34 []byte = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||||
|
var forum_35 []byte = []byte(`">
|
||||||
|
<span>
|
||||||
|
<a href="`)
|
||||||
|
var forum_36 []byte = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||||
|
var forum_37 []byte = []byte(`</a><br>
|
||||||
|
<span class="rowsmall lastReplyAt">Last: `)
|
||||||
|
var forum_38 []byte = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var forum_32 []byte = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
var forum_39 []byte = []byte(`<div class="rowitem passive">There aren't any topics in this forum yet.`)
|
||||||
var forum_33 []byte = []byte(` <a href="/topics/create/`)
|
var forum_40 []byte = []byte(` <a href="/topics/create/`)
|
||||||
var forum_34 []byte = []byte(`">Start one?</a>`)
|
var forum_41 []byte = []byte(`">Start one?</a>`)
|
||||||
var forum_35 []byte = []byte(`</div>`)
|
var forum_42 []byte = []byte(`</div>`)
|
||||||
var forum_36 []byte = []byte(`
|
var forum_43 []byte = []byte(`
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
|
|
|
@ -76,9 +76,9 @@ w.Write(topics_3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.Write(topics_4)
|
w.Write(topics_4)
|
||||||
if item.Avatar != "" {
|
if item.Creator.Avatar != "" {
|
||||||
w.Write(topics_5)
|
w.Write(topics_5)
|
||||||
w.Write([]byte(item.Avatar))
|
w.Write([]byte(item.Creator.Avatar))
|
||||||
w.Write(topics_6)
|
w.Write(topics_6)
|
||||||
}
|
}
|
||||||
w.Write(topics_7)
|
w.Write(topics_7)
|
||||||
|
@ -98,23 +98,38 @@ w.Write([]byte(item.ForumName))
|
||||||
w.Write(topics_14)
|
w.Write(topics_14)
|
||||||
}
|
}
|
||||||
w.Write(topics_15)
|
w.Write(topics_15)
|
||||||
w.Write([]byte(item.UserLink))
|
w.Write([]byte(item.Creator.Link))
|
||||||
w.Write(topics_16)
|
w.Write(topics_16)
|
||||||
w.Write([]byte(item.CreatedByName))
|
w.Write([]byte(item.Creator.Name))
|
||||||
w.Write(topics_17)
|
w.Write(topics_17)
|
||||||
if item.Is_Closed {
|
if item.Is_Closed {
|
||||||
w.Write(topics_18)
|
w.Write(topics_18)
|
||||||
}
|
}
|
||||||
|
if item.Sticky {
|
||||||
w.Write(topics_19)
|
w.Write(topics_19)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
w.Write(topics_20)
|
w.Write(topics_20)
|
||||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
if item.LastUser.Avatar != "" {
|
||||||
w.Write(topics_21)
|
w.Write(topics_21)
|
||||||
}
|
w.Write([]byte(item.LastUser.Avatar))
|
||||||
w.Write(topics_22)
|
w.Write(topics_22)
|
||||||
}
|
}
|
||||||
w.Write(topics_23)
|
w.Write(topics_23)
|
||||||
|
w.Write([]byte(item.LastUser.Link))
|
||||||
|
w.Write(topics_24)
|
||||||
|
w.Write([]byte(item.LastUser.Name))
|
||||||
|
w.Write(topics_25)
|
||||||
|
w.Write([]byte(item.LastReplyAt))
|
||||||
|
w.Write(topics_26)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w.Write(topics_27)
|
||||||
|
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||||
|
w.Write(topics_28)
|
||||||
|
}
|
||||||
|
w.Write(topics_29)
|
||||||
|
}
|
||||||
|
w.Write(topics_30)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
||||||
w.Write(footer_1)
|
w.Write(footer_1)
|
||||||
|
|
25
templates.go
25
templates.go
|
@ -13,6 +13,7 @@ import (
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TO-DO: Turn this file into a library
|
||||||
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
|
var text_overlap_list map[string]int
|
||||||
|
@ -58,6 +59,10 @@ type CTemplateSet struct
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) compile_template(name string, dir string, expects string, expectsInt interface{}, varList map[string]VarItem) (out string) {
|
func (c *CTemplateSet) compile_template(name string, dir string, expects string, expectsInt interface{}, varList map[string]VarItem) (out string) {
|
||||||
|
if dev.DebugMode {
|
||||||
|
fmt.Println("Compiling template '" + name + "'")
|
||||||
|
}
|
||||||
|
|
||||||
c.dir = dir
|
c.dir = dir
|
||||||
c.doImports = true
|
c.doImports = true
|
||||||
c.funcMap = map[string]interface{}{
|
c.funcMap = map[string]interface{}{
|
||||||
|
@ -354,9 +359,14 @@ func (c *CTemplateSet) compile_subswitch(varholder string, holdreflect reflect.V
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !cur.IsValid() {
|
||||||
|
panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
|
}
|
||||||
|
|
||||||
cur = cur.FieldByName(id)
|
cur = cur.FieldByName(id)
|
||||||
if cur.Kind() == reflect.Interface {
|
if cur.Kind() == reflect.Interface {
|
||||||
cur = cur.Elem()
|
cur = cur.Elem()
|
||||||
|
// TO-DO: Surely, there's a better way of detecting this?
|
||||||
/*if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
/*if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
||||||
varbit = "string(" + varbit + "." + id + ")"*/
|
varbit = "string(" + varbit + "." + id + ")"*/
|
||||||
//if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
//if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
||||||
|
@ -800,6 +810,21 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ
|
||||||
continue
|
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
|
||||||
|
if cur.Kind() == reflect.Ptr {
|
||||||
|
if dev.SuperDebug {
|
||||||
|
fmt.Println("Looping over pointer")
|
||||||
|
}
|
||||||
|
for cur.Kind() == reflect.Ptr {
|
||||||
|
cur = cur.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if dev.SuperDebug {
|
||||||
|
fmt.Println("Data Kind:",cur.Kind().String())
|
||||||
|
fmt.Println("Field Bit:",bit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cur = cur.FieldByName(bit)
|
cur = cur.FieldByName(bit)
|
||||||
if cur.Kind() == reflect.Interface {
|
if cur.Kind() == reflect.Interface {
|
||||||
cur = cur.Elem()
|
cur = cur.Elem()
|
||||||
|
|
|
@ -15,15 +15,23 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div id="forum_topic_list" class="rowblock topic_list">
|
<div id="forum_topic_list" class="rowblock topic_list">
|
||||||
{{range .ItemList}}<div class="rowitem passive datarow {{if .Sticky}}topic_sticky{{else if .Is_Closed}}topic_closed{{end}}" style="{{if .Avatar}}background-image: url({{.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
{{range .ItemList}}<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .Is_Closed}}topic_closed{{end}}" style="{{if .Creator.Avatar}}background-image: url({{.Creator.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||||
<span class="rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||||
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
||||||
<br /><a class="rowsmall" href="{{.UserLink}}">Starter: {{.CreatedByName}}</a>
|
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||||
{{if .Is_Closed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎{{end}}</span>
|
{{/** TO-DO: Avoid the double '|' when both .Is_Closed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||||
|
{{if .Is_Closed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||||
|
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rowitem topic_right passive datarow" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||||
|
<span>
|
||||||
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||||
|
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
{{else}}<div class="rowitem passive">There aren't any topics in this forum yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<div class="colstack_right">
|
<div id="panel_dashboard_right" class="colstack_right">
|
||||||
|
<div class="colstack_item colstack_head">
|
||||||
|
<div class="rowitem"><a>Dashboard</a></div>
|
||||||
|
</div>
|
||||||
<div id="panel_dashboard" class="colstack_grid">
|
<div id="panel_dashboard" class="colstack_grid">
|
||||||
{{range .GridItems}}
|
{{range .GridItems}}
|
||||||
<div id="{{.ID}}" class="grid_item {{.Class}}" title="{{.Note}}" style="{{if .TextColour}}color: {{.TextColour}};{{end}}
|
<div id="{{.ID}}" class="grid_item {{.Class}}" title="{{.Note}}" style="{{if .TextColour}}color: {{.TextColour}};{{end}}
|
||||||
|
|
|
@ -19,17 +19,23 @@
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="forum_topic_list" class="rowblock topic_list" style="position: relative;z-index: 50;">
|
<div id="forum_topic_list" class="rowblock topic_list" style="position: relative;z-index: 50;">
|
||||||
{{range .ItemList}}<div class="rowitem passive datarow" style="{{if .Avatar}}background-image: url({{.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}{{if .Sticky}}background-color: #FFFFCC;{{else if .Is_Closed}}background-color: #eaeaea;{{end}}">
|
{{range .ItemList}}<div class="rowitem topic_left passive datarow" style="{{if .Creator.Avatar}}background-image: url({{.Creator.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}{{if .Sticky}}background-color: #FFFFCC;{{else if .Is_Closed}}background-color: #eaeaea;{{end}}">
|
||||||
<span class="rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||||
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
||||||
<br /><a class="rowsmall" href="{{.UserLink}}">Starter: {{.CreatedByName}}</a>
|
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||||
{{if .Is_Closed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎{{end}}</span>
|
{{if .Is_Closed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎{{end}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="rowitem topic_right passive datarow" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||||
|
<span>
|
||||||
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||||
|
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{{else}}<div class="rowitem passive">There aren't any topics in here yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
{{else}}<div class="rowitem passive">There aren't any topics in here yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/{{.Forum.ID}}">Start one?</a>{{end}}</div>{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}' method="post" />
|
<form id="edit_topic_form" action='/topic/edit/submit/{{.Topic.ID}}' method="post"></form>
|
||||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/topic/{{.Topic.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/topic/{{.Topic.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||||
|
|
||||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/topic/{{.Topic.ID}}?page={{add .Page 1}}" />
|
{{if ne .LastPage .Page}}<link rel="prerender" href="/topic/{{.Topic.ID}}?page={{add .Page 1}}" />
|
||||||
|
|
|
@ -3,15 +3,23 @@
|
||||||
<div class="rowitem"><a>Topic List</a></div>
|
<div class="rowitem"><a>Topic List</a></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="topic_list" class="rowblock topic_list">
|
<div id="topic_list" class="rowblock topic_list">
|
||||||
{{range .ItemList}}<div class="rowitem passive datarow {{if .Sticky}}topic_sticky{{else if .Is_Closed}}topic_closed{{end}}" style="{{if .Avatar}}background-image: url({{.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
{{range .ItemList}}<div class="rowitem topic_left passive datarow {{if .Sticky}}topic_sticky{{else if .Is_Closed}}topic_closed{{end}}" style="{{if .Creator.Avatar}}background-image: url({{.Creator.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||||
<span class="rowsmall" style="float: right;">
|
<span class="topic_inner_right rowsmall" style="float: right;">
|
||||||
<span class="replyCount">{{.PostCount}} replies</span><br />
|
<span class="replyCount">{{.PostCount}} replies</span><br />
|
||||||
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
<span class="lastReplyAt">{{.LastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
||||||
<br /><a class="rowsmall" href="{{.UserLink}}">Starter: {{.CreatedByName}}</a>
|
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||||
{{if .Is_Closed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎{{end}}</span>
|
{{/** TO-DO: Avoid the double '|' when both .Is_Closed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||||
|
{{if .Is_Closed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||||
|
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="rowitem topic_right passive datarow" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||||
|
<span>
|
||||||
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||||
|
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
|
{{else}}<div class="rowitem passive">There aren't any topics yet.{{if .CurrentUser.Perms.CreateTopic}} <a href="/topics/create/">Start one?</a>{{end}}</div>{{end}}
|
||||||
|
|
|
@ -251,6 +251,7 @@ a {
|
||||||
textarea {
|
textarea {
|
||||||
background-color: #444444;
|
background-color: #444444;
|
||||||
border-color: #555555;
|
border-color: #555555;
|
||||||
|
color: #999999;
|
||||||
width: calc(100% - 15px);
|
width: calc(100% - 15px);
|
||||||
min-height: 80px;
|
min-height: 80px;
|
||||||
}
|
}
|
||||||
|
@ -412,6 +413,23 @@ input, select, textarea {
|
||||||
content: "Locked";
|
content: "Locked";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Temporary hack, so that I don't break the topic lists of the other themes */
|
||||||
|
.topic_list .topic_inner_right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.topic_list .rowitem {
|
||||||
|
float: left;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.topic_list .topic_left {
|
||||||
|
width: calc(100% - 284px);
|
||||||
|
}
|
||||||
|
.topic_list .topic_right {
|
||||||
|
height: 35px;
|
||||||
|
margin-left: 8px;
|
||||||
|
width: 108px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Profiles */
|
/* Profiles */
|
||||||
#profile_left_lane {
|
#profile_left_lane {
|
||||||
width: 220px;
|
width: 220px;
|
||||||
|
@ -453,6 +471,32 @@ input, select, textarea {
|
||||||
padding-left: 136px;
|
padding-left: 136px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.colstack_grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
margin-top: 3px;
|
||||||
|
grid-gap: 3px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid_stat, .grid_istat {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
background-color: rgb(61,61,61);
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel_dashboard_right .colstack_head .rowitem {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#panel_dashboard_right .colstack_head a {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: 935px) {
|
@media(max-width: 935px) {
|
||||||
.simple .user_tag {
|
.simple .user_tag {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -555,6 +599,21 @@ input, select, textarea {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media(max-width: 500px) {
|
||||||
|
.topic_list .topic_inner_right {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.topic_list .rowitem {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
.topic_list .topic_left {
|
||||||
|
width: calc(100% - 84px);
|
||||||
|
}
|
||||||
|
.topic_list .topic_right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: 470px) {
|
@media(max-width: 470px) {
|
||||||
.menu_create_topic, .like_count_label, .like_count {
|
.menu_create_topic, .like_count_label, .like_count {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
18
topic.go
18
topic.go
|
@ -68,24 +68,19 @@ type TopicsRow struct
|
||||||
Sticky bool
|
Sticky bool
|
||||||
CreatedAt string
|
CreatedAt string
|
||||||
LastReplyAt string
|
LastReplyAt string
|
||||||
//LastReplyBy int
|
LastReplyBy int
|
||||||
ParentID int
|
ParentID int
|
||||||
Status string // Deprecated. Marked for removal. -Is there anything we could use it for?
|
Status string // Deprecated. Marked for removal. -Is there anything we could use it for?
|
||||||
IpAddress string
|
IpAddress string
|
||||||
PostCount int
|
PostCount int
|
||||||
LikeCount int
|
LikeCount int
|
||||||
ClassName string
|
ClassName string
|
||||||
|
Data string // Used for report metadata
|
||||||
|
|
||||||
UserLink string
|
Creator *User
|
||||||
CreatedByName string
|
|
||||||
Avatar string
|
|
||||||
Css template.CSS
|
Css template.CSS
|
||||||
ContentLines int
|
ContentLines int
|
||||||
Tag string
|
LastUser *User
|
||||||
URL string
|
|
||||||
URLPrefix string
|
|
||||||
URLName string
|
|
||||||
Level int
|
|
||||||
|
|
||||||
ForumName string //TopicsRow
|
ForumName string //TopicsRow
|
||||||
ForumLink string
|
ForumLink string
|
||||||
|
@ -99,7 +94,6 @@ func get_topicuser(tid int) (TopicUser,error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TopicUser{ID:tid}, err
|
return TopicUser{ID:tid}, err
|
||||||
}
|
}
|
||||||
init_user_perms(user)
|
|
||||||
|
|
||||||
// We might be better off just passing seperate topic and user structs to the caller?
|
// We might be better off just passing seperate topic and user structs to the caller?
|
||||||
return copy_topic_to_topicuser(topic, user), nil
|
return copy_topic_to_topicuser(topic, user), nil
|
||||||
|
@ -112,9 +106,7 @@ func get_topicuser(tid int) (TopicUser,error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TopicUser{ID:tid}, err
|
return TopicUser{ID:tid}, err
|
||||||
}
|
}
|
||||||
init_user_perms(user)
|
return copy_topic_to_topicuser(topic, user), nil
|
||||||
tu := copy_topic_to_topicuser(topic, user)
|
|
||||||
return tu, nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
user.go
36
user.go
|
@ -233,6 +233,16 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pusher, ok := w.(http.Pusher)
|
||||||
|
if ok {
|
||||||
|
pusher.Push("/static/main.css", nil)
|
||||||
|
pusher.Push("/static/global.js", nil)
|
||||||
|
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||||
|
// TO-DO: Push the theme CSS files
|
||||||
|
// TO-DO: Push the theme scripts
|
||||||
|
// TO-DO: Push avatars?
|
||||||
|
}
|
||||||
|
|
||||||
return headerVars, true
|
return headerVars, true
|
||||||
}
|
}
|
||||||
func _simple_panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (success bool) {
|
func _simple_panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (success bool) {
|
||||||
|
@ -266,6 +276,16 @@ func _session_check(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pusher, ok := w.(http.Pusher)
|
||||||
|
if ok {
|
||||||
|
pusher.Push("/static/main.css", nil)
|
||||||
|
pusher.Push("/static/global.js", nil)
|
||||||
|
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||||
|
// TO-DO: Push the theme CSS files
|
||||||
|
// TO-DO: Push the theme scripts
|
||||||
|
// TO-DO: Push avatars?
|
||||||
|
}
|
||||||
|
|
||||||
return headerVars, true
|
return headerVars, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,14 +298,6 @@ func _pre_route(w http.ResponseWriter, r *http.Request) (User,bool) {
|
||||||
return *user, true
|
return *user, true
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.Is_Super_Admin {
|
|
||||||
user.Perms = AllPerms
|
|
||||||
user.PluginPerms = AllPluginPerms
|
|
||||||
} else {
|
|
||||||
user.Perms = groups[user.Group].Perms
|
|
||||||
user.PluginPerms = groups[user.Group].PluginPerms
|
|
||||||
}
|
|
||||||
|
|
||||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
PreError("Bad IP",w,r)
|
PreError("Bad IP",w,r)
|
||||||
|
@ -394,6 +406,14 @@ func decrease_post_user_stats(wcount int, uid int, topic bool, user User) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func init_user_perms(user *User) {
|
func init_user_perms(user *User) {
|
||||||
|
if user.Is_Super_Admin {
|
||||||
|
user.Perms = AllPerms
|
||||||
|
user.PluginPerms = AllPluginPerms
|
||||||
|
} else {
|
||||||
|
user.Perms = groups[user.Group].Perms
|
||||||
|
user.PluginPerms = groups[user.Group].PluginPerms
|
||||||
|
}
|
||||||
|
|
||||||
user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin
|
user.Is_Admin = user.Is_Super_Admin || groups[user.Group].Is_Admin
|
||||||
user.Is_Super_Mod = user.Is_Admin || groups[user.Group].Is_Mod
|
user.Is_Super_Mod = user.Is_Admin || groups[user.Group].Is_Mod
|
||||||
user.Is_Mod = user.Is_Super_Mod
|
user.Is_Mod = user.Is_Super_Mod
|
||||||
|
|
176
user_store.go
176
user_store.go
|
@ -1,14 +1,17 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
import "sync"
|
"fmt"
|
||||||
import "errors"
|
"log"
|
||||||
import "strings"
|
"sync"
|
||||||
import "strconv"
|
"errors"
|
||||||
import "database/sql"
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
import "./query_gen/lib"
|
"./query_gen/lib"
|
||||||
import "golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
// TO-DO: Add the watchdog goroutine
|
// TO-DO: Add the watchdog goroutine
|
||||||
var users UserStore
|
var users UserStore
|
||||||
|
@ -19,6 +22,8 @@ type UserStore interface {
|
||||||
Get(id int) (*User, error)
|
Get(id int) (*User, error)
|
||||||
GetUnsafe(id int) (*User, error)
|
GetUnsafe(id int) (*User, error)
|
||||||
CascadeGet(id int) (*User, error)
|
CascadeGet(id int) (*User, error)
|
||||||
|
//BulkCascadeGet(ids []int) ([]*User, error)
|
||||||
|
BulkCascadeGetMap(ids []int) (map[int]*User, error)
|
||||||
BypassGet(id int) (*User, error)
|
BypassGet(id int) (*User, error)
|
||||||
Set(item *User) error
|
Set(item *User) error
|
||||||
Add(item *User) error
|
Add(item *User) error
|
||||||
|
@ -112,6 +117,114 @@ func (sus *MemoryUserStore) CascadeGet(id int) (*User, error) {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WARNING: We did a little hack to make this as thin and quick as possible to reduce lock contention, use the * Cascade* methods instead for normal use
|
||||||
|
func (sus *MemoryUserStore) bulkGet(ids []int) (list []*User) {
|
||||||
|
list = make([]*User,len(ids))
|
||||||
|
sus.RLock()
|
||||||
|
for i, id := range ids {
|
||||||
|
list[i] = sus.items[id]
|
||||||
|
}
|
||||||
|
sus.RUnlock()
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// TO-DO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
|
// TO-DO: ID of 0 should always error?
|
||||||
|
func (sus *MemoryUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err error) {
|
||||||
|
var id_count int = len(ids)
|
||||||
|
list = make(map[int]*User)
|
||||||
|
if id_count == 0 {
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var still_here []int
|
||||||
|
slice_list := sus.bulkGet(ids)
|
||||||
|
for i, slice_item := range slice_list {
|
||||||
|
if slice_item != nil {
|
||||||
|
list[slice_item.ID] = slice_item
|
||||||
|
} else {
|
||||||
|
still_here = append(still_here,ids[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ids = still_here
|
||||||
|
|
||||||
|
// If every user is in the cache, then return immediately
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var qlist string
|
||||||
|
var uidList []interface{}
|
||||||
|
for _, id := range ids {
|
||||||
|
uidList = append(uidList,strconv.Itoa(id))
|
||||||
|
qlist += "?,"
|
||||||
|
}
|
||||||
|
qlist = qlist[0:len(qlist) - 1]
|
||||||
|
|
||||||
|
stmt, err := qgen.Builder.SimpleSelect("users","uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid IN("+qlist+")","","")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query(uidList...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
user := &User{Loggedin:true}
|
||||||
|
err := rows.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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise the user
|
||||||
|
if user.Avatar != "" {
|
||||||
|
if user.Avatar[0] == '.' {
|
||||||
|
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.Avatar = strings.Replace(config.Noavatar,"{id}",strconv.Itoa(user.ID),1)
|
||||||
|
}
|
||||||
|
user.Link = build_profile_url(name_to_slug(user.Name),user.ID)
|
||||||
|
user.Tag = groups[user.Group].Tag
|
||||||
|
init_user_perms(user)
|
||||||
|
|
||||||
|
// Add it to the cache...
|
||||||
|
sus.Set(user)
|
||||||
|
|
||||||
|
// Add it to the list to be returned
|
||||||
|
list[user.ID] = user
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did we miss any users?
|
||||||
|
if id_count > len(list) {
|
||||||
|
var sid_list string
|
||||||
|
for _, id := range ids {
|
||||||
|
_, ok := list[id]
|
||||||
|
if !ok {
|
||||||
|
sid_list += strconv.Itoa(id) + ","
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We probably don't need this, but it might be useful in case of bugs in BulkCascadeGetMap
|
||||||
|
if sid_list == "" {
|
||||||
|
if dev.DebugMode {
|
||||||
|
fmt.Println("This data is sampled later in the BulkCascadeGetMap function, so it might miss the cached IDs")
|
||||||
|
fmt.Println("id_count",id_count)
|
||||||
|
fmt.Println("ids",ids)
|
||||||
|
fmt.Println("list",list)
|
||||||
|
}
|
||||||
|
return list, errors.New("We weren't able to find a user, but we don't know which one")
|
||||||
|
}
|
||||||
|
sid_list = sid_list[0:len(sid_list) - 1]
|
||||||
|
|
||||||
|
return list, errors.New("Unable to find the users with the following IDs: " + sid_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (sus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
func (sus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
||||||
user := &User{ID:id,Loggedin:true}
|
user := &User{ID:id,Loggedin:true}
|
||||||
err := sus.get.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)
|
err := sus.get.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)
|
||||||
|
@ -322,6 +435,53 @@ func (sus *SqlUserStore) CascadeGet(id int) (*User, error) {
|
||||||
return &user, err
|
return &user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TO-DO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
|
func (sus *SqlUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err error) {
|
||||||
|
var qlist string
|
||||||
|
var uidList []interface{}
|
||||||
|
for _, id := range ids {
|
||||||
|
uidList = append(uidList,strconv.Itoa(id))
|
||||||
|
qlist += "?,"
|
||||||
|
}
|
||||||
|
qlist = qlist[0:len(qlist) - 1]
|
||||||
|
|
||||||
|
stmt, err := qgen.Builder.SimpleSelect("users","uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid IN("+qlist+")","","")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query(uidList...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
list = make(map[int]*User)
|
||||||
|
for rows.Next() {
|
||||||
|
user := &User{Loggedin:true}
|
||||||
|
err := rows.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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialise the user
|
||||||
|
if user.Avatar != "" {
|
||||||
|
if user.Avatar[0] == '.' {
|
||||||
|
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user.Avatar = strings.Replace(config.Noavatar,"{id}",strconv.Itoa(user.ID),1)
|
||||||
|
}
|
||||||
|
user.Link = build_profile_url(name_to_slug(user.Name),user.ID)
|
||||||
|
user.Tag = groups[user.Group].Tag
|
||||||
|
init_user_perms(user)
|
||||||
|
|
||||||
|
// Add it to the list to be returned
|
||||||
|
list[user.ID] = user
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) BypassGet(id int) (*User, error) {
|
func (sus *SqlUserStore) BypassGet(id int) (*User, error) {
|
||||||
user := User{ID:id,Loggedin:true}
|
user := User{ID:id,Loggedin:true}
|
||||||
err := sus.get.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)
|
err := sus.get.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)
|
||||||
|
|
Loading…
Reference in New Issue