Added Live Alerts for replies to posts.

Added relative times to posts.
Added push_alerts to the WebSockets Hub.
Added simple_delete, purge, and simple_inner_join to the query generator.
Query generator joiners now take not equals into account.
Other query generator stuff and query refactoring.
Fixed a weird padding bug on Tempra Conflux.
Fixed a couple of megapost_min_chars and bigpost_min_chars which I missed.
Fixed a security exploit in the login system.
This commit is contained in:
Azareal 2017-06-12 10:03:14 +01:00
parent 81af085c7a
commit 29d9d07cde
19 changed files with 928 additions and 596 deletions

View File

@ -1,8 +1,10 @@
package main package main
import "log"
import "strings" import "strings"
import "strconv" import "strconv"
import "errors" import "errors"
import "database/sql"
/* /*
"You received a friend invite from {user}" "You received a friend invite from {user}"
@ -111,3 +113,38 @@ func build_alert(event string, elementType string, actor_id int, targetUser_id i
return `{"msg":"{0} ` + start_frag + act + post_act + ` {1}` + end_frag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `","avatar":"` + actor.Avatar + `"}`, nil return `{"msg":"{0} ` + start_frag + act + post_act + ` {1}` + end_frag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `","avatar":"` + actor.Avatar + `"}`, nil
} }
func notify_watchers(asid int64) {
rows, err := get_watchers_stmt.Query(asid)
if err != nil && err != sql.ErrNoRows {
log.Fatal(err.Error())
return
}
var uid int
var uids []int
for rows.Next() {
err := rows.Scan(&uid)
if err != nil {
log.Fatal(err.Error())
return
}
uids = append(uids,uid)
}
err = rows.Err()
if err != nil {
log.Fatal(err.Error())
return
}
rows.Close()
var actor_id, targetUser_id, elementID int
var event, elementType string
err = get_activity_entry_stmt.QueryRow(asid).Scan(&actor_id, &targetUser_id, &event, &elementType, &elementID)
if err != nil && err != sql.ErrNoRows {
log.Fatal(err.Error())
return
}
_ = ws_hub.push_alerts(uids, event, elementType, actor_id, targetUser_id, elementID)
}

View File

@ -26,14 +26,19 @@ var get_user_reply_uid_stmt *sql.Stmt
var has_liked_topic_stmt *sql.Stmt var has_liked_topic_stmt *sql.Stmt
var has_liked_reply_stmt *sql.Stmt var has_liked_reply_stmt *sql.Stmt
var get_user_name_stmt *sql.Stmt var get_user_name_stmt *sql.Stmt
var get_user_rank_stmt *sql.Stmt
var get_user_active_stmt *sql.Stmt
var get_user_group_stmt *sql.Stmt
var get_emails_by_user_stmt *sql.Stmt var get_emails_by_user_stmt *sql.Stmt
var get_topic_basic_stmt *sql.Stmt var get_topic_basic_stmt *sql.Stmt
var get_activity_entry_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
var get_topic_replies_stmt *sql.Stmt var get_topic_replies_stmt *sql.Stmt
var get_forum_topics_stmt *sql.Stmt var get_forum_topics_stmt *sql.Stmt
var get_profile_replies_stmt *sql.Stmt var get_profile_replies_stmt *sql.Stmt
var get_watchers_stmt *sql.Stmt
var create_topic_stmt *sql.Stmt var create_topic_stmt *sql.Stmt
var create_report_stmt *sql.Stmt var create_report_stmt *sql.Stmt
var create_reply_stmt *sql.Stmt var create_reply_stmt *sql.Stmt
@ -42,7 +47,9 @@ var create_like_stmt *sql.Stmt
var add_activity_stmt *sql.Stmt var add_activity_stmt *sql.Stmt
var notify_one_stmt *sql.Stmt var notify_one_stmt *sql.Stmt
var register_stmt *sql.Stmt var register_stmt *sql.Stmt
var add_email_stmt *sql.Stmt
var create_profile_reply_stmt *sql.Stmt var create_profile_reply_stmt *sql.Stmt
var add_subscription_stmt *sql.Stmt
var create_forum_stmt *sql.Stmt var create_forum_stmt *sql.Stmt
var add_forum_perms_to_forum_stmt *sql.Stmt var add_forum_perms_to_forum_stmt *sql.Stmt
var add_plugin_stmt *sql.Stmt var add_plugin_stmt *sql.Stmt
@ -86,6 +93,10 @@ var update_user_stmt *sql.Stmt
var update_group_perms_stmt *sql.Stmt var update_group_perms_stmt *sql.Stmt
var update_group_rank_stmt *sql.Stmt var update_group_rank_stmt *sql.Stmt
var update_group_stmt *sql.Stmt var update_group_stmt *sql.Stmt
var delete_reply_stmt *sql.Stmt
var delete_topic_stmt *sql.Stmt
var delete_profile_reply_stmt *sql.Stmt
var delete_forum_perms_by_forum_stmt *sql.Stmt
func gen_mysql() (err error) { func gen_mysql() (err error) {
if debug { if debug {
@ -105,7 +116,7 @@ func gen_mysql() (err error) {
} }
log.Print("Preparing get_topic statement.") log.Print("Preparing get_topic statement.")
get_topic_stmt, err = db.Prepare("SELECT `title`,`content`,`createdBy`,`createdAt`,`is_closed`,`sticky`,`parentID`,`ipaddress`,`postCount`,`likeCount` FROM `topics` WHERE `tid` = ?") get_topic_stmt, err = db.Prepare("SELECT `title`,`content`,`createdBy`,`createdAt`,`is_closed`,`sticky`,`parentID`,`ipaddress`,`postCount`,`likeCount`,`data` FROM `topics` WHERE `tid` = ?")
if err != nil { if err != nil {
return err return err
} }
@ -212,6 +223,24 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing get_user_rank statement.")
get_user_rank_stmt, err = db.Prepare("SELECT `group`,`is_super_admin` FROM `users` WHERE `uid` = ?")
if err != nil {
return err
}
log.Print("Preparing get_user_active statement.")
get_user_active_stmt, err = db.Prepare("SELECT `active` FROM `users` WHERE `uid` = ?")
if err != nil {
return err
}
log.Print("Preparing get_user_group statement.")
get_user_group_stmt, err = db.Prepare("SELECT `group` FROM `users` WHERE `uid` = ?")
if err != nil {
return err
}
log.Print("Preparing get_emails_by_user statement.") log.Print("Preparing get_emails_by_user statement.")
get_emails_by_user_stmt, err = db.Prepare("SELECT `email`,`validated` FROM `emails` WHERE `uid` = ?") get_emails_by_user_stmt, err = db.Prepare("SELECT `email`,`validated` FROM `emails` WHERE `uid` = ?")
if err != nil { if err != nil {
@ -224,6 +253,12 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing get_activity_entry statement.")
get_activity_entry_stmt, err = db.Prepare("SELECT `actor`,`targetUser`,`event`,`elementType`,`elementID` FROM `activity_stream` WHERE `asid` = ?")
if err != nil {
return err
}
log.Print("Preparing get_topic_list statement.") log.Print("Preparing get_topic_list statement.")
get_topic_list_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC") get_topic_list_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
if err != nil { if err != nil {
@ -237,7 +272,7 @@ func gen_mysql() (err error) {
} }
log.Print("Preparing get_topic_by_reply statement.") log.Print("Preparing get_topic_by_reply statement.")
get_topic_by_reply_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`createdAt`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`parentID`,`topics`.`ipaddress`,`topics`.`postCount`,`topics`.`likeCount` FROM `replies` LEFT JOIN `topics` ON `replies`.`tid`=`topics`.`tid` WHERE `rid` = ?") get_topic_by_reply_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`createdAt`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`parentID`,`topics`.`ipaddress`,`topics`.`postCount`,`topics`.`likeCount`,`topics`.`data` FROM `replies` LEFT JOIN `topics` ON `replies`.`tid` = `topics`.`tid` WHERE `rid` = ?")
if err != nil { if err != nil {
return err return err
} }
@ -260,6 +295,12 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing get_watchers statement.")
get_watchers_stmt, err = db.Prepare("SELECT `activity_subscriptions`.`user` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
if err != nil {
return err
}
log.Print("Preparing 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 (?,?,?,?,NOW(),NOW(),?,?,?)") create_topic_stmt, err = db.Prepare("INSERT INTO `topics`(`parentID`,`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`ipaddress`,`words`,`createdBy`) VALUES (?,?,?,?,NOW(),NOW(),?,?,?)")
if err != nil { if err != nil {
@ -308,12 +349,24 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing add_email statement.")
add_email_stmt, err = db.Prepare("INSERT INTO `emails`(`email`,`uid`,`validated`,`token`) VALUES (?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing create_profile_reply statement.") log.Print("Preparing create_profile_reply statement.")
create_profile_reply_stmt, err = db.Prepare("INSERT INTO `users_replies`(`uid`,`content`,`parsed_content`,`createdAt`,`createdBy`) VALUES (?,?,?,NOW(),?)") create_profile_reply_stmt, err = db.Prepare("INSERT INTO `users_replies`(`uid`,`content`,`parsed_content`,`createdAt`,`createdBy`) VALUES (?,?,?,NOW(),?)")
if err != nil { if err != nil {
return err return err
} }
log.Print("Preparing add_subscription statement.")
add_subscription_stmt, err = db.Prepare("INSERT INTO `activity_subscriptions`(`user`,`targetID`,`targetType`,`level`) VALUES (?,?,?,2)")
if err != nil {
return err
}
log.Print("Preparing create_forum statement.") log.Print("Preparing create_forum statement.")
create_forum_stmt, err = db.Prepare("INSERT INTO `forums`(`name`,`desc`,`active`,`preset`) VALUES (?,?,?,?)") create_forum_stmt, err = db.Prepare("INSERT INTO `forums`(`name`,`desc`,`active`,`preset`) VALUES (?,?,?,?)")
if err != nil { if err != nil {
@ -572,5 +625,29 @@ func gen_mysql() (err error) {
return err return err
} }
log.Print("Preparing delete_reply statement.")
delete_reply_stmt, err = db.Prepare("DELETE FROM `replies` WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing delete_topic statement.")
delete_topic_stmt, err = db.Prepare("DELETE FROM `topics` WHERE `tid` = ?")
if err != nil {
return err
}
log.Print("Preparing delete_profile_reply statement.")
delete_profile_reply_stmt, err = db.Prepare("DELETE FROM `users_replies` WHERE `rid` = ?")
if err != nil {
return err
}
log.Print("Preparing delete_forum_perms_by_forum statement.")
delete_forum_perms_by_forum_stmt, err = db.Prepare("DELETE FROM `forums_permissions` WHERE `fid` = ?")
if err != nil {
return err
}
return nil return nil
} }

View File

@ -61,7 +61,7 @@ func compile_templates() {
log.Print("Compiling the templates") log.Print("Compiling the templates")
topic := TopicUser{1,"Blah","Hey there!",0,false,false,"Date","Date",0,"","127.0.0.1",0,1,"classname","",default_group,"",no_css_tmpl,0,"","","","",58,false} topic := TopicUser{1,"Blah","Hey there!",0,false,false,"Date","Date",0,"","127.0.0.1",0,1,"classname","","",default_group,"",no_css_tmpl,0,"","","","",58,false}
var replyList []Reply var replyList []Reply
replyList = append(replyList, Reply{0,0,"","Yo!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""}) replyList = append(replyList, Reply{0,0,"","Yo!",0,"",default_group,"",0,0,"",no_css_tmpl,0,"","","","",0,"127.0.0.1",false,1,"",""})
@ -90,7 +90,7 @@ func compile_templates() {
topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList) topics_tmpl := c.compile_template("topics.html","templates/","TopicsPage", topics_page, varList)
var topicList []TopicUser var topicList []TopicUser
topicList = append(topicList,TopicUser{1,"Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","Admin",default_group,"","",0,"","","","",58,false}) topicList = append(topicList,TopicUser{1,"Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","Admin",default_group,"","",0,"","","","",58,false})
forum_item := Forum{1,"General Forum","Where the general stuff happens",true,"all",0,"",0,"",0,""} forum_item := Forum{1,"General Forum","Where the general stuff happens",true,"all",0,"",0,"",0,""}
forum_page := ForumPage{"General Forum",user,noticeList,topicList,forum_item,1,1,nil} forum_page := ForumPage{"General Forum",user,noticeList,topicList,forum_item,1,1,nil}
forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList) forum_tmpl := c.compile_template("forum.html","templates/","ForumPage", forum_page, varList)

View File

@ -22,15 +22,14 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
is_js = "0" is_js = "0"
} }
var tid, fid int var tid int
tid, err = strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):]) tid, err = strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
if err != nil { if err != nil {
PreErrorJSQ("The provided TopicID is not a valid number.",w,r,is_js) PreErrorJSQ("The provided TopicID is not a valid number.",w,r,is_js)
return return
} }
var old_is_closed bool old_topic, err := topics.CascadeGet(tid)
err = db.QueryRow("select parentID, is_closed from topics where tid = ?", tid).Scan(&fid,&old_is_closed)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
PreErrorJSQ("The topic you tried to edit doesn't exist.",w,r,is_js) PreErrorJSQ("The topic you tried to edit doesn't exist.",w,r,is_js)
return return
@ -39,7 +38,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
user, ok := SimpleForumSessionCheck(w,r,fid) user, ok := SimpleForumSessionCheck(w,r,old_topic.ParentID)
if !ok { if !ok {
return return
} }
@ -65,7 +64,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
if old_is_closed != is_closed { if old_topic.Is_Closed != is_closed {
var action string var action string
if is_closed { if is_closed {
action = "lock" action = "lock"
@ -97,9 +96,12 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
} }
err = topics.Load(tid) err = topics.Load(tid)
if err != nil { if err == sql.ErrNoRows {
LocalErrorJSQ("This topic no longer exists!",w,r,user,is_js) LocalErrorJSQ("This topic no longer exists!",w,r,user,is_js)
return return
} else if err != nil {
InternalErrorJSQ(err,w,r,is_js)
return
} }
if is_js == "0" { if is_js == "0" {
@ -116,9 +118,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
var content string topic, err := topics.CascadeGet(tid)
var createdBy, fid int
err = db.QueryRow("select content, createdBy, parentID from topics where tid = ?", tid).Scan(&content, &createdBy, &fid)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
PreError("The topic you tried to delete doesn't exist.",w,r) PreError("The topic you tried to delete doesn't exist.",w,r)
return return
@ -127,7 +127,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
user, ok := SimpleForumSessionCheck(w,r,fid) user, ok := SimpleForumSessionCheck(w,r,topic.ParentID)
if !ok { if !ok {
return return
} }
@ -163,20 +163,20 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) {
//log.Print("Topic #" + strconv.Itoa(tid) + " was deleted by User #" + strconv.Itoa(user.ID)) //log.Print("Topic #" + strconv.Itoa(tid) + " was deleted by User #" + strconv.Itoa(user.ID))
http.Redirect(w,r,"/",http.StatusSeeOther) http.Redirect(w,r,"/",http.StatusSeeOther)
wcount := word_count(content) wcount := word_count(topic.Content)
err = decrease_post_user_stats(wcount,createdBy,true,user) err = decrease_post_user_stats(wcount,topic.CreatedBy,true,user)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
_, err = remove_topics_from_forum_stmt.Exec(1, fid) _, err = remove_topics_from_forum_stmt.Exec(1, topic.ParentID)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
forums[fid].TopicCount -= 1 forums[topic.ParentID].TopicCount -= 1
topics.Remove(tid) topics.Remove(tid)
} }
@ -553,7 +553,7 @@ func route_ban(w http.ResponseWriter, r *http.Request) {
} }
var uname string var uname string
err = db.QueryRow("select name from users where uid = ?", uid).Scan(&uname) err = get_user_name_stmt.QueryRow(uid).Scan(&uname)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("The user you're trying to ban no longer exists.",w,r,user) LocalError("The user you're trying to ban no longer exists.",w,r,user)
return return
@ -588,14 +588,14 @@ func route_ban_submit(w http.ResponseWriter, r *http.Request) {
LocalError("The provided User ID is not a valid number.",w,r,user) LocalError("The provided User ID is not a valid number.",w,r,user)
return return
} }
if uid == -2 { /*if uid == -2 {
LocalError("Sigh, are you really trying to ban me? Do you despise so much? Despite all of our adventures over at /arcane-tower/...?",w,r,user) LocalError("Sigh, are you really trying to ban me? Do you despise so much? Despite all of our adventures over at /arcane-tower/...?",w,r,user)
return return
} }*/
var group int var group int
var is_super_admin bool var is_super_admin bool
err = db.QueryRow("select `group`,`is_super_admin` from `users` where `uid` = ?", uid).Scan(&group, &is_super_admin) err = get_user_rank_stmt.QueryRow(uid).Scan(&group, &is_super_admin)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("The user you're trying to ban no longer exists.",w,r,user) LocalError("The user you're trying to ban no longer exists.",w,r,user)
return return
@ -663,9 +663,8 @@ func route_unban(w http.ResponseWriter, r *http.Request) {
return return
} }
var uname string
var group int var group int
err = db.QueryRow("select `name`, `group` from users where `uid` = ?", uid).Scan(&uname, &group) err = get_user_group_stmt.QueryRow(uid).Scan(&group)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("The user you're trying to unban no longer exists.",w,r,user) LocalError("The user you're trying to unban no longer exists.",w,r,user)
return return
@ -697,10 +696,14 @@ func route_unban(w http.ResponseWriter, r *http.Request) {
} }
err = users.Load(uid) err = users.Load(uid)
if err != nil { if err != nil && err == sql.ErrNoRows {
LocalError("This user no longer exists!",w,r,user) LocalError("This user no longer exists!",w,r,user)
return return
} else if err != nil {
InternalError(err,w,r)
return
} }
http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther) http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther)
} }
@ -724,9 +727,8 @@ func route_activate(w http.ResponseWriter, r *http.Request) {
return return
} }
var uname string
var active bool var active bool
err = db.QueryRow("select `name`,`active` from users where `uid` = ?", uid).Scan(&uname, &active) err = get_user_active_stmt.QueryRow(uid).Scan(&active)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("The account you're trying to activate no longer exists.",w,r,user) LocalError("The account you're trying to activate no longer exists.",w,r,user)
return return

View File

@ -16,19 +16,12 @@ var db_collation string = "utf8mb4_general_ci"
var get_topic_replies_offset_stmt *sql.Stmt // I'll need to rewrite this one to stop it hard-coding the per page setting before moving it to the query generator var get_topic_replies_offset_stmt *sql.Stmt // I'll need to rewrite this one to stop it hard-coding the per page setting before moving it to the query generator
var get_forum_topics_offset_stmt *sql.Stmt var get_forum_topics_offset_stmt *sql.Stmt
var notify_watchers_stmt *sql.Stmt var notify_watchers_stmt *sql.Stmt
var add_subscription_stmt *sql.Stmt
var delete_reply_stmt *sql.Stmt
var delete_topic_stmt *sql.Stmt
var get_activity_feed_by_watcher_stmt *sql.Stmt var get_activity_feed_by_watcher_stmt *sql.Stmt
var get_activity_count_by_watcher_stmt *sql.Stmt var get_activity_count_by_watcher_stmt *sql.Stmt
var add_email_stmt *sql.Stmt var update_email_stmt, verify_email_stmt *sql.Stmt
var update_email_stmt *sql.Stmt
var verify_email_stmt *sql.Stmt
var delete_profile_reply_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 delete_forum_perms_by_forum_stmt *sql.Stmt
var add_forum_perms_to_forum_admins_stmt *sql.Stmt var add_forum_perms_to_forum_admins_stmt *sql.Stmt
var add_forum_perms_to_forum_staff_stmt *sql.Stmt var add_forum_perms_to_forum_staff_stmt *sql.Stmt
var add_forum_perms_to_forum_members_stmt *sql.Stmt var add_forum_perms_to_forum_members_stmt *sql.Stmt
@ -86,24 +79,6 @@ func init_database() (err error) {
return err return err
} }
log.Print("Preparing add_subscription statement.")
add_subscription_stmt, err = db.Prepare("INSERT INTO activity_subscriptions(user,targetID,targetType,level) VALUES(?,?,?,2)")
if err != nil {
return err
}
log.Print("Preparing delete_reply statement.")
delete_reply_stmt, err = db.Prepare("DELETE FROM replies WHERE rid = ?")
if err != nil {
return err
}
log.Print("Preparing delete_topic statement.")
delete_topic_stmt, err = db.Prepare("DELETE FROM topics WHERE tid = ?")
if err != nil {
return err
}
log.Print("Preparing get_activity_feed_by_watcher statement.") log.Print("Preparing get_activity_feed_by_watcher statement.")
get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8") get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8")
if err != nil { if err != nil {
@ -116,12 +91,6 @@ func init_database() (err error) {
return err return err
} }
log.Print("Preparing add_email statement.")
add_email_stmt, err = db.Prepare("INSERT INTO emails(`email`,`uid`,`validated`,`token`) VALUES(?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing update_email statement.") log.Print("Preparing update_email statement.")
update_email_stmt, err = db.Prepare("UPDATE emails SET email = ?, uid = ?, validated = ?, token = ? WHERE email = ?") update_email_stmt, err = db.Prepare("UPDATE emails SET email = ?, uid = ?, validated = ?, token = ? WHERE email = ?")
if err != nil { if err != nil {
@ -134,12 +103,6 @@ func init_database() (err error) {
return err return err
} }
log.Print("Preparing delete_profile_reply statement.")
delete_profile_reply_stmt, err = db.Prepare("DELETE FROM users_replies WHERE rid = ?")
if err != nil {
return err
}
log.Print("Preparing forum_entry_exists statement.") log.Print("Preparing forum_entry_exists statement.")
forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' order by fid asc limit 1") forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' order by fid asc limit 1")
if err != nil { if err != nil {
@ -152,12 +115,6 @@ func init_database() (err error) {
return err return err
} }
log.Print("Preparing delete_forum_perms_by_forum statement.")
delete_forum_perms_by_forum_stmt, err = db.Prepare("DELETE FROM forums_permissions WHERE fid = ?")
if err != nil {
return err
}
log.Print("Preparing add_forum_perms_to_forum_admins statement.") log.Print("Preparing add_forum_perms_to_forum_admins statement.")
add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 1") add_forum_perms_to_forum_admins_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) SELECT `gid`,? AS fid,? AS preset,? AS permissions FROM users_groups WHERE is_admin = 1")
if err != nil { if err != nil {

View File

@ -32,5 +32,9 @@ func(hub *WS_Hub) push_alert(_ int, _ string, _ string, _ int, _ int, _ int) err
return ws_nouser return ws_nouser
} }
func(hub *WS_Hub) push_alerts(_ []int, _ string, _ string, _ int, _ int, _ int) error {
return ws_nouser
}
func route_websockets(_ http.ResponseWriter, _ *http.Request) { func route_websockets(_ http.ResponseWriter, _ *http.Request) {
} }

View File

@ -1030,7 +1030,6 @@ func route_panel_groups(w http.ResponseWriter, r *http.Request){
} }
can_edit = user.Perms.EditGroup && (!group.Is_Admin || user.Perms.EditGroupAdmin) && (!group.Is_Mod || user.Perms.EditGroupSuperMod) can_edit = user.Perms.EditGroup && (!group.Is_Admin || user.Perms.EditGroupAdmin) && (!group.Is_Mod || user.Perms.EditGroupSuperMod)
groupList = append(groupList, GroupAdmin{group.ID,group.Name,rank,rank_class,can_edit,can_delete}) groupList = append(groupList, GroupAdmin{group.ID,group.Name,rank,rank_class,can_edit,can_delete})
} }
//fmt.Printf("%+v\n", groupList) //fmt.Printf("%+v\n", groupList)

View File

@ -1,6 +1,7 @@
var form_vars = {}; var form_vars = {};
var alertList = []; var alertList = [];
var alertCount = 0; var alertCount = 0;
var conn;
function post_link(event) function post_link(event)
{ {
@ -345,7 +346,7 @@ $(document).ready(function(){
$(".menu_alerts").click(function(event) { $(".menu_alerts").click(function(event) {
event.stopPropagation(); event.stopPropagation();
if($(this).hasClass("selectedAlert")) return; if($(this).hasClass("selectedAlert")) return;
load_alerts($(this)); if(!conn) load_alerts($(this));
this.className += " selectedAlert"; this.className += " selectedAlert";
document.getElementById("back").className += " alertActive" document.getElementById("back").className += " alertActive"
}); });

View File

@ -1,10 +1,7 @@
/* WIP Under Construction */ /* WIP Under Construction */
package main package main
//import "fmt"
import "strings"
import "log" import "log"
import "os"
var db_registry []DB_Adapter var db_registry []DB_Adapter
var blank_order []DB_Order var blank_order []DB_Order
@ -40,6 +37,7 @@ type DB_Joiner struct
LeftColumn string LeftColumn string
RightTable string RightTable string
RightColumn string RightColumn string
Operator string
} }
type DB_Order struct type DB_Order struct
@ -63,9 +61,13 @@ type DB_Adapter interface {
simple_insert(string,string,string,string) error simple_insert(string,string,string,string) error
simple_replace(string,string,string,string) error simple_replace(string,string,string,string) error
simple_update(string,string,string,string) error simple_update(string,string,string,string) error
simple_delete(string,string,string) error
purge(string,string) error
simple_select(string,string,string,string,string/*,int,int*/) error simple_select(string,string,string,string,string/*,int,int*/) error
simple_left_join(string,string,string,string,string,string,string/*,int,int*/) error simple_left_join(string,string,string,string,string,string,string/*,int,int*/) error
simple_inner_join(string,string,string,string,string,string,string/*,int,int*/) error
write() error write() error
// TO-DO: Add a simple query builder // TO-DO: Add a simple query builder
} }
@ -83,7 +85,11 @@ func write_statements(adapter DB_Adapter) error {
if err != nil { if err != nil {
return err return err
} }
err = write_joins(adapter) err = write_left_joins(adapter)
if err != nil {
return err
}
err = write_inner_joins(adapter)
if err != nil { if err != nil {
return err return err
} }
@ -99,6 +105,10 @@ func write_statements(adapter DB_Adapter) error {
if err != nil { if err != nil {
return err return err
} }
err = write_deletes(adapter)
if err != nil {
return err
}
return nil return nil
} }
@ -108,7 +118,7 @@ func write_selects(adapter DB_Adapter) error {
adapter.simple_select("get_full_user","users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","") adapter.simple_select("get_full_user","users","name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip","uid = ?","")
adapter.simple_select("get_topic","topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount","tid = ?","") adapter.simple_select("get_topic","topics","title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data","tid = ?","")
adapter.simple_select("get_reply","replies","content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","") adapter.simple_select("get_reply","replies","content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","")
@ -145,24 +155,40 @@ func write_selects(adapter DB_Adapter) error {
adapter.simple_select("get_user_name","users","name","uid = ?","") adapter.simple_select("get_user_name","users","name","uid = ?","")
adapter.simple_select("get_user_rank","users","group, is_super_admin","uid = ?","")
adapter.simple_select("get_user_active","users","active","uid = ?","")
adapter.simple_select("get_user_group","users","group","uid = ?","")
adapter.simple_select("get_emails_by_user","emails","email, validated","uid = ?","") adapter.simple_select("get_emails_by_user","emails","email, validated","uid = ?","")
adapter.simple_select("get_topic_basic","topics","title, content","tid = ?","") adapter.simple_select("get_topic_basic","topics","title, content","tid = ?","")
adapter.simple_select("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","")
return nil return nil
} }
func write_joins(adapter DB_Adapter) error { func write_left_joins(adapter DB_Adapter) error {
adapter.simple_left_join("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.simple_left_join("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.simple_left_join("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.simple_left_join("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.simple_left_join("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount","replies.tid = topics.tid","rid = ?","") adapter.simple_left_join("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data","replies.tid = topics.tid","rid = ?","")
adapter.simple_left_join("get_topic_replies","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress","replies.createdBy = users.uid","tid = ?","") adapter.simple_left_join("get_topic_replies","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress","replies.createdBy = users.uid","tid = ?","")
adapter.simple_left_join("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc") adapter.simple_left_join("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc")
adapter.simple_left_join("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","") adapter.simple_left_join("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","")
return nil
}
func write_inner_joins(adapter DB_Adapter) error {
adapter.simple_inner_join("get_watchers","activity_stream","activity_subscriptions","activity_subscriptions.user","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","")
return nil return nil
} }
@ -185,8 +211,12 @@ func write_inserts(adapter DB_Adapter) error {
// create_account_stmt, err = db.Prepare("INSERT INTO // create_account_stmt, err = db.Prepare("INSERT INTO
adapter.simple_insert("register","users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,?,?,''") adapter.simple_insert("register","users","name, email, password, salt, group, is_super_admin, session, active, message","?,?,?,?,?,0,?,?,''")
adapter.simple_insert("add_email","emails","email, uid, validated, token","?,?,?,?")
adapter.simple_insert("create_profile_reply","users_replies","uid,content,parsed_content,createdAt,createdBy","?,?,?,NOW(),?") adapter.simple_insert("create_profile_reply","users_replies","uid,content,parsed_content,createdAt,createdBy","?,?,?,NOW(),?")
adapter.simple_insert("add_subscription","activity_subscriptions","user,targetID,targetType,level","?,?,?,2")
adapter.simple_insert("create_forum","forums","name, desc, active, preset","?,?,?,?") adapter.simple_insert("create_forum","forums","name, desc, active, preset","?,?,?,?")
adapter.simple_insert("add_forum_perms_to_forum","forums_permissions","gid,fid,preset,permissions","?,?,?,?") adapter.simple_insert("add_forum_perms_to_forum","forums_permissions","gid,fid,preset,permissions","?,?,?,?")
@ -286,335 +316,15 @@ func write_updates(adapter DB_Adapter) error {
return nil return nil
} }
func _process_columns(colstr string) (columns []DB_Column) { func write_deletes(adapter DB_Adapter) error {
if colstr == "" { adapter.simple_delete("delete_reply","replies","rid = ?")
return columns
}
colstr = strings.Replace(colstr," as "," AS ",-1)
for _, segment := range strings.Split(colstr,",") {
var outcol DB_Column
dothalves := strings.Split(strings.TrimSpace(segment),".")
var halves []string adapter.simple_delete("delete_topic","topics","tid = ?")
if len(dothalves) == 2 {
outcol.Table = dothalves[0]
halves = strings.Split(dothalves[1]," AS ")
} else {
halves = strings.Split(dothalves[0]," AS ")
}
halves[0] = strings.TrimSpace(halves[0])
if len(halves) == 2 {
outcol.Alias = strings.TrimSpace(halves[1])
}
if halves[0][len(halves[0]) - 1] == ')' {
outcol.Type = "function"
} else {
outcol.Type = "column"
}
outcol.Left = halves[0]
columns = append(columns,outcol)
}
return columns
}
func _process_orderby(orderstr string) (order []DB_Order) {
if orderstr == "" {
return order
}
for _, segment := range strings.Split(orderstr,",") {
var outorder DB_Order
halves := strings.Split(strings.TrimSpace(segment)," ")
if len(halves) != 2 {
continue
}
outorder.Column = halves[0]
outorder.Order = strings.ToLower(halves[1])
order = append(order,outorder)
}
return order
}
func _process_joiner(joinstr string) (joiner []DB_Joiner) {
if joinstr == "" {
return joiner
}
joinstr = strings.Replace(joinstr," on "," ON ",-1)
joinstr = strings.Replace(joinstr," and "," AND ",-1)
for _, segment := range strings.Split(joinstr," AND ") {
var outjoin DB_Joiner
halves := strings.Split(segment,"=")
if len(halves) != 2 {
continue
}
left_column := strings.Split(halves[0],".")
right_column := strings.Split(halves[1],".")
outjoin.LeftTable = strings.TrimSpace(left_column[0])
outjoin.RightTable = strings.TrimSpace(right_column[0])
outjoin.LeftColumn = strings.TrimSpace(left_column[1])
outjoin.RightColumn = strings.TrimSpace(right_column[1])
joiner = append(joiner,outjoin)
}
return joiner
}
// TO-DO: Add support for keywords like BETWEEN. We'll probably need an arbitrary expression parser like with the update setters.
func _process_where(wherestr string) (where []DB_Where) {
if wherestr == "" {
return where
}
wherestr = strings.Replace(wherestr," and "," AND ",-1)
for _, segment := range strings.Split(wherestr," AND ") {
// TO-DO: Subparse the contents of a function and spit out a DB_Function struct
var outwhere DB_Where
var parseOffset int
var left, right string
left, parseOffset = _get_identifier(segment, parseOffset) adapter.simple_delete("delete_profile_reply","users_replies","rid = ?")
outwhere.Operator, parseOffset = _get_operator(segment, parseOffset + 1)
right, parseOffset = _get_identifier(segment, parseOffset + 1)
outwhere.LeftType = _get_identifier_type(left)
outwhere.RightType = _get_identifier_type(right)
left_operand := strings.Split(left,".") adapter.simple_delete("delete_forum_perms_by_forum","forums_permissions","fid = ?")
right_operand := strings.Split(right,".")
if len(left_operand) == 2 { return nil
outwhere.LeftTable = strings.TrimSpace(left_operand[0])
outwhere.LeftColumn = strings.TrimSpace(left_operand[1])
} else {
outwhere.LeftColumn = strings.TrimSpace(left_operand[0])
}
if len(right_operand) == 2 {
outwhere.RightTable = strings.TrimSpace(right_operand[0])
outwhere.RightColumn = strings.TrimSpace(right_operand[1])
} else {
outwhere.RightColumn = strings.TrimSpace(right_operand[0])
}
where = append(where,outwhere)
}
return where
}
func _process_set(setstr string) (setter []DB_Setter) {
if setstr == "" {
return setter
}
//fmt.Println("setstr",setstr)
// First pass, splitting the string by commas while ignoring the innards of functions
var setset []string
var buffer string
var last_item int
setstr += ","
for i := 0; i < len(setstr); i++ {
if setstr[i] == '(' {
i = _skip_function_call(setstr,i-1)
setset = append(setset,setstr[last_item:i+1])
buffer = ""
last_item = i + 2
} else if setstr[i] == ',' && buffer != "" {
setset = append(setset,buffer)
buffer = ""
last_item = i + 1
} else if (setstr[i] > 32) && setstr[i] != ',' && setstr[i] != ')' {
buffer += string(setstr[i])
}
}
// Second pass. Break this setitem into manageable chunks
buffer = ""
for _, setitem := range setset {
var tmp_setter DB_Setter
halves := strings.Split(setitem,"=")
if len(halves) != 2 {
continue
}
tmp_setter.Column = strings.TrimSpace(halves[0])
halves[1] += ")"
var optype int // 0: None, 1: Number, 2: Column, 3: Function, 4: String, 5: Operator
//fmt.Println("halves[1]",halves[1])
for i := 0; i < len(halves[1]); i++ {
char := halves[1][i]
//fmt.Println("optype",optype)
switch(optype) {
case 0: // unknown
if ('0' <= char && char <= '9') {
optype = 1
buffer = string(char)
} else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') {
optype = 2
buffer = string(char)
} else if char == '\'' {
optype = 4
} else if _is_op_byte(char) {
optype = 5
buffer = string(char)
} else if char == '?' {
//fmt.Println("Expr:","?")
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{"?","substitute"})
}
case 1: // number
if ('0' <= char && char <= '9') {
buffer += string(char)
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"number"})
}
case 2: // column
if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') {
buffer += string(char)
} else if char == '(' {
optype = 3
i--
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"column"})
}
case 3: // function
var pre_i int = i
//fmt.Println("buffer",buffer)
//fmt.Println("len(halves)",len(halves[1]))
//fmt.Println("pre_i",string(halves[1][pre_i]))
//fmt.Println("msg prior to pre_i",halves[1][0:pre_i])
i = _skip_function_call(halves[1],i-1)
//fmt.Println("i",i)
//fmt.Println("msg prior to i-1",halves[1][0:i-1])
//fmt.Println("string(i-1)",string(halves[1][i-1]))
//fmt.Println("string(i)",string(halves[1][i]))
buffer += halves[1][pre_i:i] + string(halves[1][i])
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"function"})
optype = 0
case 4: // string
if char != '\'' {
buffer += string(char)
} else {
optype = 0
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"string"})
}
case 5: // operator
if _is_op_byte(char) {
buffer += string(char)
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"operator"})
}
}
}
setter = append(setter,tmp_setter)
}
//fmt.Println("setter",setter)
return setter
}
func _is_op_byte(char byte) bool {
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/'
}
func _process_fields(fieldstr string) (fields []DB_Field) {
if fieldstr == "" {
return fields
}
var buffer string
var last_item int
fieldstr += ","
for i := 0; i < len(fieldstr); i++ {
if fieldstr[i] == '(' {
i = _skip_function_call(fieldstr,i-1)
fields = append(fields,DB_Field{Name:fieldstr[last_item:i+1],Type:_get_identifier_type(fieldstr[last_item:i+1])})
buffer = ""
last_item = i + 2
} else if fieldstr[i] == ',' && buffer != "" {
fields = append(fields,DB_Field{Name:buffer,Type:_get_identifier_type(buffer)})
buffer = ""
last_item = i + 1
} else if (fieldstr[i] > 32) && fieldstr[i] != ',' && fieldstr[i] != ')' {
buffer += string(fieldstr[i])
}
}
return fields
}
func _get_identifier_type(identifier string) string {
if ('a' <= identifier[0] && identifier[0] <= 'z') || ('A' <= identifier[0] && identifier[0] <= 'Z') {
if identifier[len(identifier) - 1] == ')' {
return "function"
}
return "column"
}
if identifier[0] == '\'' || identifier[0] == '"' {
return "string"
}
return "literal"
}
func _get_identifier(segment string, startOffset int) (out string, i int) {
segment = strings.TrimSpace(segment)
segment += " " // Avoid overflow bugs with slicing
for i = startOffset; i < len(segment); i++ {
if segment[i] == '(' {
i = _skip_function_call(segment,i)
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
}
if segment[i] == ' ' && i != startOffset {
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
}
}
return strings.TrimSpace(segment[startOffset:]), (i - 1)
}
func _get_operator(segment string, startOffset int) (out string, i int) {
segment = strings.TrimSpace(segment)
segment += " " // Avoid overflow bugs with slicing
for i = startOffset; i < len(segment); i++ {
if segment[i] == ' ' && i != startOffset {
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
}
}
return strings.TrimSpace(segment[startOffset:]), (i - 1)
}
func _skip_function_call(data string, index int) int {
var brace_count int
for ;index < len(data); index++{
char := data[index]
if char == '(' {
brace_count++
} else if char == ')' {
brace_count--
if brace_count == 0 {
return index
}
}
}
return index
}
func write_file(name string, content string) (err error) {
f, err := os.Create(name)
if err != nil {
return err
}
_, err = f.WriteString(content)
if err != nil {
return err
}
f.Sync()
f.Close()
return
} }

View File

@ -152,6 +152,53 @@ func (adapter *Mysql_Adapter) simple_update(name string, table string, set strin
return nil return nil
} }
func (adapter *Mysql_Adapter) simple_delete(name string, table string, where string) error {
if name == "" {
return errors.New("You need a name for this statement")
}
if table == "" {
return errors.New("You need a name for this table")
}
if where == "" {
return errors.New("You need to specify what data you want to delete")
}
var querystr string = "DELETE FROM `" + table + "` WHERE"
for _, loc := range _process_where(where) {
var left, right string
if loc.LeftType == "column" {
left = "`" + loc.LeftColumn + "`"
} else {
left = loc.LeftColumn
}
if loc.RightType == "column" {
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
}
querystr += " " + left + " " + loc.Operator + " " + right + " AND "
}
querystr = querystr[0:len(querystr) - 4]
adapter.write_statement(name,strings.TrimSpace(querystr))
return nil
}
// We don't want to accidentally wipe tables, so we'll have a seperate method for purging tables instead
func (adapter *Mysql_Adapter) purge(name string, table string) error {
if name == "" {
return errors.New("You need a name for this statement")
}
if table == "" {
return errors.New("You need a name for this table")
}
adapter.write_statement(name,"DELETE FROM `" + table + "`")
return nil
}
func (adapter *Mysql_Adapter) simple_select(name string, table string, columns string, where string, orderby string/*, offset int, maxCount int*/) error { func (adapter *Mysql_Adapter) simple_select(name string, table string, columns string, where string, orderby string/*, offset int, maxCount int*/) error {
if name == "" { if name == "" {
return errors.New("You need a name for this statement") return errors.New("You need a name for this statement")
@ -253,7 +300,92 @@ func (adapter *Mysql_Adapter) simple_left_join(name string, table1 string, table
querystr += " FROM `" + table1 + "` LEFT JOIN `" + table2 + "` ON " querystr += " FROM `" + table1 + "` LEFT JOIN `" + table2 + "` ON "
for _, joiner := range _process_joiner(joiners) { for _, joiner := range _process_joiner(joiners) {
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "`=`" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND " querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND "
}
// Remove the trailing AND
querystr = querystr[0:len(querystr) - 4]
if len(where) != 0 {
querystr += " WHERE"
for _, loc := range _process_where(where) {
var left, right string
if loc.LeftTable != "" {
left = "`" + loc.LeftTable + "`.`" + loc.LeftColumn + "`"
} else if loc.LeftType == "column" {
left = "`" + loc.LeftColumn + "`"
} else {
left = loc.LeftColumn
}
if loc.RightTable != "" {
right = "`" + loc.RightTable + "`.`" + loc.RightColumn + "`"
} else if loc.RightType == "column" {
right = "`" + loc.RightColumn + "`"
} else {
right = loc.RightColumn
}
querystr += " " + left + " " + loc.Operator + " " + right + " AND "
}
querystr = querystr[0:len(querystr) - 4]
}
if len(orderby) != 0 {
querystr += " ORDER BY "
for _, column := range _process_orderby(orderby) {
querystr += column.Column + " " + strings.ToUpper(column.Order) + ","
}
querystr = querystr[0:len(querystr) - 1]
}
adapter.write_statement(name,strings.TrimSpace(querystr))
return nil
}
func (adapter *Mysql_Adapter) simple_inner_join(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string/*, offset int, maxCount int*/) error {
if name == "" {
return errors.New("You need a name for this statement")
}
if table1 == "" {
return errors.New("You need a name for the left table")
}
if table2 == "" {
return errors.New("You need a name for the right table")
}
if len(columns) == 0 {
return errors.New("No columns found for simple_inner_join")
}
if len(joiners) == 0 {
return errors.New("No joiners found for simple_inner_join")
}
var querystr string = "SELECT "
for _, column := range _process_columns(columns) {
var source, alias string
// Escape the column names, just in case we've used a reserved keyword
if column.Table != "" {
source = "`" + column.Table + "`.`" + column.Left + "`"
} else if column.Type == "function" {
source = column.Left
} else {
source = "`" + column.Left + "`"
}
if column.Alias != "" {
alias = " AS `" + column.Alias + "`"
}
querystr += source + alias + ","
}
// Remove the trailing comma
querystr = querystr[0:len(querystr) - 1]
querystr += " FROM `" + table1 + "` INNER JOIN `" + table2 + "` ON "
for _, joiner := range _process_joiner(joiners) {
querystr += "`" + joiner.LeftTable + "`.`" + joiner.LeftColumn + "` " + joiner.Operator + " `" + joiner.RightTable + "`.`" + joiner.RightColumn + "` AND "
} }
// Remove the trailing AND // Remove the trailing AND
querystr = querystr[0:len(querystr) - 4] querystr = querystr[0:len(querystr) - 4]

341
query_gen/utils.go Normal file
View File

@ -0,0 +1,341 @@
/* WIP Under Construction */
package main
//import "fmt"
import "strings"
import "os"
func _process_columns(colstr string) (columns []DB_Column) {
if colstr == "" {
return columns
}
colstr = strings.Replace(colstr," as "," AS ",-1)
for _, segment := range strings.Split(colstr,",") {
var outcol DB_Column
dothalves := strings.Split(strings.TrimSpace(segment),".")
var halves []string
if len(dothalves) == 2 {
outcol.Table = dothalves[0]
halves = strings.Split(dothalves[1]," AS ")
} else {
halves = strings.Split(dothalves[0]," AS ")
}
halves[0] = strings.TrimSpace(halves[0])
if len(halves) == 2 {
outcol.Alias = strings.TrimSpace(halves[1])
}
if halves[0][len(halves[0]) - 1] == ')' {
outcol.Type = "function"
} else {
outcol.Type = "column"
}
outcol.Left = halves[0]
columns = append(columns,outcol)
}
return columns
}
func _process_orderby(orderstr string) (order []DB_Order) {
if orderstr == "" {
return order
}
for _, segment := range strings.Split(orderstr,",") {
var outorder DB_Order
halves := strings.Split(strings.TrimSpace(segment)," ")
if len(halves) != 2 {
continue
}
outorder.Column = halves[0]
outorder.Order = strings.ToLower(halves[1])
order = append(order,outorder)
}
return order
}
func _process_joiner(joinstr string) (joiner []DB_Joiner) {
if joinstr == "" {
return joiner
}
joinstr = strings.Replace(joinstr," on "," ON ",-1)
joinstr = strings.Replace(joinstr," and "," AND ",-1)
for _, segment := range strings.Split(joinstr," AND ") {
var outjoin DB_Joiner
var parseOffset int
var left, right string
left, parseOffset = _get_identifier(segment, parseOffset)
outjoin.Operator, parseOffset = _get_operator(segment, parseOffset + 1)
right, parseOffset = _get_identifier(segment, parseOffset + 1)
left_column := strings.Split(left,".")
right_column := strings.Split(right,".")
outjoin.LeftTable = strings.TrimSpace(left_column[0])
outjoin.RightTable = strings.TrimSpace(right_column[0])
outjoin.LeftColumn = strings.TrimSpace(left_column[1])
outjoin.RightColumn = strings.TrimSpace(right_column[1])
joiner = append(joiner,outjoin)
}
return joiner
}
// TO-DO: Add support for keywords like BETWEEN. We'll probably need an arbitrary expression parser like with the update setters.
func _process_where(wherestr string) (where []DB_Where) {
if wherestr == "" {
return where
}
wherestr = strings.Replace(wherestr," and "," AND ",-1)
for _, segment := range strings.Split(wherestr," AND ") {
// TO-DO: Subparse the contents of a function and spit out a DB_Function struct
var outwhere DB_Where
var parseOffset int
var left, right string
left, parseOffset = _get_identifier(segment, parseOffset)
outwhere.Operator, parseOffset = _get_operator(segment, parseOffset + 1)
right, parseOffset = _get_identifier(segment, parseOffset + 1)
outwhere.LeftType = _get_identifier_type(left)
outwhere.RightType = _get_identifier_type(right)
left_operand := strings.Split(left,".")
right_operand := strings.Split(right,".")
if len(left_operand) == 2 {
outwhere.LeftTable = strings.TrimSpace(left_operand[0])
outwhere.LeftColumn = strings.TrimSpace(left_operand[1])
} else {
outwhere.LeftColumn = strings.TrimSpace(left_operand[0])
}
if len(right_operand) == 2 {
outwhere.RightTable = strings.TrimSpace(right_operand[0])
outwhere.RightColumn = strings.TrimSpace(right_operand[1])
} else {
outwhere.RightColumn = strings.TrimSpace(right_operand[0])
}
where = append(where,outwhere)
}
return where
}
func _process_set(setstr string) (setter []DB_Setter) {
if setstr == "" {
return setter
}
//fmt.Println("setstr",setstr)
// First pass, splitting the string by commas while ignoring the innards of functions
var setset []string
var buffer string
var last_item int
setstr += ","
for i := 0; i < len(setstr); i++ {
if setstr[i] == '(' {
i = _skip_function_call(setstr,i-1)
setset = append(setset,setstr[last_item:i+1])
buffer = ""
last_item = i + 2
} else if setstr[i] == ',' && buffer != "" {
setset = append(setset,buffer)
buffer = ""
last_item = i + 1
} else if (setstr[i] > 32) && setstr[i] != ',' && setstr[i] != ')' {
buffer += string(setstr[i])
}
}
// Second pass. Break this setitem into manageable chunks
buffer = ""
for _, setitem := range setset {
var tmp_setter DB_Setter
halves := strings.Split(setitem,"=")
if len(halves) != 2 {
continue
}
tmp_setter.Column = strings.TrimSpace(halves[0])
halves[1] += ")"
var optype int // 0: None, 1: Number, 2: Column, 3: Function, 4: String, 5: Operator
//fmt.Println("halves[1]",halves[1])
for i := 0; i < len(halves[1]); i++ {
char := halves[1][i]
//fmt.Println("optype",optype)
switch(optype) {
case 0: // unknown
if ('0' <= char && char <= '9') {
optype = 1
buffer = string(char)
} else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') {
optype = 2
buffer = string(char)
} else if char == '\'' {
optype = 4
} else if _is_op_byte(char) {
optype = 5
buffer = string(char)
} else if char == '?' {
//fmt.Println("Expr:","?")
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{"?","substitute"})
}
case 1: // number
if ('0' <= char && char <= '9') {
buffer += string(char)
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"number"})
}
case 2: // column
if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') {
buffer += string(char)
} else if char == '(' {
optype = 3
i--
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"column"})
}
case 3: // function
var pre_i int = i
//fmt.Println("buffer",buffer)
//fmt.Println("len(halves)",len(halves[1]))
//fmt.Println("pre_i",string(halves[1][pre_i]))
//fmt.Println("msg prior to pre_i",halves[1][0:pre_i])
i = _skip_function_call(halves[1],i-1)
//fmt.Println("i",i)
//fmt.Println("msg prior to i-1",halves[1][0:i-1])
//fmt.Println("string(i-1)",string(halves[1][i-1]))
//fmt.Println("string(i)",string(halves[1][i]))
buffer += halves[1][pre_i:i] + string(halves[1][i])
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"function"})
optype = 0
case 4: // string
if char != '\'' {
buffer += string(char)
} else {
optype = 0
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"string"})
}
case 5: // operator
if _is_op_byte(char) {
buffer += string(char)
} else {
optype = 0
i--
//fmt.Println("Expr:",buffer)
tmp_setter.Expr = append(tmp_setter.Expr,DB_Token{buffer,"operator"})
}
}
}
setter = append(setter,tmp_setter)
}
//fmt.Println("setter",setter)
return setter
}
func _is_op_byte(char byte) bool {
return char == '<' || char == '>' || char == '=' || char == '!' || char == '*' || char == '%' || char == '+' || char == '-' || char == '/'
}
func _process_fields(fieldstr string) (fields []DB_Field) {
if fieldstr == "" {
return fields
}
var buffer string
var last_item int
fieldstr += ","
for i := 0; i < len(fieldstr); i++ {
if fieldstr[i] == '(' {
i = _skip_function_call(fieldstr,i-1)
fields = append(fields,DB_Field{Name:fieldstr[last_item:i+1],Type:_get_identifier_type(fieldstr[last_item:i+1])})
buffer = ""
last_item = i + 2
} else if fieldstr[i] == ',' && buffer != "" {
fields = append(fields,DB_Field{Name:buffer,Type:_get_identifier_type(buffer)})
buffer = ""
last_item = i + 1
} else if (fieldstr[i] > 32) && fieldstr[i] != ',' && fieldstr[i] != ')' {
buffer += string(fieldstr[i])
}
}
return fields
}
func _get_identifier_type(identifier string) string {
if ('a' <= identifier[0] && identifier[0] <= 'z') || ('A' <= identifier[0] && identifier[0] <= 'Z') {
if identifier[len(identifier) - 1] == ')' {
return "function"
}
return "column"
}
if identifier[0] == '\'' || identifier[0] == '"' {
return "string"
}
return "literal"
}
func _get_identifier(segment string, startOffset int) (out string, i int) {
segment = strings.TrimSpace(segment)
segment += " " // Avoid overflow bugs with slicing
for i = startOffset; i < len(segment); i++ {
if segment[i] == '(' {
i = _skip_function_call(segment,i)
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
}
if (segment[i] == ' ' || _is_op_byte(segment[i])) && i != startOffset {
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
}
}
return strings.TrimSpace(segment[startOffset:]), (i - 1)
}
func _get_operator(segment string, startOffset int) (out string, i int) {
segment = strings.TrimSpace(segment)
segment += " " // Avoid overflow bugs with slicing
for i = startOffset; i < len(segment); i++ {
if !_is_op_byte(segment[i]) && i != startOffset {
return strings.TrimSpace(segment[startOffset:i]), (i - 1)
}
}
return strings.TrimSpace(segment[startOffset:]), (i - 1)
}
func _skip_function_call(data string, index int) int {
var brace_count int
for ;index < len(data); index++{
char := data[index]
if char == '(' {
brace_count++
} else if char == ')' {
brace_count--
if brace_count == 0 {
return index
}
}
}
return index
}
func write_file(name string, content string) (err error) {
f, err := os.Create(name)
if err != nil {
return err
}
_, err = f.WriteString(content)
if err != nil {
return err
}
f.Sync()
f.Close()
return
}

View File

@ -3,7 +3,7 @@ package main
import ( import (
"log" "log"
"fmt" //"fmt"
"strconv" "strconv"
"bytes" "bytes"
"regexp" "regexp"
@ -142,7 +142,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){
/*topicItem.CreatedAt, err = relative_time(topicItem.CreatedAt) /*topicItem.CreatedAt, err = relative_time(topicItem.CreatedAt)
if err != nil { if err != nil {
InternalError(err,w,r) replyItem.CreatedAt = ""
}*/ }*/
topicItem.LastReplyAt, err = relative_time(topicItem.LastReplyAt) topicItem.LastReplyAt, err = relative_time(topicItem.LastReplyAt)
if err != nil { if err != nil {
@ -347,6 +347,11 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
} }
}*/ }*/
topic.CreatedAt, err = relative_time(topic.CreatedAt)
if err != nil {
topic.CreatedAt = ""
}
// Calculate the offset // Calculate the offset
last_page := int(topic.PostCount / items_per_page) + 1 last_page := int(topic.PostCount / items_per_page) + 1
if page > 1 { if page > 1 {
@ -407,6 +412,11 @@ func route_topic_id(w http.ResponseWriter, r *http.Request){
} }
}*/ }*/
replyItem.CreatedAt, err = relative_time(replyItem.CreatedAt)
if err != nil {
replyItem.CreatedAt = ""
}
// We really shouldn't have inline HTML, we should do something about this... // We really shouldn't have inline HTML, we should do something about this...
if replyItem.ActionType != "" { if replyItem.ActionType != "" {
switch(replyItem.ActionType) { switch(replyItem.ActionType) {
@ -671,9 +681,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return return
} }
var topic_name string topic, err := topics.CascadeGet(tid)
var fid, createdBy int
err = db.QueryRow("select title, parentID, createdBy from topics where tid = ?",tid).Scan(&topic_name,&fid,&createdBy)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
PreError("Couldn't find the parent topic",w,r) PreError("Couldn't find the parent topic",w,r)
return return
@ -682,7 +690,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return return
} }
user, ok := SimpleForumSessionCheck(w,r,fid) user, ok := SimpleForumSessionCheck(w,r,topic.ParentID)
if !ok { if !ok {
return return
} }
@ -710,13 +718,13 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
_, err = update_forum_cache_stmt.Exec(topic_name,tid,user.Name,user.ID,1) _, err = update_forum_cache_stmt.Exec(topic.Title,tid,user.Name,user.ID,1)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
res, err := add_activity_stmt.Exec(user.ID,createdBy,"reply","topic",tid) res, err := add_activity_stmt.Exec(user.ID,topic.CreatedBy,"reply","topic",tid)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
@ -733,9 +741,14 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return return
} }
// Alert the subscribers about this post without blocking this post from being posted
if enable_websockets {
go notify_watchers(lastId)
}
// Reload the topic... // Reload the topic...
err = topics.Load(tid) err = topics.Load(tid)
if err != nil && err != sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
LocalError("The destination no longer exists",w,r,user) LocalError("The destination no longer exists",w,r,user)
return return
} else if err != nil { } else if err != nil {
@ -764,8 +777,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
var words, fid, createdBy int topic, err := topics.CascadeGet(tid)
err = db.QueryRow("select parentID, words, createdBy from topics where tid = ?", tid).Scan(&fid,&words,&createdBy)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
PreError("The requested topic doesn't exist.",w,r) PreError("The requested topic doesn't exist.",w,r)
return return
@ -774,7 +786,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
user, ok := SimpleForumSessionCheck(w,r,fid) user, ok := SimpleForumSessionCheck(w,r,topic.ParentID)
if !ok { if !ok {
return return
} }
@ -783,7 +795,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
if createdBy == user.ID { if topic.CreatedBy == user.ID {
LocalError("You can't like your own topics",w,r,user) LocalError("You can't like your own topics",w,r,user)
return return
} }
@ -797,7 +809,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
_, err = users.CascadeGet(createdBy) _, err = users.CascadeGet(topic.CreatedBy)
if err != nil && err == sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
LocalError("The target user doesn't exist",w,r,user) LocalError("The target user doesn't exist",w,r,user)
return return
@ -819,7 +831,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
res, err := add_activity_stmt.Exec(user.ID,createdBy,"like","topic",tid) res, err := add_activity_stmt.Exec(user.ID,topic.CreatedBy,"like","topic",tid)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
@ -830,18 +842,18 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return return
} }
_, err = notify_one_stmt.Exec(createdBy,lastId) _, err = notify_one_stmt.Exec(topic.CreatedBy,lastId)
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
// Live alerts, if the poster is online and WebSockets is enabled // Live alerts, if the poster is online and WebSockets is enabled
_ = ws_hub.push_alert(createdBy,"like","topic",user.ID,createdBy,tid) _ = ws_hub.push_alert(topic.CreatedBy,"like","topic",user.ID,topic.CreatedBy,tid)
// Reload the topic... // Reload the topic...
err = topics.Load(tid) err = topics.Load(tid)
if err != nil && err != sql.ErrNoRows { if err != nil && err == sql.ErrNoRows {
LocalError("The liked topic no longer exists",w,r,user) LocalError("The liked topic no longer exists",w,r,user)
return return
} else if err != nil { } else if err != nil {
@ -948,11 +960,7 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) {
} }
// Live alerts, if the poster is online and WebSockets is enabled // Live alerts, if the poster is online and WebSockets is enabled
fmt.Println("Calling push_alert") _ = ws_hub.push_alert(createdBy,"like","post",user.ID,createdBy,rid)
err = ws_hub.push_alert(createdBy,"like","post",user.ID,createdBy,rid)
if err != nil {
fmt.Println(err.Error())
}
http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther) http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther)
} }
@ -1031,7 +1039,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, sitem_id string
var fid int = 1 var fid int = 1
var tid int var tid int
var title, content, data string var title, content string
if item_type == "reply" { if item_type == "reply" {
err = db.QueryRow("select tid, content from replies where rid = ?", item_id).Scan(&tid, &content) err = db.QueryRow("select tid, content from replies where rid = ?", item_id).Scan(&tid, &content)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@ -1042,20 +1050,21 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, sitem_id string
return return
} }
err = db.QueryRow("select title, data from topics where tid = ?",tid).Scan(&title,&data) topic, err := topics.CascadeGet(tid)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("We were unable to find the topic which the reported post is supposed to be in",w,r,user) LocalError("We weren't able to find the topic the reported post is supposed to be in",w,r,user)
return return
} else if err != nil { } else if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
title = "Reply: " + title
title = "Reply: " + topic.Title
content = content + "\n\nOriginal Post: #rid-" + strconv.Itoa(item_id) content = content + "\n\nOriginal Post: #rid-" + strconv.Itoa(item_id)
} else if item_type == "user-reply" { } else if item_type == "user-reply" {
err = db.QueryRow("select uid, content from users_replies where rid = ?", item_id).Scan(&tid, &content) err = db.QueryRow("select uid, content from users_replies where rid = ?", item_id).Scan(&tid, &content)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("We were unable to find the reported post",w,r,user) LocalError("We weren't able to find the reported post",w,r,user)
return return
} else if err != nil { } else if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
@ -1064,7 +1073,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, sitem_id string
err = get_user_name_stmt.QueryRow(tid).Scan(&title) err = get_user_name_stmt.QueryRow(tid).Scan(&title)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
LocalError("We were unable to find the profile which the reported post is supposed to be on",w,r,user) LocalError("We weren't able to find the profile the reported post is supposed to be on",w,r,user)
return return
} else if err != nil { } else if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
@ -1315,8 +1324,8 @@ func route_account_own_edit_avatar_submit(w http.ResponseWriter, r *http.Request
LocalError("This user no longer exists!",w,r,user) LocalError("This user no longer exists!",w,r,user)
return return
} }
noticeList = append(noticeList, "Your avatar was successfully updated")
noticeList = append(noticeList, "Your avatar was successfully updated")
pi := Page{"Edit Avatar",user,noticeList,tList,nil} pi := Page{"Edit Avatar",user,noticeList,tList,nil}
templates.ExecuteTemplate(w,"account-own-edit-avatar.html", pi) templates.ExecuteTemplate(w,"account-own-edit-avatar.html", pi)
} }
@ -1558,7 +1567,7 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
return return
} }
// Emergency password reset mechanism.. // Admin password reset mechanism...
if salt == "" { if salt == "" {
if password != real_password { if password != real_password {
LocalError("That's not the correct password.",w,r,user) LocalError("That's not the correct password.",w,r,user)
@ -1566,15 +1575,26 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
} }
// Re-encrypt the password // Re-encrypt the password
SetPassword(uid, password) SetPassword(uid, real_password)
} else { // Normal login..
// Fe-fetch the user data...
err = login_stmt.QueryRow(username).Scan(&uid, &username, &real_password, &salt)
if err == sql.ErrNoRows {
LocalError("That username doesn't exist anymore.",w,r,user)
return
} else if err != nil {
InternalError(err,w,r)
return
}
}
password = password + salt password = password + salt
if err != nil { if err != nil {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
err := bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password)) err = bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password))
if err == bcrypt.ErrMismatchedHashAndPassword { if err == bcrypt.ErrMismatchedHashAndPassword {
LocalError("That's not the correct password.",w,r,user) LocalError("That's not the correct password.",w,r,user)
return return
@ -1582,7 +1602,6 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
InternalError(err,w,r) InternalError(err,w,r)
return return
} }
}
session, err = GenerateSafeString(sessionLength) session, err = GenerateSafeString(sessionLength)
if err != nil { if err != nil {

View File

@ -296,75 +296,83 @@ var topic_alt_42 []byte = []byte(`&type=topic" class="action_button report_item"
`) `)
var topic_alt_43 []byte = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`) var topic_alt_43 []byte = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`)
var topic_alt_44 []byte = []byte(`</a>`) var topic_alt_44 []byte = []byte(`</a>`)
var topic_alt_45 []byte = []byte(`<a class="action_button action_button_right hide_on_micro">`) var topic_alt_45 []byte = []byte(`
var topic_alt_46 []byte = []byte(` up</a>`) <a class="action_button action_button_right hide_on_mobile">`)
var topic_alt_47 []byte = []byte(` var topic_alt_46 []byte = []byte(`</a>
`)
var topic_alt_47 []byte = []byte(`<a class="action_button action_button_right hide_on_micro">`)
var topic_alt_48 []byte = []byte(` up</a>`)
var topic_alt_49 []byte = []byte(`
</div> </div>
</div><div style="clear:both;"></div> </div><div style="clear:both;"></div>
</div> </div>
`) `)
var topic_alt_48 []byte = []byte(` var topic_alt_50 []byte = []byte(`
<div class="rowitem passive deletable_block editable_parent post_item `) <div class="rowitem passive deletable_block editable_parent post_item `)
var topic_alt_49 []byte = []byte(`action_item`) var topic_alt_51 []byte = []byte(`action_item`)
var topic_alt_50 []byte = []byte(`"> var topic_alt_52 []byte = []byte(`">
<div class="userinfo"> <div class="userinfo">
<div class="avatar_item" style="background-image: url(`) <div class="avatar_item" style="background-image: url(`)
var topic_alt_51 []byte = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div> var topic_alt_53 []byte = []byte(`), url(/static/white-dot.jpg);background-position: 0px -10px;">&nbsp;</div>
<a href="/user/`) <a href="/user/`)
var topic_alt_52 []byte = []byte(`" class="the_name">`) var topic_alt_54 []byte = []byte(`" class="the_name">`)
var topic_alt_53 []byte = []byte(`</a> var topic_alt_55 []byte = []byte(`</a>
`) `)
var topic_alt_54 []byte = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`) var topic_alt_56 []byte = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
var topic_alt_55 []byte = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_56 []byte = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_57 []byte = []byte(`</div><div class="tag_post"></div></div>`) var topic_alt_57 []byte = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_58 []byte = []byte(` var topic_alt_58 []byte = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag post_level">Level `)
var topic_alt_59 []byte = []byte(`</div><div class="tag_post"></div></div>`)
var topic_alt_60 []byte = []byte(`
</div> </div>
<div class="content_container" `) <div class="content_container" `)
var topic_alt_59 []byte = []byte(`style="margin-left: 0px;"`) var topic_alt_61 []byte = []byte(`style="margin-left: 0px;"`)
var topic_alt_60 []byte = []byte(`> var topic_alt_62 []byte = []byte(`>
`) `)
var topic_alt_61 []byte = []byte(` var topic_alt_63 []byte = []byte(`
<span class="action_icon" style="font-size: 18px;padding-right: 5px;">`) <span class="action_icon" style="font-size: 18px;padding-right: 5px;">`)
var topic_alt_62 []byte = []byte(`</span> var topic_alt_64 []byte = []byte(`</span>
<span>`) <span>`)
var topic_alt_63 []byte = []byte(`</span> var topic_alt_65 []byte = []byte(`</span>
`) `)
var topic_alt_64 []byte = []byte(` var topic_alt_66 []byte = []byte(`
<div class="editable_block user_content">`) <div class="editable_block user_content">`)
var topic_alt_65 []byte = []byte(`</div> var topic_alt_67 []byte = []byte(`</div>
<div class="button_container"> <div class="button_container">
`) `)
var topic_alt_66 []byte = []byte(`<a href="/reply/like/submit/`) var topic_alt_68 []byte = []byte(`<a href="/reply/like/submit/`)
var topic_alt_67 []byte = []byte(`" class="action_button">+1</a>`) var topic_alt_69 []byte = []byte(`" class="action_button">+1</a>`)
var topic_alt_68 []byte = []byte(`<a href="/reply/edit/submit/`) var topic_alt_70 []byte = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_69 []byte = []byte(`" class="action_button edit_item">Edit</a>`) var topic_alt_71 []byte = []byte(`" class="action_button edit_item">Edit</a>`)
var topic_alt_70 []byte = []byte(`<a href="/reply/delete/submit/`) var topic_alt_72 []byte = []byte(`<a href="/reply/delete/submit/`)
var topic_alt_71 []byte = []byte(`" class="action_button delete_item">Delete</a>`) var topic_alt_73 []byte = []byte(`" class="action_button delete_item">Delete</a>`)
var topic_alt_72 []byte = []byte(` var topic_alt_74 []byte = []byte(`
<a href="/report/submit/`) <a href="/report/submit/`)
var topic_alt_73 []byte = []byte(`?session=`) var topic_alt_75 []byte = []byte(`?session=`)
var topic_alt_74 []byte = []byte(`&type=reply" class="action_button report_item">Report</a> var topic_alt_76 []byte = []byte(`&type=reply" class="action_button report_item">Report</a>
`) `)
var topic_alt_75 []byte = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`) var topic_alt_77 []byte = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`)
var topic_alt_76 []byte = []byte(`</a>`) var topic_alt_78 []byte = []byte(`</a>`)
var topic_alt_77 []byte = []byte(`<a class="action_button action_button_right hide_on_micro">`)
var topic_alt_78 []byte = []byte(` up</a>`)
var topic_alt_79 []byte = []byte(` var topic_alt_79 []byte = []byte(`
<a class="action_button action_button_right hide_on_mobile">`)
var topic_alt_80 []byte = []byte(`</a>
`)
var topic_alt_81 []byte = []byte(`<a class="action_button action_button_right hide_on_micro">`)
var topic_alt_82 []byte = []byte(` up</a>`)
var topic_alt_83 []byte = []byte(`
</div> </div>
`) `)
var topic_alt_80 []byte = []byte(` var topic_alt_84 []byte = []byte(`
</div> </div>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</div> </div>
`) `)
var topic_alt_81 []byte = []byte(`</div> var topic_alt_85 []byte = []byte(`</div>
`) `)
var topic_alt_82 []byte = []byte(` var topic_alt_86 []byte = []byte(`
<div class="rowblock" style="border-top: none;"> <div class="rowblock" style="border-top: none;">
<form action="/reply/create/" method="post"> <form action="/reply/create/" method="post">
<input name="tid" value='`) <input name="tid" value='`)
var topic_alt_83 []byte = []byte(`' type="hidden" /> var topic_alt_87 []byte = []byte(`' type="hidden" />
<div class="formrow"> <div class="formrow">
<div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div> <div class="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div>
</div> </div>

View File

@ -1,8 +1,8 @@
// Code generated by. DO NOT EDIT. // Code generated by. DO NOT EDIT.
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
package main package main
import "strconv"
import "io" import "io"
import "strconv"
func init() { func init() {
template_topic_alt_handle = template_topic_alt template_topic_alt_handle = template_topic_alt
@ -105,6 +105,7 @@ w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_28) w.Write(topic_alt_28)
w.Write([]byte(tmpl_topic_alt_vars.Topic.Content)) w.Write([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_29) w.Write(topic_alt_29)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem { if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_30) w.Write(topic_alt_30)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
@ -141,89 +142,98 @@ w.Write(topic_alt_43)
w.Write([]byte(tmpl_topic_alt_vars.Topic.IpAddress)) w.Write([]byte(tmpl_topic_alt_vars.Topic.IpAddress))
w.Write(topic_alt_44) w.Write(topic_alt_44)
} }
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_45)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_46)
} }
w.Write(topic_alt_45)
w.Write([]byte(tmpl_topic_alt_vars.Topic.CreatedAt))
w.Write(topic_alt_46)
if tmpl_topic_alt_vars.Topic.LikeCount > 0 {
w.Write(topic_alt_47) w.Write(topic_alt_47)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.LikeCount)))
w.Write(topic_alt_48)
}
w.Write(topic_alt_49)
if len(tmpl_topic_alt_vars.ItemList) != 0 { if len(tmpl_topic_alt_vars.ItemList) != 0 {
for _, item := range tmpl_topic_alt_vars.ItemList { for _, item := range tmpl_topic_alt_vars.ItemList {
w.Write(topic_alt_48)
if item.ActionType != "" {
w.Write(topic_alt_49)
}
w.Write(topic_alt_50) w.Write(topic_alt_50)
w.Write([]byte(item.Avatar))
w.Write(topic_alt_51)
w.Write([]byte(strconv.Itoa(item.CreatedBy)))
w.Write(topic_alt_52)
w.Write([]byte(item.CreatedByName))
w.Write(topic_alt_53)
if item.Tag != "" {
w.Write(topic_alt_54)
w.Write([]byte(item.Tag))
w.Write(topic_alt_55)
} else {
w.Write(topic_alt_56)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_57)
}
w.Write(topic_alt_58)
if item.ActionType != "" { if item.ActionType != "" {
w.Write(topic_alt_51)
}
w.Write(topic_alt_52)
w.Write([]byte(item.Avatar))
w.Write(topic_alt_53)
w.Write([]byte(strconv.Itoa(item.CreatedBy)))
w.Write(topic_alt_54)
w.Write([]byte(item.CreatedByName))
w.Write(topic_alt_55)
if item.Tag != "" {
w.Write(topic_alt_56)
w.Write([]byte(item.Tag))
w.Write(topic_alt_57)
} else {
w.Write(topic_alt_58)
w.Write([]byte(strconv.Itoa(item.Level)))
w.Write(topic_alt_59) w.Write(topic_alt_59)
} }
w.Write(topic_alt_60) w.Write(topic_alt_60)
if item.ActionType != "" { if item.ActionType != "" {
w.Write(topic_alt_61) w.Write(topic_alt_61)
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_62)
w.Write([]byte(item.ActionType))
w.Write(topic_alt_63)
} else {
w.Write(topic_alt_64)
w.Write([]byte(item.ContentHtml))
w.Write(topic_alt_65)
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_66)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_67)
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply { w.Write(topic_alt_62)
if item.ActionType != "" {
w.Write(topic_alt_63)
w.Write([]byte(item.ActionIcon))
w.Write(topic_alt_64)
w.Write([]byte(item.ActionType))
w.Write(topic_alt_65)
} else {
w.Write(topic_alt_66)
w.Write([]byte(item.ContentHtml))
w.Write(topic_alt_67)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_68) w.Write(topic_alt_68)
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_69) w.Write(topic_alt_69)
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply { if tmpl_topic_alt_vars.CurrentUser.Perms.EditReply {
w.Write(topic_alt_70) w.Write(topic_alt_70)
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_71) w.Write(topic_alt_71)
} }
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_72) w.Write(topic_alt_72)
w.Write([]byte(strconv.Itoa(item.ID))) w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_73) w.Write(topic_alt_73)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_74)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_75)
w.Write([]byte(item.IpAddress))
w.Write(topic_alt_76)
} }
if item.LikeCount > 0 { w.Write(topic_alt_74)
w.Write([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_75)
w.Write([]byte(tmpl_topic_alt_vars.CurrentUser.Session))
w.Write(topic_alt_76)
if tmpl_topic_alt_vars.CurrentUser.Perms.ViewIPs {
w.Write(topic_alt_77) w.Write(topic_alt_77)
w.Write([]byte(strconv.Itoa(item.LikeCount))) w.Write([]byte(item.IpAddress))
w.Write(topic_alt_78) w.Write(topic_alt_78)
} }
}
w.Write(topic_alt_79) w.Write(topic_alt_79)
} w.Write([]byte(item.CreatedAt))
w.Write(topic_alt_80) w.Write(topic_alt_80)
} if item.LikeCount > 0 {
}
w.Write(topic_alt_81) w.Write(topic_alt_81)
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply { w.Write([]byte(strconv.Itoa(item.LikeCount)))
w.Write(topic_alt_82) w.Write(topic_alt_82)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID))) }
w.Write(topic_alt_83) w.Write(topic_alt_83)
} }
w.Write(topic_alt_84)
}
}
w.Write(topic_alt_85)
if tmpl_topic_alt_vars.CurrentUser.Perms.CreateReply {
w.Write(topic_alt_86)
w.Write([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
w.Write(topic_alt_87)
}
w.Write(footer_0) w.Write(footer_0)
} }

View File

@ -32,14 +32,16 @@
<div class="hide_on_edit topic_content user_content">{{.Topic.Content}}</div> <div class="hide_on_edit topic_content user_content">{{.Topic.Content}}</div>
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea> <textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
<div class="button_container"> <div class="button_container">
{{if .CurrentUser.Loggedin}}
{{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button">+1</a>{{end}} {{if .CurrentUser.Perms.LikeItem}}<a href="/topic/like/submit/{{.Topic.ID}}" class="action_button">+1</a>{{end}}
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit">Edit</a>{{end}} {{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit">Edit</a>{{end}}
{{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}} {{if .CurrentUser.Perms.DeleteTopic}}<a href="/topic/delete/submit/{{.Topic.ID}}" class="action_button delete_item">Delete</a>{{end}}
{{if .CurrentUser.Perms.PinTopic}} {{if .CurrentUser.Perms.PinTopic}}
{{if .Topic.Sticky}}<a href='/topic/unstick/submit/{{.Topic.ID}}' class="action_button">Unpin</a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="action_button">Pin</a>{{end}}{{end}} {{if .Topic.Sticky}}<a href='/topic/unstick/submit/{{.Topic.ID}}' class="action_button">Unpin</a>{{else}}<a href='/topic/stick/submit/{{.Topic.ID}}' class="action_button">Pin</a>{{end}}{{end}}
<a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item">Report</a> <a href="/report/submit/{{.Topic.ID}}?session={{.CurrentUser.Session}}&type=topic" class="action_button report_item">Report</a>
{{/* Element Queries might help with having to use JS. Who knows when or *if* the W3C will standardise it */}}
{{if .CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.Topic.IpAddress}}</a>{{end}} {{if .CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.Topic.IpAddress}}</a>{{end}}
{{end}}
<a class="action_button action_button_right hide_on_mobile">{{.Topic.CreatedAt}}</a>
{{if .Topic.LikeCount}}<a class="action_button action_button_right hide_on_micro">{{.Topic.LikeCount}} up</a>{{end}} {{if .Topic.LikeCount}}<a class="action_button action_button_right hide_on_micro">{{.Topic.LikeCount}} up</a>{{end}}
</div> </div>
</div><div style="clear:both;"></div> </div><div style="clear:both;"></div>
@ -58,11 +60,14 @@
{{else}} {{else}}
<div class="editable_block user_content">{{.ContentHtml}}</div> <div class="editable_block user_content">{{.ContentHtml}}</div>
<div class="button_container"> <div class="button_container">
{{if $.CurrentUser.Loggedin}}
{{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button">+1</a>{{end}} {{if $.CurrentUser.Perms.LikeItem}}<a href="/reply/like/submit/{{.ID}}" class="action_button">+1</a>{{end}}
{{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item">Edit</a>{{end}} {{if $.CurrentUser.Perms.EditReply}}<a href="/reply/edit/submit/{{.ID}}" class="action_button edit_item">Edit</a>{{end}}
{{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item">Delete</a>{{end}} {{if $.CurrentUser.Perms.DeleteReply}}<a href="/reply/delete/submit/{{.ID}}" class="action_button delete_item">Delete</a>{{end}}
<a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item">Report</a> <a href="/report/submit/{{.ID}}?session={{$.CurrentUser.Session}}&type=reply" class="action_button report_item">Report</a>
{{if $.CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.IpAddress}}</a>{{end}} {{if $.CurrentUser.Perms.ViewIPs}}<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">{{.IpAddress}}</a>{{end}}
{{end}}
<a class="action_button action_button_right hide_on_mobile">{{.CreatedAt}}</a>
{{if .LikeCount}}<a class="action_button action_button_right hide_on_micro">{{.LikeCount}} up</a>{{end}} {{if .LikeCount}}<a class="action_button action_button_right hide_on_micro">{{.LikeCount}} up</a>{{end}}
</div> </div>
{{end}} {{end}}

View File

@ -446,7 +446,6 @@ button.username {
padding-right: 4px; padding-right: 4px;
padding-bottom: 2px; padding-bottom: 2px;
} }
.post_item:last-child { padding-bottom: 7px; }
.post_tag { .post_tag {
margin-top: 0px; margin-top: 0px;
text-align: center; text-align: center;

View File

@ -22,6 +22,7 @@ type Topic struct
PostCount int PostCount int
LikeCount int LikeCount int
ClassName string // CSS Class Name ClassName string // CSS Class Name
Data string // Used for report metadata
} }
type TopicUser struct type TopicUser struct
@ -41,6 +42,7 @@ type TopicUser struct
PostCount int PostCount int
LikeCount int LikeCount int
ClassName string ClassName string
Data string // Used for report metadata
CreatedByName string CreatedByName string
Group int Group int
@ -140,7 +142,7 @@ func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) {
} }
topic = &Topic{ID:id} topic = &Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
if err == nil { if err == nil {
sts.Add(topic) sts.Add(topic)
} }
@ -149,13 +151,13 @@ func (sts *StaticTopicStore) CascadeGet(id int) (*Topic, error) {
func (sts *StaticTopicStore) BypassGet(id int) (*Topic, error) { func (sts *StaticTopicStore) BypassGet(id int) (*Topic, error) {
topic := &Topic{ID:id} topic := &Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return topic, err return topic, err
} }
func (sts *StaticTopicStore) Load(id int) error { func (sts *StaticTopicStore) Load(id int) error {
topic := &Topic{ID:id} topic := &Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
if err == nil { if err == nil {
sts.Set(topic) sts.Set(topic)
} else { } else {
@ -245,31 +247,31 @@ func NewSqlTopicStore() *SqlTopicStore {
func (sus *SqlTopicStore) Get(id int) (*Topic, error) { func (sus *SqlTopicStore) Get(id int) (*Topic, error) {
topic := Topic{ID:id} topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return &topic, err return &topic, err
} }
func (sus *SqlTopicStore) GetUnsafe(id int) (*Topic, error) { func (sus *SqlTopicStore) GetUnsafe(id int) (*Topic, error) {
topic := Topic{ID:id} topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return &topic, err return &topic, err
} }
func (sus *SqlTopicStore) CascadeGet(id int) (*Topic, error) { func (sus *SqlTopicStore) CascadeGet(id int) (*Topic, error) {
topic := Topic{ID:id} topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return &topic, err return &topic, err
} }
func (sts *SqlTopicStore) BypassGet(id int) (*Topic, error) { func (sts *SqlTopicStore) BypassGet(id int) (*Topic, error) {
topic := &Topic{ID:id} topic := &Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return topic, err return topic, err
} }
func (sus *SqlTopicStore) Load(id int) error { func (sus *SqlTopicStore) Load(id int) error {
topic := Topic{ID:id} topic := Topic{ID:id}
err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_stmt.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return err return err
} }
@ -336,7 +338,6 @@ func get_topicuser(tid int) (TopicUser,error) {
//fmt.Printf("%+v\n", the_topic) //fmt.Printf("%+v\n", the_topic)
tu.Tag = groups[tu.Group].Tag tu.Tag = groups[tu.Group].Tag
topics.Add(&the_topic) topics.Add(&the_topic)
//err = errors.Error("Loaded data via query")
return tu, err return tu, err
} }
@ -360,12 +361,13 @@ func copy_topic_to_topicuser(topic *Topic, user *User) (tu TopicUser) {
tu.IpAddress = topic.IpAddress tu.IpAddress = topic.IpAddress
tu.PostCount = topic.PostCount tu.PostCount = topic.PostCount
tu.LikeCount = topic.LikeCount tu.LikeCount = topic.LikeCount
tu.Data = topic.Data
return tu return tu
} }
func get_topic_by_reply(rid int) (*Topic, error) { func get_topic_by_reply(rid int) (*Topic, error) {
topic := Topic{ID:0} topic := Topic{ID:0}
err := get_topic_by_reply_stmt.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount) err := get_topic_by_reply_stmt.QueryRow(rid).Scan(&topic.ID, &topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.Is_Closed, &topic.Sticky, &topic.ParentID, &topic.IpAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
return &topic, err return &topic, err
} }

20
user.go
View File

@ -195,17 +195,6 @@ func (sts *StaticUserStore) AddUnsafe(item *User) error {
return nil return nil
} }
/*func (sts *StaticUserStore) SetConn(id int, conn interface{}) *User, error {
sts.Lock()
user, err := sts.CascadeGet(id)
sts.Unlock()
if err != nil {
return nil, err
}
user.WS_Conn = conn
return user, nil
}*/
func (sts *StaticUserStore) Remove(id int) error { func (sts *StaticUserStore) Remove(id int) error {
sts.Lock() sts.Lock()
delete(sts.items,id) delete(sts.items,id)
@ -336,8 +325,7 @@ func (sus *SqlUserStore) GetCapacity() int {
} }
func (sus *SqlUserStore) GetLength() int { func (sus *SqlUserStore) GetLength() int {
// Return the total number of users registered on the forums return 0 // Return the total number of users registered on the forums?
return 0
} }
func SetPassword(uid int, password string) (error) { func SetPassword(uid int, password string) (error) {
@ -364,10 +352,8 @@ func SendValidationEmail(username string, email string, token string) bool {
if enable_ssl { if enable_ssl {
schema = "s" schema = "s"
} }
subject := "Validate Your Email @ " + site_name subject := "Validate Your Email @ " + site_name
msg := "Dear " + username + ", following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. http" + schema + "://" + site_url + "/user/edit/token/" + token + "\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused." msg := "Dear " + username + ", following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. http" + schema + "://" + site_url + "/user/edit/token/" + token + "\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
return SendEmail(email, subject, msg) return SendEmail(email, subject, msg)
} }
@ -542,13 +528,13 @@ func decrease_post_user_stats(wcount int, uid int, topic bool, user User) error
base_score = -2 base_score = -2
} }
if wcount >= settings["megapost_min_chars"].(int) { if wcount >= settings["megapost_min_words"].(int) {
_, err := increment_user_megaposts_stmt.Exec(-1,-1,-1,uid) _, err := increment_user_megaposts_stmt.Exec(-1,-1,-1,uid)
if err != nil { if err != nil {
return err return err
} }
mod = 4 mod = 4
} else if wcount >= settings["bigpost_min_chars"].(int) { } else if wcount >= settings["bigpost_min_words"].(int) {
_, err := increment_user_bigposts_stmt.Exec(-1,-1,uid) _, err := increment_user_bigposts_stmt.Exec(-1,-1,uid)
if err != nil { if err != nil {
return err return err

View File

@ -108,12 +108,55 @@ func(hub *WS_Hub) push_alert(targetUser int, event string, elementType string, a
return err return err
} }
//fmt.Println("Writing to the client")
w.Write([]byte(alert)) w.Write([]byte(alert))
w.Close() w.Close()
return nil return nil
} }
func(hub *WS_Hub) push_alerts(users []int, event string, elementType string, actor_id int, targetUser_id int, elementID int) error {
//fmt.Println("In push_alerts")
var ws_users []*WS_User
hub.users.RLock()
// We don't want to keep a lock on this for too long, so we'll accept some nil pointers
for _, uid := range users {
ws_users = append(ws_users, hub.online_users[uid])
}
hub.users.RUnlock()
if len(ws_users) == 0 {
return ws_nouser
}
var errs []error
for _, ws_user := range ws_users {
if ws_user == nil {
continue
}
//fmt.Println("Building alert")
alert, err := build_alert(event, elementType, actor_id, targetUser_id, elementID, *ws_user.User)
if err != nil {
errs = append(errs,err)
}
//fmt.Println("Getting WS Writer")
w, err := ws_user.conn.NextWriter(websocket.TextMessage)
if err != nil {
errs = append(errs,err)
}
w.Write([]byte(alert))
w.Close()
}
// Return the first error
if len(errs) != 0 {
for _, err := range errs {
return err
}
}
return nil
}
func route_websockets(w http.ResponseWriter, r *http.Request) { func route_websockets(w http.ResponseWriter, r *http.Request) {
user, ok := SimpleSessionCheck(w,r) user, ok := SimpleSessionCheck(w,r)
if !ok { if !ok {