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
import "log"
import "strings"
import "strconv"
import "errors"
import "database/sql"
/*
"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
}
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_reply_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_topic_basic_stmt *sql.Stmt
var get_activity_entry_stmt *sql.Stmt
var get_topic_list_stmt *sql.Stmt
var get_topic_user_stmt *sql.Stmt
var get_topic_by_reply_stmt *sql.Stmt
var get_topic_replies_stmt *sql.Stmt
var get_forum_topics_stmt *sql.Stmt
var get_profile_replies_stmt *sql.Stmt
var get_watchers_stmt *sql.Stmt
var create_topic_stmt *sql.Stmt
var create_report_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 notify_one_stmt *sql.Stmt
var register_stmt *sql.Stmt
var add_email_stmt *sql.Stmt
var create_profile_reply_stmt *sql.Stmt
var add_subscription_stmt *sql.Stmt
var create_forum_stmt *sql.Stmt
var add_forum_perms_to_forum_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_rank_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) {
if debug {
@ -105,7 +116,7 @@ func gen_mysql() (err error) {
}
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 {
return err
}
@ -212,6 +223,24 @@ func gen_mysql() (err error) {
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.")
get_emails_by_user_stmt, err = db.Prepare("SELECT `email`,`validated` FROM `emails` WHERE `uid` = ?")
if err != nil {
@ -224,38 +253,50 @@ func gen_mysql() (err error) {
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.")
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 {
return err
}
log.Print("Preparing get_topic_user statement.")
get_topic_user_stmt, err = db.Prepare("SELECT `topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`createdAt`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`parentID`,`topics`.`ipaddress`,`topics`.`postCount`,`topics`.`likeCount`,`users`.`name`,`users`.`avatar`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy`=`users`.`uid` WHERE `tid` = ?")
get_topic_user_stmt, err = db.Prepare("SELECT `topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`createdAt`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`parentID`,`topics`.`ipaddress`,`topics`.`postCount`,`topics`.`likeCount`,`users`.`name`,`users`.`avatar`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `tid` = ?")
if err != nil {
return err
}
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 {
return err
}
log.Print("Preparing get_topic_replies statement.")
get_topic_replies_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy`=`users`.`uid` WHERE `tid` = ?")
get_topic_replies_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ?")
if err != nil {
return err
}
log.Print("Preparing get_forum_topics statement.")
get_forum_topics_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy`=`users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
get_forum_topics_stmt, err = db.Prepare("SELECT `topics`.`tid`,`topics`.`title`,`topics`.`content`,`topics`.`createdBy`,`topics`.`is_closed`,`topics`.`sticky`,`topics`.`createdAt`,`topics`.`lastReplyAt`,`topics`.`parentID`,`users`.`name`,`users`.`avatar` FROM `topics` LEFT JOIN `users` ON `topics`.`createdBy` = `users`.`uid` WHERE `topics`.`parentID` = ? ORDER BY topics.sticky DESC,topics.lastReplyAt DESC,topics.createdBy DESC")
if err != nil {
return err
}
log.Print("Preparing get_profile_replies statement.")
get_profile_replies_stmt, err = db.Prepare("SELECT `users_replies`.`rid`,`users_replies`.`content`,`users_replies`.`createdBy`,`users_replies`.`createdAt`,`users_replies`.`lastEdit`,`users_replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group` FROM `users_replies` LEFT JOIN `users` ON `users_replies`.`createdBy`=`users`.`uid` WHERE `users_replies`.`uid` = ?")
get_profile_replies_stmt, err = db.Prepare("SELECT `users_replies`.`rid`,`users_replies`.`content`,`users_replies`.`createdBy`,`users_replies`.`createdAt`,`users_replies`.`lastEdit`,`users_replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group` FROM `users_replies` LEFT JOIN `users` ON `users_replies`.`createdBy` = `users`.`uid` WHERE `users_replies`.`uid` = ?")
if err != nil {
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
}
@ -308,12 +349,24 @@ func gen_mysql() (err error) {
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.")
create_profile_reply_stmt, err = db.Prepare("INSERT INTO `users_replies`(`uid`,`content`,`parsed_content`,`createdAt`,`createdBy`) VALUES (?,?,?,NOW(),?)")
if err != nil {
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.")
create_forum_stmt, err = db.Prepare("INSERT INTO `forums`(`name`,`desc`,`active`,`preset`) VALUES (?,?,?,?)")
if err != nil {
@ -572,5 +625,29 @@ func gen_mysql() (err error) {
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
}

View File

@ -61,7 +61,7 @@ func compile_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
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)
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_page := ForumPage{"General Forum",user,noticeList,topicList,forum_item,1,1,nil}
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"
}
var tid, fid int
var tid int
tid, err = strconv.Atoi(r.URL.Path[len("/topic/edit/submit/"):])
if err != nil {
PreErrorJSQ("The provided TopicID is not a valid number.",w,r,is_js)
return
}
var old_is_closed bool
err = db.QueryRow("select parentID, is_closed from topics where tid = ?", tid).Scan(&fid,&old_is_closed)
old_topic, err := topics.CascadeGet(tid)
if err == sql.ErrNoRows {
PreErrorJSQ("The topic you tried to edit doesn't exist.",w,r,is_js)
return
@ -39,7 +38,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
return
}
user, ok := SimpleForumSessionCheck(w,r,fid)
user, ok := SimpleForumSessionCheck(w,r,old_topic.ParentID)
if !ok {
return
}
@ -65,7 +64,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
return
}
if old_is_closed != is_closed {
if old_topic.Is_Closed != is_closed {
var action string
if is_closed {
action = "lock"
@ -97,9 +96,12 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request) {
}
err = topics.Load(tid)
if err != nil {
if err == sql.ErrNoRows {
LocalErrorJSQ("This topic no longer exists!",w,r,user,is_js)
return
} else if err != nil {
InternalErrorJSQ(err,w,r,is_js)
return
}
if is_js == "0" {
@ -116,9 +118,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) {
return
}
var content string
var createdBy, fid int
err = db.QueryRow("select content, createdBy, parentID from topics where tid = ?", tid).Scan(&content, &createdBy, &fid)
topic, err := topics.CascadeGet(tid)
if err == sql.ErrNoRows {
PreError("The topic you tried to delete doesn't exist.",w,r)
return
@ -127,7 +127,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request) {
return
}
user, ok := SimpleForumSessionCheck(w,r,fid)
user, ok := SimpleForumSessionCheck(w,r,topic.ParentID)
if !ok {
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))
http.Redirect(w,r,"/",http.StatusSeeOther)
wcount := word_count(content)
err = decrease_post_user_stats(wcount,createdBy,true,user)
wcount := word_count(topic.Content)
err = decrease_post_user_stats(wcount,topic.CreatedBy,true,user)
if err != nil {
InternalError(err,w,r)
return
}
_, err = remove_topics_from_forum_stmt.Exec(1, fid)
_, err = remove_topics_from_forum_stmt.Exec(1, topic.ParentID)
if err != nil {
InternalError(err,w,r)
return
}
forums[fid].TopicCount -= 1
forums[topic.ParentID].TopicCount -= 1
topics.Remove(tid)
}
@ -553,7 +553,7 @@ func route_ban(w http.ResponseWriter, r *http.Request) {
}
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 {
LocalError("The user you're trying to ban no longer exists.",w,r,user)
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)
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)
return
}
}*/
var group int
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 {
LocalError("The user you're trying to ban no longer exists.",w,r,user)
return
@ -663,9 +663,8 @@ func route_unban(w http.ResponseWriter, r *http.Request) {
return
}
var uname string
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 {
LocalError("The user you're trying to unban no longer exists.",w,r,user)
return
@ -697,10 +696,14 @@ func route_unban(w http.ResponseWriter, r *http.Request) {
}
err = users.Load(uid)
if err != nil {
if err != nil && err == sql.ErrNoRows {
LocalError("This user no longer exists!",w,r,user)
return
} else if err != nil {
InternalError(err,w,r)
return
}
http.Redirect(w,r,"/user/" + strconv.Itoa(uid),http.StatusSeeOther)
}
@ -724,9 +727,8 @@ func route_activate(w http.ResponseWriter, r *http.Request) {
return
}
var uname string
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 {
LocalError("The account you're trying to activate no longer exists.",w,r,user)
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_forum_topics_offset_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_count_by_watcher_stmt *sql.Stmt
var add_email_stmt *sql.Stmt
var update_email_stmt *sql.Stmt
var verify_email_stmt *sql.Stmt
var delete_profile_reply_stmt *sql.Stmt
var update_email_stmt, verify_email_stmt *sql.Stmt
var forum_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_staff_stmt *sql.Stmt
var add_forum_perms_to_forum_members_stmt *sql.Stmt
@ -86,24 +79,6 @@ func init_database() (err error) {
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.")
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 {
@ -116,12 +91,6 @@ func init_database() (err error) {
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.")
update_email_stmt, err = db.Prepare("UPDATE emails SET email = ?, uid = ?, validated = ?, token = ? WHERE email = ?")
if err != nil {
@ -134,12 +103,6 @@ func init_database() (err error) {
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.")
forum_entry_exists_stmt, err = db.Prepare("SELECT `fid` FROM `forums` WHERE `name` = '' order by fid asc limit 1")
if err != nil {
@ -152,12 +115,6 @@ func init_database() (err error) {
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.")
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 {

View File

@ -32,5 +32,9 @@ func(hub *WS_Hub) push_alert(_ int, _ string, _ string, _ int, _ int, _ int) err
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) {
}

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)
groupList = append(groupList, GroupAdmin{group.ID,group.Name,rank,rank_class,can_edit,can_delete})
}
//fmt.Printf("%+v\n", groupList)

View File

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

View File

@ -1,10 +1,7 @@
/* WIP Under Construction */
package main
//import "fmt"
import "strings"
import "log"
import "os"
var db_registry []DB_Adapter
var blank_order []DB_Order
@ -40,6 +37,7 @@ type DB_Joiner struct
LeftColumn string
RightTable string
RightColumn string
Operator string
}
type DB_Order struct
@ -63,9 +61,13 @@ type DB_Adapter interface {
simple_insert(string,string,string,string) error
simple_replace(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_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
// TO-DO: Add a simple query builder
}
@ -83,7 +85,11 @@ func write_statements(adapter DB_Adapter) error {
if err != nil {
return err
}
err = write_joins(adapter)
err = write_left_joins(adapter)
if err != nil {
return err
}
err = write_inner_joins(adapter)
if err != nil {
return err
}
@ -99,6 +105,10 @@ func write_statements(adapter DB_Adapter) error {
if err != nil {
return err
}
err = write_deletes(adapter)
if err != nil {
return err
}
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_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 = ?","")
@ -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_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_topic_basic","topics","title, content","tid = ?","")
adapter.simple_select("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","")
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_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_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 = ?","")
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
}
@ -185,8 +211,12 @@ func write_inserts(adapter DB_Adapter) error {
// 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("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("add_subscription","activity_subscriptions","user,targetID,targetType,level","?,?,?,2")
adapter.simple_insert("create_forum","forums","name, desc, active, preset","?,?,?,?")
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
}
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),".")
func write_deletes(adapter DB_Adapter) error {
adapter.simple_delete("delete_reply","replies","rid = ?")
var halves []string
if len(dothalves) == 2 {
outcol.Table = dothalves[0]
halves = strings.Split(dothalves[1]," AS ")
} else {
halves = strings.Split(dothalves[0]," AS ")
}
adapter.simple_delete("delete_topic","topics","tid = ?")
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)
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] == ' ' && 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
adapter.simple_delete("delete_profile_reply","users_replies","rid = ?")
adapter.simple_delete("delete_forum_perms_by_forum","forums_permissions","fid = ?")
return nil
}

View File

@ -152,6 +152,53 @@ func (adapter *Mysql_Adapter) simple_update(name string, table string, set strin
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 {
if name == "" {
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 "
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
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 (
"log"
"fmt"
//"fmt"
"strconv"
"bytes"
"regexp"
@ -142,7 +142,7 @@ func route_topics(w http.ResponseWriter, r *http.Request){
/*topicItem.CreatedAt, err = relative_time(topicItem.CreatedAt)
if err != nil {
InternalError(err,w,r)
replyItem.CreatedAt = ""
}*/
topicItem.LastReplyAt, err = relative_time(topicItem.LastReplyAt)
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
last_page := int(topic.PostCount / items_per_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...
if replyItem.ActionType != "" {
switch(replyItem.ActionType) {
@ -671,9 +681,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return
}
var topic_name string
var fid, createdBy int
err = db.QueryRow("select title, parentID, createdBy from topics where tid = ?",tid).Scan(&topic_name,&fid,&createdBy)
topic, err := topics.CascadeGet(tid)
if err == sql.ErrNoRows {
PreError("Couldn't find the parent topic",w,r)
return
@ -682,7 +690,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return
}
user, ok := SimpleForumSessionCheck(w,r,fid)
user, ok := SimpleForumSessionCheck(w,r,topic.ParentID)
if !ok {
return
}
@ -710,13 +718,13 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
InternalError(err,w,r)
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 {
InternalError(err,w,r)
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 {
InternalError(err,w,r)
return
@ -733,9 +741,14 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return
}
// Alert the subscribers about this post without blocking this post from being posted
if enable_websockets {
go notify_watchers(lastId)
}
// Reload the topic...
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)
return
} else if err != nil {
@ -764,8 +777,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
var words, fid, createdBy int
err = db.QueryRow("select parentID, words, createdBy from topics where tid = ?", tid).Scan(&fid,&words,&createdBy)
topic, err := topics.CascadeGet(tid)
if err == sql.ErrNoRows {
PreError("The requested topic doesn't exist.",w,r)
return
@ -774,7 +786,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
user, ok := SimpleForumSessionCheck(w,r,fid)
user, ok := SimpleForumSessionCheck(w,r,topic.ParentID)
if !ok {
return
}
@ -783,12 +795,12 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
if createdBy == user.ID {
if topic.CreatedBy == user.ID {
LocalError("You can't like your own topics",w,r,user)
return
}
err = has_liked_topic_stmt.QueryRow(user.ID, tid).Scan(&tid)
err = has_liked_topic_stmt.QueryRow(user.ID,tid).Scan(&tid)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
return
@ -797,7 +809,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
_, err = users.CascadeGet(createdBy)
_, err = users.CascadeGet(topic.CreatedBy)
if err != nil && err == sql.ErrNoRows {
LocalError("The target user doesn't exist",w,r,user)
return
@ -819,7 +831,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
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 {
InternalError(err,w,r)
return
@ -830,18 +842,18 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
_, err = notify_one_stmt.Exec(createdBy,lastId)
_, err = notify_one_stmt.Exec(topic.CreatedBy,lastId)
if err != nil {
InternalError(err,w,r)
return
}
// 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...
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)
return
} 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
fmt.Println("Calling push_alert")
err = ws_hub.push_alert(createdBy,"like","post",user.ID,createdBy,rid)
if err != nil {
fmt.Println(err.Error())
}
_ = ws_hub.push_alert(createdBy,"like","post",user.ID,createdBy,rid)
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 tid int
var title, content, data string
var title, content string
if item_type == "reply" {
err = db.QueryRow("select tid, content from replies where rid = ?", item_id).Scan(&tid, &content)
if err == sql.ErrNoRows {
@ -1042,20 +1050,21 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, sitem_id string
return
}
err = db.QueryRow("select title, data from topics where tid = ?",tid).Scan(&title,&data)
topic, err := topics.CascadeGet(tid)
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
} else if err != nil {
InternalError(err,w,r)
return
}
title = "Reply: " + title
title = "Reply: " + topic.Title
content = content + "\n\nOriginal Post: #rid-" + strconv.Itoa(item_id)
} else if item_type == "user-reply" {
err = db.QueryRow("select uid, content from users_replies where rid = ?", item_id).Scan(&tid, &content)
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
} else if err != nil {
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)
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
} else if err != nil {
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)
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}
templates.ExecuteTemplate(w,"account-own-edit-avatar.html", pi)
}
@ -1558,7 +1567,7 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
return
}
// Emergency password reset mechanism..
// Admin password reset mechanism...
if salt == "" {
if password != real_password {
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
SetPassword(uid, password)
} else { // Normal login..
SetPassword(uid, real_password)
// 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
if err != nil {
InternalError(err,w,r)
return
}
err := bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password))
err = bcrypt.CompareHashAndPassword([]byte(real_password), []byte(password))
if err == bcrypt.ErrMismatchedHashAndPassword {
LocalError("That's not the correct password.",w,r,user)
return
@ -1582,7 +1602,6 @@ func route_login_submit(w http.ResponseWriter, r *http.Request) {
InternalError(err,w,r)
return
}
}
session, err = GenerateSafeString(sessionLength)
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_44 []byte = []byte(`</a>`)
var topic_alt_45 []byte = []byte(`<a class="action_button action_button_right hide_on_micro">`)
var topic_alt_46 []byte = []byte(` up</a>`)
var topic_alt_47 []byte = []byte(`
var topic_alt_45 []byte = []byte(`
<a class="action_button action_button_right hide_on_mobile">`)
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 style="clear:both;"></div>
</div>
`)
var topic_alt_48 []byte = []byte(`
var topic_alt_50 []byte = []byte(`
<div class="rowitem passive deletable_block editable_parent post_item `)
var topic_alt_49 []byte = []byte(`action_item`)
var topic_alt_50 []byte = []byte(`">
var topic_alt_51 []byte = []byte(`action_item`)
var topic_alt_52 []byte = []byte(`">
<div class="userinfo">
<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/`)
var topic_alt_52 []byte = []byte(`" class="the_name">`)
var topic_alt_53 []byte = []byte(`</a>
var topic_alt_54 []byte = []byte(`" class="the_name">`)
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_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_56 []byte = []byte(`<div class="tag_block"><div class="tag_pre"></div><div class="post_tag">`)
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 class="content_container" `)
var topic_alt_59 []byte = []byte(`style="margin-left: 0px;"`)
var topic_alt_60 []byte = []byte(`>
var topic_alt_61 []byte = []byte(`style="margin-left: 0px;"`)
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;">`)
var topic_alt_62 []byte = []byte(`</span>
var topic_alt_64 []byte = []byte(`</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">`)
var topic_alt_65 []byte = []byte(`</div>
var topic_alt_67 []byte = []byte(`</div>
<div class="button_container">
`)
var topic_alt_66 []byte = []byte(`<a href="/reply/like/submit/`)
var topic_alt_67 []byte = []byte(`" class="action_button">+1</a>`)
var topic_alt_68 []byte = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_69 []byte = []byte(`" class="action_button edit_item">Edit</a>`)
var topic_alt_70 []byte = []byte(`<a href="/reply/delete/submit/`)
var topic_alt_71 []byte = []byte(`" class="action_button delete_item">Delete</a>`)
var topic_alt_72 []byte = []byte(`
var topic_alt_68 []byte = []byte(`<a href="/reply/like/submit/`)
var topic_alt_69 []byte = []byte(`" class="action_button">+1</a>`)
var topic_alt_70 []byte = []byte(`<a href="/reply/edit/submit/`)
var topic_alt_71 []byte = []byte(`" class="action_button edit_item">Edit</a>`)
var topic_alt_72 []byte = []byte(`<a href="/reply/delete/submit/`)
var topic_alt_73 []byte = []byte(`" class="action_button delete_item">Delete</a>`)
var topic_alt_74 []byte = []byte(`
<a href="/report/submit/`)
var topic_alt_73 []byte = []byte(`?session=`)
var topic_alt_74 []byte = []byte(`&type=reply" class="action_button report_item">Report</a>
var topic_alt_75 []byte = []byte(`?session=`)
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_76 []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_77 []byte = []byte(`<a href="#" title="IP Address" class="action_button action_button_right ip_item hide_on_mobile">`)
var topic_alt_78 []byte = []byte(`</a>`)
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>
`)
var topic_alt_80 []byte = []byte(`
var topic_alt_84 []byte = []byte(`
</div>
<div style="clear:both;"></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;">
<form action="/reply/create/" method="post">
<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="formitem"><textarea name="reply-content" placeholder="Insert reply here"></textarea></div>
</div>

View File

@ -1,8 +1,8 @@
// 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. */
package main
import "strconv"
import "io"
import "strconv"
func init() {
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([]byte(tmpl_topic_alt_vars.Topic.Content))
w.Write(topic_alt_29)
if tmpl_topic_alt_vars.CurrentUser.Loggedin {
if tmpl_topic_alt_vars.CurrentUser.Perms.LikeItem {
w.Write(topic_alt_30)
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(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([]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 {
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([]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 != "" {
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_60)
if item.ActionType != "" {
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([]byte(strconv.Itoa(item.ID)))
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([]byte(strconv.Itoa(item.ID)))
w.Write(topic_alt_71)
}
if tmpl_topic_alt_vars.CurrentUser.Perms.DeleteReply {
w.Write(topic_alt_72)
w.Write([]byte(strconv.Itoa(item.ID)))
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([]byte(strconv.Itoa(item.LikeCount)))
w.Write([]byte(item.IpAddress))
w.Write(topic_alt_78)
}
}
w.Write(topic_alt_79)
}
w.Write([]byte(item.CreatedAt))
w.Write(topic_alt_80)
}
}
if item.LikeCount > 0 {
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([]byte(strconv.Itoa(tmpl_topic_alt_vars.Topic.ID)))
}
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)
}

View File

@ -32,14 +32,16 @@
<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>
<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.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.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}}
<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}}
{{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}}
</div>
</div><div style="clear:both;"></div>
@ -58,11 +60,14 @@
{{else}}
<div class="editable_block user_content">{{.ContentHtml}}</div>
<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.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}}
<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}}
{{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}}
</div>
{{end}}

View File

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

View File

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

20
user.go
View File

@ -195,17 +195,6 @@ func (sts *StaticUserStore) AddUnsafe(item *User) error {
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 {
sts.Lock()
delete(sts.items,id)
@ -336,8 +325,7 @@ func (sus *SqlUserStore) GetCapacity() int {
}
func (sus *SqlUserStore) GetLength() int {
// Return the total number of users registered on the forums
return 0
return 0 // Return the total number of users registered on the forums?
}
func SetPassword(uid int, password string) (error) {
@ -364,10 +352,8 @@ func SendValidationEmail(username string, email string, token string) bool {
if enable_ssl {
schema = "s"
}
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."
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
}
if wcount >= settings["megapost_min_chars"].(int) {
if wcount >= settings["megapost_min_words"].(int) {
_, err := increment_user_megaposts_stmt.Exec(-1,-1,-1,uid)
if err != nil {
return err
}
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)
if err != nil {
return err

View File

@ -108,12 +108,55 @@ func(hub *WS_Hub) push_alert(targetUser int, event string, elementType string, a
return err
}
//fmt.Println("Writing to the client")
w.Write([]byte(alert))
w.Close()
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) {
user, ok := SimpleSessionCheck(w,r)
if !ok {