Added support for Live Likes, the first step towards Live Alerts.

Moved 26 queries into the query generator.
Added support for sime replace queries in the query generator.
The alert list can only have eight entries now.
Users can no longer like their own posts.
Renamed the bigpost_min_chars setting to bigpost_min_words.
Renamed the megapost_min_chars setting to megapost_min_words.
Moved the alert building logic into it's own file.
Fixed a bug in the megapost and bigpost algorithms where only posts larger than the minimum would be considered.

Renamed the GuestCount and UserCount methods for WebSockets to guest_count and user_count.
Added the broadcast_message, push_message, and push_alert methods to the WebSockets Hub.
This commit is contained in:
Azareal 2017-06-10 08:58:15 +01:00
parent 47259fcf71
commit 81af085c7a
16 changed files with 728 additions and 454 deletions

113
alerts.go Normal file
View File

@ -0,0 +1,113 @@
package main
import "strings"
import "strconv"
import "errors"
/*
"You received a friend invite from {user}"
"{x}{mentioned you on}{user}{'s profile}"
"{x}{mentioned you in}{topic}"
"{x}{likes}{you}"
"{x}{liked}{your topic}{topic}"
"{x}{liked}{your post on}{user}{'s profile}" todo
"{x}{liked}{your post in}{topic}"
"{x}{replied to}{your post in}{topic}" todo
"{x}{replied to}{topic}"
"{x}{replied to}{your topic}{topic}"
"{x}{created a new topic}{topic}"
*/
func build_alert(event string, elementType string, actor_id int, targetUser_id int, elementID int, user User /* The current user */) (string, error) {
var targetUser *User
actor, err := users.CascadeGet(actor_id)
if err != nil {
return "", errors.New("Unable to find the actor")
}
/*if elementType != "forum" {
targetUser, err = users.CascadeGet(targetUser_id)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
}*/
if event == "friend_invite" {
return `{"msg":"You received a friend invite from {0}","sub":["` + actor.Name + `"],"path":"\/user\/`+strconv.Itoa(actor.ID)+`","avatar":"`+strings.Replace(actor.Avatar,"/","\\/",-1)+`"}`, nil
}
var act, post_act, url, area string
var start_frag, end_frag string
switch(elementType) {
case "forum":
if event == "reply" {
act = "created a new topic"
topic, err := topics.CascadeGet(elementID)
if err != nil {
return "", errors.New("Unable to find the linked topic")
}
url = build_topic_url(elementID)
area = topic.Title
// Store the forum ID in the targetUser column instead of making a new one? o.O
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now...
} else {
act = "did something in a forum"
}
case "topic":
topic, err := topics.CascadeGet(elementID)
if err != nil {
return "", errors.New("Unable to find the linked topic")
}
url = build_topic_url(elementID)
area = topic.Title
if targetUser_id == user.ID {
post_act = " your topic"
}
case "user":
targetUser, err = users.CascadeGet(elementID)
if err != nil {
return "", errors.New("Unable to find the target user")
}
area = targetUser.Name
end_frag = "'s profile"
url = build_profile_url(elementID)
case "post":
topic, err := get_topic_by_reply(elementID)
if err != nil {
return "", errors.New("Unable to find the linked reply or parent topic")
}
url = build_topic_url(topic.ID)
area = topic.Title
if targetUser_id == user.ID {
post_act = " your post in"
}
default:
return "", errors.New("Invalid elementType")
}
switch(event) {
case "like":
if elementType == "user" {
act = "likes"
end_frag = ""
if targetUser.ID == user.ID {
area = "you"
}
} else {
act = "liked"
}
case "mention":
if elementType == "user" {
act = "mentioned you on"
} else {
act = "mentioned you in"
post_act = ""
}
case "reply": act = "replied to"
}
return `{"msg":"{0} ` + start_frag + act + post_act + ` {1}` + end_frag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `","avatar":"` + actor.Avatar + `"}`, nil
}

View File

@ -40,7 +40,17 @@ var create_reply_stmt *sql.Stmt
var create_action_reply_stmt *sql.Stmt
var create_like_stmt *sql.Stmt
var add_activity_stmt *sql.Stmt
var notify_one_stmt *sql.Stmt
var register_stmt *sql.Stmt
var create_profile_reply_stmt *sql.Stmt
var create_forum_stmt *sql.Stmt
var add_forum_perms_to_forum_stmt *sql.Stmt
var add_plugin_stmt *sql.Stmt
var add_theme_stmt *sql.Stmt
var create_group_stmt *sql.Stmt
var add_modlog_entry_stmt *sql.Stmt
var add_adminlog_entry_stmt *sql.Stmt
var add_forum_perms_to_group_stmt *sql.Stmt
var add_replies_to_topic_stmt *sql.Stmt
var remove_replies_from_topic_stmt *sql.Stmt
var add_topics_to_forum_stmt *sql.Stmt
@ -60,6 +70,22 @@ var set_avatar_stmt *sql.Stmt
var set_username_stmt *sql.Stmt
var change_group_stmt *sql.Stmt
var activate_user_stmt *sql.Stmt
var update_user_level_stmt *sql.Stmt
var increment_user_score_stmt *sql.Stmt
var increment_user_posts_stmt *sql.Stmt
var increment_user_bigposts_stmt *sql.Stmt
var increment_user_megaposts_stmt *sql.Stmt
var increment_user_topics_stmt *sql.Stmt
var edit_profile_reply_stmt *sql.Stmt
var delete_forum_stmt *sql.Stmt
var update_forum_stmt *sql.Stmt
var update_setting_stmt *sql.Stmt
var update_plugin_stmt *sql.Stmt
var update_theme_stmt *sql.Stmt
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
func gen_mysql() (err error) {
if debug {
@ -270,12 +296,72 @@ func gen_mysql() (err error) {
return err
}
log.Print("Preparing notify_one statement.")
notify_one_stmt, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) VALUES (?,?)")
if err != nil {
return err
}
log.Print("Preparing register statement.")
register_stmt, err = db.Prepare("INSERT INTO `users`(`name`,`email`,`password`,`salt`,`group`,`is_super_admin`,`session`,`active`,`message`) VALUES (?,?,?,?,?,0,?,?,'')")
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 create_forum statement.")
create_forum_stmt, err = db.Prepare("INSERT INTO `forums`(`name`,`desc`,`active`,`preset`) VALUES (?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing add_forum_perms_to_forum statement.")
add_forum_perms_to_forum_stmt, err = db.Prepare("INSERT INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing add_plugin statement.")
add_plugin_stmt, err = db.Prepare("INSERT INTO `plugins`(`uname`,`active`) VALUES (?,?)")
if err != nil {
return err
}
log.Print("Preparing add_theme statement.")
add_theme_stmt, err = db.Prepare("INSERT INTO `themes`(`uname`,`default`) VALUES (?,?)")
if err != nil {
return err
}
log.Print("Preparing create_group statement.")
create_group_stmt, err = db.Prepare("INSERT INTO `users_groups`(`name`,`tag`,`is_admin`,`is_mod`,`is_banned`,`permissions`) VALUES (?,?,?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing add_modlog_entry statement.")
add_modlog_entry_stmt, err = db.Prepare("INSERT INTO `moderation_logs`(`action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt`) VALUES (?,?,?,?,?,NOW())")
if err != nil {
return err
}
log.Print("Preparing add_adminlog_entry statement.")
add_adminlog_entry_stmt, err = db.Prepare("INSERT INTO `administration_logs`(`action`,`elementID`,`elementType`,`ipaddress`,`actorID`,`doneAt`) VALUES (?,?,?,?,?,NOW())")
if err != nil {
return err
}
log.Print("Preparing add_forum_perms_to_group statement.")
add_forum_perms_to_group_stmt, err = db.Prepare("REPLACE INTO `forums_permissions`(`gid`,`fid`,`preset`,`permissions`) VALUES (?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing add_replies_to_topic statement.")
add_replies_to_topic_stmt, err = db.Prepare("UPDATE `topics` SET `postCount` = `postCount` + ?,`lastReplyAt` = NOW() WHERE `tid` = ? ")
if err != nil {
@ -389,6 +475,102 @@ func gen_mysql() (err error) {
if err != nil {
return err
}
log.Print("Preparing update_user_level statement.")
update_user_level_stmt, err = db.Prepare("UPDATE `users` SET `level` = ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing increment_user_score statement.")
increment_user_score_stmt, err = db.Prepare("UPDATE `users` SET `score` = `score` + ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing increment_user_posts statement.")
increment_user_posts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing increment_user_bigposts statement.")
increment_user_bigposts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ?,`bigposts` = `bigposts` + ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing increment_user_megaposts statement.")
increment_user_megaposts_stmt, err = db.Prepare("UPDATE `users` SET `posts` = `posts` + ?,`bigposts` = `bigposts` + ?,`megaposts` = `megaposts` + ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing increment_user_topics statement.")
increment_user_topics_stmt, err = db.Prepare("UPDATE `users` SET `topics` = `topics` + ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing edit_profile_reply statement.")
edit_profile_reply_stmt, err = db.Prepare("UPDATE `users_replies` SET `content` = ?,`parsed_content` = ? WHERE `rid` = ? ")
if err != nil {
return err
}
log.Print("Preparing delete_forum statement.")
delete_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = '',`active` = 0 WHERE `fid` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_forum statement.")
update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_setting statement.")
update_setting_stmt, err = db.Prepare("UPDATE `settings` SET `content` = ? WHERE `name` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_plugin statement.")
update_plugin_stmt, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_theme statement.")
update_theme_stmt, err = db.Prepare("UPDATE `themes` SET `default` = ? WHERE `uname` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_user statement.")
update_user_stmt, err = db.Prepare("UPDATE `users` SET `name` = ?,`email` = ?,`group` = ? WHERE `uid` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_group_perms statement.")
update_group_perms_stmt, err = db.Prepare("UPDATE `users_groups` SET `permissions` = ? WHERE `gid` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_group_rank statement.")
update_group_rank_stmt, err = db.Prepare("UPDATE `users_groups` SET `is_admin` = ?,`is_mod` = ?,`is_banned` = ? WHERE `gid` = ? ")
if err != nil {
return err
}
log.Print("Preparing update_group statement.")
update_group_stmt, err = db.Prepare("UPDATE `users_groups` SET `name` = ?,`tag` = ? WHERE `gid` = ? ")
if err != nil {
return err
}
return nil
}

View File

@ -1,34 +1,40 @@
package main
import "os"
import "fmt"
import "log"
import "bytes"
import "strings"
import "strconv"
import "math/rand"
import "testing"
import "net/http"
import "net/http/httptest"
import "io/ioutil"
import "database/sql"
import "runtime/pprof"
//import _ "github.com/go-sql-driver/mysql"
//import "github.com/erikstmartin/go-testdb"
//import "github.com/husobee/vestigo"
import (
"os"
"fmt"
"log"
"bytes"
"strings"
"strconv"
//"math/rand"
"testing"
"time"
"net/http"
"net/http/httptest"
"io/ioutil"
"database/sql"
"runtime/pprof"
//_ "github.com/go-sql-driver/mysql"
//"github.com/erikstmartin/go-testdb"
//"github.com/husobee/vestigo"
)
var db_test *sql.DB
var db_prod *sql.DB
var db_test, db_prod *sql.DB
var gloinited bool = false
func gloinit() {
debug = false
nogrouplog = true
//nogrouplog = true
// init_database is a little noisy for a benchmark
//discard := ioutil.Discard
//log.SetOutput(discard)
startTime = time.Now()
timeLocation = startTime.Location()
init_themes()
err := init_database()
if err != nil {
@ -352,7 +358,7 @@ func BenchmarkForumsGuestRouteParallel(b *testing.B) {
}
func BenchmarkRoutesSerial(b *testing.B) {
/*func BenchmarkRoutesSerial(b *testing.B) {
b.ReportAllocs()
admin, err := users.CascadeGet(1)
if err != nil {
@ -404,13 +410,13 @@ func BenchmarkRoutesSerial(b *testing.B) {
gloinit()
}
/*f, err := os.Create("routes_bench_cpu.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)*/
//defer pprof.StopCPUProfile()
//pprof.StopCPUProfile()
//f, err := os.Create("routes_bench_cpu.prof")
//if err != nil {
// log.Fatal(err)
//}
//pprof.StartCPUProfile(f)
///defer pprof.StopCPUProfile()
///pprof.StopCPUProfile()
b.Run("static_recorder", func(b *testing.B) {
for i := 0; i < b.N; i++ {
@ -490,11 +496,11 @@ func BenchmarkRoutesSerial(b *testing.B) {
}
})
b.Run("forums_guest_recorder", func(b *testing.B) {
/*f, err := os.Create("routes_bench_forums_cpu_2.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)*/
//f, err := os.Create("routes_bench_forums_cpu_2.prof")
//if err != nil {
// log.Fatal(err)
//}
//pprof.StartCPUProfile(f)
for i := 0; i < b.N; i++ {
//forums_w.Code = 200
forums_w.Body.Reset()
@ -508,11 +514,11 @@ func BenchmarkRoutesSerial(b *testing.B) {
}
b.Run("topic_admin_recorder_with_plugins", func(b *testing.B) {
/*f, err := os.Create("routes_bench_topic_cpu.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)*/
//f, err := os.Create("routes_bench_topic_cpu.prof")
//if err != nil {
// log.Fatal(err)
//}
//pprof.StartCPUProfile(f)
for i := 0; i < b.N; i++ {
//topic_w.Code = 200
topic_w.Body.Reset()
@ -525,11 +531,11 @@ func BenchmarkRoutesSerial(b *testing.B) {
//pprof.StopCPUProfile()
})
b.Run("topic_guest_recorder_with_plugins", func(b *testing.B) {
/*f, err := os.Create("routes_bench_topic_cpu_2.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)*/
//f, err := os.Create("routes_bench_topic_cpu_2.prof")
//if err != nil {
// log.Fatal(err)
//}
//pprof.StartCPUProfile(f)
for i := 0; i < b.N; i++ {
//topic_w.Code = 200
topic_w.Body.Reset()
@ -573,11 +579,11 @@ func BenchmarkRoutesSerial(b *testing.B) {
}
})
b.Run("forums_guest_recorder_with_plugins", func(b *testing.B) {
/*f, err := os.Create("routes_bench_forums_cpu_2.prof")
if err != nil {
log.Fatal(err)
}
pprof.StartCPUProfile(f)*/
//f, err := os.Create("routes_bench_forums_cpu_2.prof")
//if err != nil {
// log.Fatal(err)
//}
//pprof.StartCPUProfile(f)
for i := 0; i < b.N; i++ {
//forums_w.Code = 200
forums_w.Body.Reset()
@ -585,7 +591,7 @@ func BenchmarkRoutesSerial(b *testing.B) {
}
//pprof.StopCPUProfile()
})
}
}*/
func BenchmarkQueryTopicParallel(b *testing.B) {
b.ReportAllocs()
@ -1224,7 +1230,7 @@ func TestStaticRoute(t *testing.T) {
fmt.Println("No problems found in the static route!")
}
func TestTopicAdminRoute(t *testing.T) {
/*func TestTopicAdminRoute(t *testing.T) {
if !gloinited {
gloinit()
}
@ -1256,9 +1262,9 @@ func TestTopicAdminRoute(t *testing.T) {
panic("HTTP Error!")
}
fmt.Println("No problems found in the topic-admin route!")
}
}*/
func TestTopicGuestRoute(t *testing.T) {
/*func TestTopicGuestRoute(t *testing.T) {
if !gloinited {
gloinit()
}
@ -1276,7 +1282,7 @@ func TestTopicGuestRoute(t *testing.T) {
panic("HTTP Error!")
}
fmt.Println("No problems found in the topic-guest route!")
}
}*/
func TestForumsAdminRoute(t *testing.T) {
if !gloinited {
@ -1331,7 +1337,7 @@ func TestForumsGuestRoute(t *testing.T) {
fmt.Println("No problems found in the forums-guest route!")
}
func TestForumAdminRoute(t *testing.T) {
/*func TestForumAdminRoute(t *testing.T) {
if !gloinited {
gloinit()
}
@ -1362,9 +1368,9 @@ func TestForumAdminRoute(t *testing.T) {
panic("HTTP Error!")
}
fmt.Println("No problems found in the forum-admin route!")
}
}*/
func TestForumGuestRoute(t *testing.T) {
/*func TestForumGuestRoute(t *testing.T) {
if !gloinited {
gloinit()
}
@ -1382,7 +1388,7 @@ func TestForumGuestRoute(t *testing.T) {
panic("HTTP Error!")
}
fmt.Println("No problems found in the forum-guest route!")
}
}*/
/*func TestAlerts(t *testing.T) {
if !gloinited {

193
mysql.go
View File

@ -16,49 +16,23 @@ 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 notify_one_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 update_user_level_stmt *sql.Stmt
var increment_user_score_stmt *sql.Stmt
var increment_user_posts_stmt *sql.Stmt
var increment_user_bigposts_stmt *sql.Stmt
var increment_user_megaposts_stmt *sql.Stmt
var increment_user_topics_stmt *sql.Stmt
var create_profile_reply_stmt *sql.Stmt
var edit_profile_reply_stmt *sql.Stmt
var delete_profile_reply_stmt *sql.Stmt
var create_forum_stmt *sql.Stmt
var delete_forum_stmt *sql.Stmt
var update_forum_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_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
var add_forum_perms_to_group_stmt *sql.Stmt
//var forum_perm_exists_for_group_stmt *sql.Stmt
var update_forum_perms_for_group_stmt *sql.Stmt
var update_setting_stmt *sql.Stmt
var add_plugin_stmt *sql.Stmt
var update_plugin_stmt *sql.Stmt
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 create_group_stmt *sql.Stmt
var add_theme_stmt *sql.Stmt
var update_theme_stmt *sql.Stmt
var add_modlog_entry_stmt *sql.Stmt
var add_adminlog_entry_stmt *sql.Stmt
var todays_post_count_stmt *sql.Stmt
var todays_topic_count_stmt *sql.Stmt
var todays_report_count_stmt *sql.Stmt
@ -112,12 +86,6 @@ func init_database() (err error) {
return err
}
log.Print("Preparing notify_one statement.")
notify_one_stmt, err = db.Prepare("INSERT INTO activity_stream_matches(watcher,asid) VALUES(?,?)")
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 {
@ -137,7 +105,13 @@ func init_database() (err error) {
}
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` = ?")
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 {
return err
}
log.Print("Preparing get_activity_count_by_watcher statement.")
get_activity_count_by_watcher_stmt, err = db.Prepare("SELECT count(*) 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` = ?")
if err != nil {
return err
}
@ -160,79 +134,12 @@ func init_database() (err error) {
return err
}
log.Print("Preparing update_user_level statement.")
update_user_level_stmt, err = db.Prepare("UPDATE users SET level = ? WHERE uid = ?")
if err != nil {
return err
}
log.Print("Preparing increment_user_score statement.")
increment_user_score_stmt, err = db.Prepare("UPDATE users SET score = score + ? WHERE uid = ?")
if err != nil {
return err
}
log.Print("Preparing increment_user_posts statement.")
increment_user_posts_stmt, err = db.Prepare("UPDATE users SET posts = posts + ? WHERE uid = ?")
if err != nil {
return err
}
log.Print("Preparing increment_user_bigposts statement.")
increment_user_bigposts_stmt, err = db.Prepare("UPDATE users SET posts = posts + ?, bigposts = bigposts + ? WHERE uid = ?")
if err != nil {
return err
}
log.Print("Preparing increment_user_megaposts statement.")
increment_user_megaposts_stmt, err = db.Prepare("UPDATE users SET posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ? WHERE uid = ?")
if err != nil {
return err
}
log.Print("Preparing increment_user_topics statement.")
increment_user_topics_stmt, err = db.Prepare("UPDATE users SET topics = topics + ? WHERE uid = ?")
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 edit_profile_reply statement.")
edit_profile_reply_stmt, err = db.Prepare("UPDATE users_replies SET content = ?, parsed_content = ? WHERE rid = ?")
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 create_forum statement.")
create_forum_stmt, err = db.Prepare("INSERT INTO forums(`name`,`desc`,`active`,`preset`) VALUES(?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing delete_forum statement.")
//delete_forum_stmt, err = db.Prepare("delete from forums where fid = ?")
delete_forum_stmt, err = db.Prepare("update forums set name= '', active = 0 where fid = ?")
if err != nil {
return err
}
log.Print("Preparing update_forum statement.")
update_forum_stmt, err = db.Prepare("update forums set `name` = ?, `desc` = ?, `active` = ?, `preset` = ? where fid = ?")
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 {
@ -251,12 +158,6 @@ func init_database() (err error) {
return err
}
log.Print("Preparing add_forum_perms_to_forum statement.")
add_forum_perms_to_forum_stmt, err = db.Prepare("INSERT INTO forums_permissions(gid,fid,preset,permissions) VALUES(?,?,?,?)")
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 {
@ -275,84 +176,6 @@ func init_database() (err error) {
return err
}
log.Print("Preparing add_forum_perms_to_group statement.")
add_forum_perms_to_group_stmt, err = db.Prepare("REPLACE INTO forums_permissions(gid,fid,preset,permissions) VALUES(?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing update_setting statement.")
update_setting_stmt, err = db.Prepare("UPDATE settings SET content = ? WHERE name = ?")
if err != nil {
return err
}
log.Print("Preparing add_plugin statement.")
add_plugin_stmt, err = db.Prepare("INSERT INTO plugins(uname,active) VALUES(?,?)")
if err != nil {
return err
}
log.Print("Preparing update_plugin statement.")
update_plugin_stmt, err = db.Prepare("UPDATE plugins SET active = ? WHERE uname = ?")
if err != nil {
return err
}
log.Print("Preparing add_theme statement.")
add_theme_stmt, err = db.Prepare("INSERT INTO `themes`(`uname`,`default`) VALUES(?,?)")
if err != nil {
return err
}
log.Print("Preparing update_theme statement.")
update_theme_stmt, err = db.Prepare("update `themes` set `default` = ? where `uname` = ?")
if err != nil {
return err
}
log.Print("Preparing update_user statement.")
update_user_stmt, err = db.Prepare("update `users` set `name` = ?,`email` = ?,`group` = ? where `uid` = ?")
if err != nil {
return err
}
log.Print("Preparing update_group_rank statement.")
update_group_perms_stmt, err = db.Prepare("update `users_groups` set `permissions` = ? where `gid` = ?")
if err != nil {
return err
}
log.Print("Preparing update_group_rank statement.")
update_group_rank_stmt, err = db.Prepare("update `users_groups` set `is_admin` = ?, `is_mod` = ?, `is_banned` = ? where `gid` = ?")
if err != nil {
return err
}
log.Print("Preparing update_group statement.")
update_group_stmt, err = db.Prepare("update `users_groups` set `name` = ?, `tag` = ? where `gid` = ?")
if err != nil {
return err
}
log.Print("Preparing create_group statement.")
create_group_stmt, err = db.Prepare("INSERT INTO users_groups(name,tag,is_admin,is_mod,is_banned,permissions) VALUES(?,?,?,?,?,?)")
if err != nil {
return err
}
log.Print("Preparing add_modlog_entry statement.")
add_modlog_entry_stmt, err = db.Prepare("INSERT INTO moderation_logs(action,elementID,elementType,ipaddress,actorID,doneAt) VALUES(?,?,?,?,?,NOW())")
if err != nil {
return err
}
log.Print("Preparing add_adminlog_entry statement.")
add_adminlog_entry_stmt, err = db.Prepare("INSERT INTO moderation_logs(action,elementID,elementType,ipaddress,actorID,doneAt) VALUES(?,?,?,?,?,NOW())")
if err != nil {
return err
}
log.Print("Preparing todays_post_count statement.")
todays_post_count_stmt, err = db.Prepare("select count(*) from replies where createdAt BETWEEN (now() - interval 1 day) and now()")
if err != nil {

View File

@ -193,8 +193,8 @@ CREATE TABLE `administration_logs`(
INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool');
INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3');
INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_chars','250','int');
INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_chars','1000','int');
INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int');
INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1);
INSERT INTO users(`name`,`password`,`email`,`group`,`is_super_admin`,`createdAt`,`lastActiveAt`,`message`,`last_ip`)

View File

@ -2,21 +2,35 @@
package main
import "errors"
import "net/http"
var ws_hub WS_Hub
var ws_nouser error = errors.New("This user isn't connected via WebSockets")
type WS_Hub struct
{
}
func (_ *WS_Hub) GuestCount() int {
func (_ *WS_Hub) guest_count() int {
return 0
}
func (_ *WS_Hub) UserCount() int {
func (_ *WS_Hub) user_count() int {
return 0
}
func (hub *WS_Hub) broadcast_message(_ string) error {
return nil
}
func (hub *WS_Hub) push_message(_ int, _ string) error {
return ws_nouser
}
func(hub *WS_Hub) push_alert(_ int, _ string, _ string, _ int, _ int, _ int) error {
return ws_nouser
}
func route_websockets(_ http.ResponseWriter, _ *http.Request) {
}

View File

@ -137,8 +137,8 @@ func route_panel(w http.ResponseWriter, r *http.Request){
}
if enable_websockets {
uonline := ws_hub.UserCount()
gonline := ws_hub.GuestCount()
uonline := ws_hub.user_count()
gonline := ws_hub.guest_count()
totonline := uonline + gonline
var onlineColour string
@ -489,7 +489,6 @@ func route_panel_forums_edit_perms_submit(w http.ResponseWriter, r *http.Request
return
}
//_, err = update_forum_perms_for_group_stmt.Exec(perm_preset,perms,gid,fid)
_, err = add_forum_perms_to_group_stmt.Exec(gid,fid,perm_preset,perms)
if err != nil {
InternalErrorJSQ(err,w,r,is_js)

View File

@ -1,4 +1,6 @@
var form_vars = {};
var alertList = [];
var alertCount = 0;
function post_link(event)
{
@ -17,7 +19,6 @@ function load_alerts(menu_alerts)
url:'/api/?action=get&module=alerts&format=json',
success: function(data) {
if("errmsg" in data) {
//console.log(data.errmsg);
menu_alerts.find(".alertList").html("<div class='alertItem'>"+data.errmsg+"</div>");
return;
}
@ -36,12 +37,14 @@ function load_alerts(menu_alerts)
}
}
if("avatar" in msg)
{
if("avatar" in msg) {
alist += "<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><a class='text' href=\""+msg.path+"\">"+mmsg+"</a></div>";
alertList.push("<div class='alertItem withAvatar' style='background-image:url(\""+msg.avatar+"\");'><a class='text' href=\""+msg.path+"\">"+mmsg+"</a></div>");
anyAvatar = true
} else {
alist += "<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>";
alertList.push("<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>");
}
else alist += "<div class='alertItem'><a href=\""+msg.path+"\" class='text'>"+mmsg+"</a></div>";
//console.log(msg);
//console.log(mmsg);
}
@ -52,14 +55,19 @@ function load_alerts(menu_alerts)
//if(anyAvatar) menu_alerts.addClass("hasAvatars");
}
menu_alerts.find(".alertList").html(alist);
if(data.msgs.length != 0) menu_alerts.find(".alert_counter").text(data.msgs.length);
if(data.msgCount != 0) menu_alerts.find(".alert_counter").text(data.msgCount);
alertCount = data.msgCount;
},
error: function(magic,theStatus,error) {
try {
var data = JSON.parse(magic.responseText);
if("errmsg" in data) errtxt = data.errmsg;
else errtxt = "Unable to get the alerts";
} catch(e) { errtxt = "Unable to get the alerts"; }
} catch(err) {
errtxt = "Unable to get the alerts";
console.log(magic.responseText);
console.log(err);
}
menu_alerts.find(".alertList").html("<div class='alertItem'>"+errtxt+"</div>");
}
});
@ -81,9 +89,7 @@ $(document).ready(function(){
lastN++;
}
}
if(data.length > lastIndex) {
out[out.length - 1] += data.substring(lastIndex);
}
if(data.length > lastIndex) out[out.length - 1] += data.substring(lastIndex);
return out;
}
@ -96,8 +102,37 @@ $(document).ready(function(){
conn = false;
}
conn.onmessage = function(event) {
//console.log("WS_Message:");
//console.log(event.data);
//console.log("WS_Message: ",event.data);
if(event.data[0] == "{") {
try {
var data = JSON.parse(event.data);
} catch(err) { console.log(err); }
if ("msg" in data) {
var msg = data.msg
if("sub" in data) {
for(var i = 0; i < data.sub.length; i++) {
msg = msg.replace("\{"+i+"\}", data.sub[i]);
}
}
if("avatar" in data) alertList.push("<div class='alertItem withAvatar' style='background-image:url(\""+data.avatar+"\");'><a class='text' href=\""+data.path+"\">"+msg+"</a></div>");
else alertList.push("<div class='alertItem'><a href=\""+data.path+"\" class='text'>"+msg+"</a></div>");
if(alertList.length > 8) alertList.shift();
//console.log("post alertList",alertList);
alertCount++;
var alist = ""
for (var i = 0; i < alertList.length; i++) {
alist += alertList[i];
}
//console.log(alist);
$("#general_alerts").find(".alertList").html(alist); // Add support for other alert feeds like PM Alerts
$("#general_alerts").find(".alert_counter").text(alertCount);
}
}
var messages = event.data.split('\r');
for(var i = 0; i < messages.length; i++) {
//console.log("Message:");
@ -266,9 +301,6 @@ $(document).ready(function(){
var newContent = $(this).find('input').eq(0).val();
this.innerHTML = newContent;
}
//console.log("field_name",field_name);
//console.log("field_type",field_type);
//console.log("newContent",newContent);
this.setAttribute("data-value",newContent);
out_data[field_name] = newContent;
});

View File

@ -61,7 +61,7 @@ type DB_Setter struct {
type DB_Adapter interface {
get_name() string
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_select(string,string,string,string,string/*,int,int*/) error
simple_left_join(string,string,string,string,string,string,string/*,int,int*/) error
@ -79,6 +79,30 @@ func main() {
}
func write_statements(adapter DB_Adapter) error {
err := write_selects(adapter)
if err != nil {
return err
}
err = write_joins(adapter)
if err != nil {
return err
}
err = write_inserts(adapter)
if err != nil {
return err
}
err = write_replaces(adapter)
if err != nil {
return err
}
err = write_updates(adapter)
if err != nil {
return err
}
return nil
}
func write_selects(adapter DB_Adapter) error {
// url_prefix and url_name will be removed from this query in a later commit
adapter.simple_select("get_user","users","name, group, is_super_admin, avatar, message, url_prefix, url_name, level","uid = ?","")
@ -124,7 +148,10 @@ func write_statements(adapter DB_Adapter) error {
adapter.simple_select("get_emails_by_user","emails","email, validated","uid = ?","")
adapter.simple_select("get_topic_basic","topics","title, content","tid = ?","")
return nil
}
func write_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 = ?","")
@ -136,7 +163,10 @@ func write_statements(adapter DB_Adapter) error {
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_inserts(adapter DB_Adapter) error {
adapter.simple_insert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,ipaddress,words,createdBy","?,?,?,?,NOW(),NOW(),?,?,?")
adapter.simple_insert("create_report","topics","title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class","?,?,?,NOW(),NOW(),?,?,1,'report'")
@ -149,11 +179,39 @@ func write_statements(adapter DB_Adapter) error {
adapter.simple_insert("add_activity","activity_stream","actor,targetUser,event,elementType,elementID","?,?,?,?,?")
adapter.simple_insert("notify_one","activity_stream_matches","watcher,asid","?,?")
// Add an admin version of register_stmt with more flexibility?
// 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("create_profile_reply","users_replies","uid,content,parsed_content,createdAt,createdBy","?,?,?,NOW(),?")
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_plugin","plugins","uname,active","?,?")
adapter.simple_insert("add_theme","themes","uname,default","?,?")
adapter.simple_insert("create_group","users_groups","name, tag, is_admin, is_mod, is_banned, permissions","?,?,?,?,?,?")
adapter.simple_insert("add_modlog_entry","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,NOW()")
adapter.simple_insert("add_adminlog_entry","administration_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,NOW()")
return nil
}
func write_replaces(adapter DB_Adapter) error {
adapter.simple_replace("add_forum_perms_to_group","forums_permissions","gid,fid,preset,permissions","?,?,?,?")
return nil
}
func write_updates(adapter DB_Adapter) error {
adapter.simple_update("add_replies_to_topic","topics","postCount = postCount + ?, lastReplyAt = NOW()","tid = ?")
adapter.simple_update("remove_replies_from_topic","topics","postCount = postCount - ?","tid = ?")
@ -192,6 +250,39 @@ func write_statements(adapter DB_Adapter) error {
adapter.simple_update("activate_user","users","active = 1","uid = ?")
adapter.simple_update("update_user_level","users","level = ?","uid = ?")
adapter.simple_update("increment_user_score","users","score = score + ?","uid = ?")
adapter.simple_update("increment_user_posts","users","posts = posts + ?","uid = ?")
adapter.simple_update("increment_user_bigposts","users","posts = posts + ?, bigposts = bigposts + ?","uid = ?")
adapter.simple_update("increment_user_megaposts","users","posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?","uid = ?")
adapter.simple_update("increment_user_topics","users","topics = topics + ?","uid = ?")
adapter.simple_update("edit_profile_reply","users_replies","content = ?, parsed_content = ?","rid = ?")
//delete_forum_stmt, err = db.Prepare("delete from forums where fid = ?")
adapter.simple_update("delete_forum","forums","name= '', active = 0","fid = ?")
adapter.simple_update("update_forum","forums","name = ?, desc = ?, active = ?, preset = ?","fid = ?")
adapter.simple_update("update_setting","settings","content = ?","name = ?")
adapter.simple_update("update_plugin","plugins","active = ?","uname = ?")
adapter.simple_update("update_theme","themes","default = ?","uname = ?")
adapter.simple_update("update_user","users","name = ?, email = ?, group = ?","uid = ?")
adapter.simple_update("update_group_perms","users_groups","permissions = ?","gid = ?")
adapter.simple_update("update_group_rank","users_groups","is_admin = ?, is_mod = ?, is_banned = ?","gid = ?")
adapter.simple_update("update_group","users_groups","name = ?, tag = ?","gid = ?")
return nil
}

View File

@ -58,6 +58,44 @@ func (adapter *Mysql_Adapter) simple_insert(name string, table string, columns s
return nil
}
func (adapter *Mysql_Adapter) simple_replace(name string, table string, columns string, fields 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 len(columns) == 0 {
return errors.New("No columns found for simple_insert")
}
if len(fields) == 0 {
return errors.New("No input data found for simple_insert")
}
var querystr string = "REPLACE INTO `" + table + "`("
// Escape the column names, just in case we've used a reserved keyword
for _, column := range _process_columns(columns) {
if column.Type == "function" {
querystr += column.Left + ","
} else {
querystr += "`" + column.Left + "`,"
}
}
// Remove the trailing comma
querystr = querystr[0:len(querystr) - 1]
querystr += ") VALUES ("
for _, field := range _process_fields(fields) {
querystr += field.Name + ","
}
querystr = querystr[0:len(querystr) - 1]
adapter.write_statement(name,querystr + ")")
return nil
}
func (adapter *Mysql_Adapter) simple_update(name string, table string, set string, where string) error {
if name == "" {
return errors.New("You need a name for this statement")

165
routes.go
View File

@ -3,7 +3,7 @@ package main
import (
"log"
// "fmt"
"fmt"
"strconv"
"bytes"
"regexp"
@ -705,12 +705,12 @@ func route_create_reply(w http.ResponseWriter, r *http.Request) {
return
}
_, err = add_replies_to_topic_stmt.Exec(1, tid)
_, err = add_replies_to_topic_stmt.Exec(1,tid)
if err != nil {
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_name,tid,user.Name,user.ID,1)
if err != nil {
InternalError(err,w,r)
return
@ -783,6 +783,11 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
if 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)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
@ -801,7 +806,6 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
//score := words_to_score(words,true)
score := 1
_, err = create_like_stmt.Exec(score,tid,"topics",user.ID)
if err != nil {
@ -826,17 +830,15 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) {
return
}
/*_, err = notify_watchers_stmt.Exec(lastId)
if err != nil {
InternalError(err,w,r)
return
}*/
_, err = notify_one_stmt.Exec(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)
// Reload the topic...
err = topics.Load(tid)
if err != nil && err != sql.ErrNoRows {
@ -892,6 +894,11 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) {
return
}
if createdBy == user.ID {
LocalError("You can't like your own replies",w,r,user)
return
}
err = has_liked_reply_stmt.QueryRow(user.ID, rid).Scan(&rid)
if err != nil && err != sql.ErrNoRows {
InternalError(err,w,r)
@ -910,7 +917,6 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) {
return
}
//score := words_to_score(words,false)
score := 1
_, err = create_like_stmt.Exec(score,rid,"replies",user.ID)
if err != nil {
@ -941,6 +947,13 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) {
return
}
// 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())
}
http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther)
}
@ -1764,12 +1777,18 @@ func route_api(w http.ResponseWriter, r *http.Request) {
return
}
var msglist string
var asid, actor_id, targetUser_id int
var event, elementType string
var elementID int
//---
var targetUser *User
var msglist, event, elementType string
var asid, actor_id, targetUser_id, elementID int
var msgCount int
err = get_activity_count_by_watcher_stmt.QueryRow(user.ID).Scan(&msgCount)
if err == sql.ErrNoRows {
PreError("Couldn't find the parent topic",w,r)
return
} else if err != nil {
InternalError(err,w,r)
return
}
rows, err := get_activity_feed_by_watcher_stmt.Query(user.ID)
if err != nil {
@ -1783,116 +1802,12 @@ func route_api(w http.ResponseWriter, r *http.Request) {
InternalErrorJS(err,w,r)
return
}
actor, err := users.CascadeGet(actor_id)
res, err := build_alert(event, elementType, actor_id, targetUser_id, elementID, user)
if err != nil {
LocalErrorJS("Unable to find the actor",w,r)
LocalErrorJS(err.Error(),w,r)
return
}
/*if elementType != "forum" {
targetUser, err = users.CascadeGet(targetUser_id)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
}*/
if event == "friend_invite" {
msglist += `{"msg":"You received a friend invite from {0}","sub":["` + actor.Name + `"],"path":"\/user\/`+strconv.Itoa(actor.ID)+`","avatar":"`+strings.Replace(actor.Avatar,"/","\\/",-1)+`"},`
continue
}
/*
"You received a friend invite from {user}"
"{x}{mentioned you on}{user}{'s profile}"
"{x}{mentioned you in}{topic}"
"{x}{likes}{you}"
"{x}{liked}{your topic}{topic}"
"{x}{liked}{your post on}{user}{'s profile}" todo
"{x}{liked}{your post in}{topic}"
"{x}{replied to}{your post in}{topic}" todo
"{x}{replied to}{topic}"
"{x}{replied to}{your topic}{topic}"
"{x}{created a new topic}{topic}"
*/
var act, post_act, url, area string
var start_frag, end_frag string
switch(elementType) {
case "forum":
if event == "reply" {
act = "created a new topic"
topic, err := topics.CascadeGet(elementID)
if err != nil {
LocalErrorJS("Unable to find the linked topic",w,r)
return
}
url = build_topic_url(elementID)
area = topic.Title
// Store the forum ID in the targetUser column instead of making a new one? o.O
// Add an additional column for extra information later on when we add the ability to link directly to posts. We don't need the forum data for now..
} else {
act = "did something in a forum"
}
case "topic":
topic, err := topics.CascadeGet(elementID)
if err != nil {
LocalErrorJS("Unable to find the linked topic",w,r)
return
}
url = build_topic_url(elementID)
area = topic.Title
if targetUser_id == user.ID {
post_act = " your topic"
}
case "user":
targetUser, err = users.CascadeGet(elementID)
if err != nil {
LocalErrorJS("Unable to find the target user",w,r)
return
}
area = targetUser.Name
end_frag = "'s profile"
url = build_profile_url(elementID)
case "post":
topic, err := get_topic_by_reply(elementID)
if err != nil {
LocalErrorJS("Unable to find the target reply or parent topic",w,r)
return
}
url = build_topic_url(topic.ID)
area = topic.Title
if targetUser_id == user.ID {
post_act = " your post in"
}
default:
LocalErrorJS("Invalid elementType",w,r)
}
switch(event) {
case "like":
if elementType == "user" {
act = "likes"
end_frag = ""
if targetUser.ID == user.ID {
area = "you"
}
} else {
act = "liked"
}
case "mention":
if elementType == "user" {
act = "mentioned you on"
} else {
act = "mentioned you in"
post_act = ""
}
case "reply": act = "replied to"
}
msglist += `{"msg":"{0} ` + start_frag + act + post_act + ` {1}` + end_frag + `","sub":["` + actor.Name + `","` + area + `"],"path":"` + url + `","avatar":"` + actor.Avatar + `"},`
msglist += res + ","
}
err = rows.Err()
@ -1905,8 +1820,8 @@ func route_api(w http.ResponseWriter, r *http.Request) {
if len(msglist) != 0 {
msglist = msglist[0:len(msglist)-1]
}
w.Write([]byte(`{"msgs":[`+msglist+`]}`))
//fmt.Println(`{"msgs":[`+msglist+`]}`)
w.Write([]byte(`{"msgs":[` + msglist + `],"msgCount":` + strconv.Itoa(msgCount) + `}`))
//fmt.Println(`{"msgs":[` + msglist + `],"msgCount":` + strconv.Itoa(msgCount) + `}`)
//case "topics":
//case "forums":
//case "users":

View File

@ -43,7 +43,7 @@ func parseSetting(sname string, scontent string, stype string, constraint string
if len(cons) < 2 {
return "Invalid constraint! The second field wasn't set!"
}
con1, err := strconv.Atoi(cons[0])
if err != nil {
return "Invalid contraint! The constraint field wasn't an integer!"
@ -52,12 +52,12 @@ func parseSetting(sname string, scontent string, stype string, constraint string
if err != nil {
return "Invalid contraint! The constraint field wasn't an integer!"
}
value, err := strconv.Atoi(scontent)
if err != nil {
return "Only integers are allowed in this setting x.x\nType mismatch in " + sname
}
if value < con1 || value > con2 {
return "Only integers between a certain range are allowed in this setting"
}
@ -66,4 +66,4 @@ func parseSetting(sname string, scontent string, stype string, constraint string
settings[sname] = scontent
}
return ""
}
}

View File

@ -40,7 +40,7 @@ var menu_6 []byte = []byte(`
<li class="menu_left menu_login"><a href="/accounts/login/">Login</a></li>
`)
var menu_7 []byte = []byte(`
<li class="menu_right menu_alerts">
<li id="general_alerts" class="menu_right menu_alerts">
<div class="alert_bell">🔔</div>
<div class="alert_counter"></div>
<div class="alertList"></div>

View File

@ -15,7 +15,7 @@
<li class="menu_left menu_register"><a href="/accounts/create/">Register</a></li>
<li class="menu_left menu_login"><a href="/accounts/login/">Login</a></li>
{{end}}
<li class="menu_right menu_alerts">
<li id="general_alerts" class="menu_right menu_alerts">
<div class="alert_bell">🔔︎</div>
<div class="alert_counter"></div>
<div class="alertList"></div>

12
user.go
View File

@ -484,9 +484,9 @@ func words_to_score(wcount int, topic bool) (score int) {
score = 1
}
if wcount > settings["megapost_min_chars"].(int) {
if wcount >= settings["megapost_min_words"].(int) {
score += 4
} else if wcount > settings["bigpost_min_chars"].(int) {
} else if wcount >= settings["bigpost_min_words"].(int) {
score += 1
}
return score
@ -503,13 +503,13 @@ func increase_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
@ -542,13 +542,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_chars"].(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_chars"].(int) {
_, err := increment_user_bigposts_stmt.Exec(-1,-1,uid)
if err != nil {
return err

View File

@ -2,17 +2,20 @@
package main
import "fmt"
import "sync"
import "time"
import "bytes"
import "strconv"
import "runtime"
import "net/http"
import(
"fmt"
"sync"
"time"
"bytes"
"strconv"
"errors"
"runtime"
"net/http"
import "github.com/gorilla/websocket"
import "github.com/shirou/gopsutil/cpu"
import "github.com/shirou/gopsutil/mem"
"github.com/gorilla/websocket"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/mem"
)
type WS_User struct
{
@ -30,6 +33,7 @@ type WS_Hub struct
var ws_hub WS_Hub
var ws_upgrader = websocket.Upgrader{ReadBufferSize:1024,WriteBufferSize:1024}
var ws_nouser error = errors.New("This user isn't connected via WebSockets")
func init() {
enable_websockets = true
@ -40,18 +44,76 @@ func init() {
}
}
func (hub *WS_Hub) GuestCount() int {
func (hub *WS_Hub) guest_count() int {
defer hub.guests.RUnlock()
hub.guests.RLock()
return len(hub.online_guests)
}
func (hub *WS_Hub) UserCount() int {
func (hub *WS_Hub) user_count() int {
defer hub.users.RUnlock()
hub.users.RLock()
return len(hub.online_users)
}
func (hub *WS_Hub) broadcast_message(msg string) error {
hub.users.RLock()
for _, ws_user := range hub.online_users {
w, err := ws_user.conn.NextWriter(websocket.TextMessage)
if err != nil {
return err
}
w.Write([]byte(msg))
}
hub.users.RUnlock()
return nil
}
func (hub *WS_Hub) push_message(targetUser int, msg string) error {
hub.users.RLock()
ws_user, ok := hub.online_users[targetUser]
hub.users.RUnlock()
if !ok {
return ws_nouser
}
w, err := ws_user.conn.NextWriter(websocket.TextMessage)
if err != nil {
return err
}
w.Write([]byte(msg))
w.Close()
return nil
}
func(hub *WS_Hub) push_alert(targetUser int, event string, elementType string, actor_id int, targetUser_id int, elementID int) error {
//fmt.Println("In push_alert")
hub.users.RLock()
ws_user, ok := hub.online_users[targetUser]
hub.users.RUnlock()
if !ok {
return ws_nouser
}
//fmt.Println("Building alert")
alert, err := build_alert(event, elementType, actor_id, targetUser_id, elementID, *ws_user.User)
if err != nil {
return err
}
//fmt.Println("Getting WS Writer")
w, err := ws_user.conn.NextWriter(websocket.TextMessage)
if err != nil {
return err
}
//fmt.Println("Writing to the client")
w.Write([]byte(alert))
w.Close()
return nil
}
func route_websockets(w http.ResponseWriter, r *http.Request) {
user, ok := SimpleSessionCheck(w,r)
if !ok {
@ -65,7 +127,7 @@ func route_websockets(w http.ResponseWriter, r *http.Request) {
if err != nil && err != ErrStoreCapacityOverflow {
return
}
ws_user := &WS_User{conn,userptr}
if user.ID == 0 {
ws_hub.guests.Lock()
@ -76,7 +138,7 @@ func route_websockets(w http.ResponseWriter, r *http.Request) {
ws_hub.online_users[user.ID] = ws_user
ws_hub.users.Unlock()
}
//conn.SetReadLimit(/* put the max request size from earlier here? */)
//conn.SetReadDeadline(time.Now().Add(60 * time.Second))
var current_page []byte
@ -94,7 +156,7 @@ func route_websockets(w http.ResponseWriter, r *http.Request) {
}
break
}
//fmt.Println("Message",message)
//fmt.Println("Message",string(message))
messages := bytes.Split(message,[]byte("\r"))
@ -106,7 +168,7 @@ func route_websockets(w http.ResponseWriter, r *http.Request) {
if len(msgblocks) < 2 {
continue
}
if !bytes.Equal(msgblocks[1],current_page) {
ws_leave_page(ws_user, current_page)
current_page = msgblocks[1]
@ -116,9 +178,9 @@ func route_websockets(w http.ResponseWriter, r *http.Request) {
}
}
/*if bytes.Equal(message,[]byte(`start-view`)) {
} else if bytes.Equal(message,[]byte(`end-view`)) {
}*/
}
}
@ -134,17 +196,17 @@ func ws_page_responses(ws_user *WS_User, page []byte) {
//fmt.Println(err.Error())
return
}
fmt.Println(ws_hub.online_users)
uonline := ws_hub.UserCount()
gonline := ws_hub.GuestCount()
uonline := ws_hub.user_count()
gonline := ws_hub.guest_count()
totonline := uonline + gonline
w.Write([]byte("set #dash-totonline " + strconv.Itoa(totonline) + " online\r"))
w.Write([]byte("set #dash-gonline " + strconv.Itoa(gonline) + " guests online\r"))
w.Write([]byte("set #dash-uonline " + strconv.Itoa(uonline) + " users online\r"))
w.Close()*/
// Listen for changes and inform the admins...
admin_stats_mutex.Lock()
watchers := len(admin_stats_watchers)
@ -169,7 +231,7 @@ var admin_stats_watchers map[*WS_User]bool
var admin_stats_mutex sync.RWMutex
func admin_stats_ticker() {
time.Sleep(time.Second)
var last_uonline int = -1
var last_gonline int = -1
var last_totonline int = -1
@ -177,30 +239,29 @@ func admin_stats_ticker() {
var last_available_ram int64 = -1
var no_stat_updates bool = false
var no_ram_updates bool = false
var onlineColour, onlineGuestsColour, onlineUsersColour, cpustr, cpuColour, ramstr, ramColour string
var cpuerr, ramerr error
var memres *mem.VirtualMemoryStat
var cpu_perc []float64
var totunit, uunit, gunit string
AdminStatLoop:
for {
//fmt.Println("tick tock")
admin_stats_mutex.RLock()
watch_count := len(admin_stats_watchers)
admin_stats_mutex.RUnlock()
if watch_count == 0 {
break AdminStatLoop
}
cpu_perc, cpuerr = cpu.Percent(time.Duration(time.Second),true)
memres, ramerr = mem.VirtualMemory()
uonline := ws_hub.UserCount()
gonline := ws_hub.GuestCount()
uonline := ws_hub.user_count()
gonline := ws_hub.guest_count()
totonline := uonline + gonline
// It's far more likely that the CPU Usage will change than the other stats, so we'll optimise them seperately...
no_stat_updates = (uonline == last_uonline && gonline == last_gonline && totonline == last_totonline)
no_ram_updates = (last_available_ram == int64(memres.Available))
@ -208,7 +269,7 @@ AdminStatLoop:
time.Sleep(time.Second)
continue
}
if !no_stat_updates {
if totonline > 10 {
onlineColour = "stat_green"
@ -217,7 +278,7 @@ AdminStatLoop:
} else {
onlineColour = "stat_red"
}
if gonline > 10 {
onlineGuestsColour = "stat_green"
} else if gonline > 1 {
@ -225,7 +286,7 @@ AdminStatLoop:
} else {
onlineGuestsColour = "stat_red"
}
if uonline > 5 {
onlineUsersColour = "stat_green"
} else if uonline > 1 {
@ -233,12 +294,12 @@ AdminStatLoop:
} else {
onlineUsersColour = "stat_red"
}
totonline, totunit = convert_friendly_unit(totonline)
uonline, uunit = convert_friendly_unit(uonline)
gonline, gunit = convert_friendly_unit(gonline)
}
if cpuerr != nil {
cpustr = "Unknown"
} else {
@ -252,14 +313,14 @@ AdminStatLoop:
cpuColour = "stat_red"
}
}
if !no_ram_updates {
if ramerr != nil {
ramstr = "Unknown"
} else {
total_count, total_unit := convert_byte_unit(float64(memres.Total))
used_count := convert_byte_in_unit(float64(memres.Total - memres.Available),total_unit)
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
var totstr string
if (total_count - float64(int(total_count))) > 0.85 {
@ -268,12 +329,12 @@ AdminStatLoop:
} else {
totstr = fmt.Sprintf("%.1f",total_count)
}
if used_count > total_count {
used_count = total_count
}
ramstr = fmt.Sprintf("%.1f",used_count) + " / " + totstr + total_unit
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
if ramperc < 50 {
ramColour = "stat_green"
@ -284,48 +345,48 @@ AdminStatLoop:
}
}
}
admin_stats_mutex.RLock()
watchers := admin_stats_watchers
admin_stats_mutex.RUnlock()
for watcher, _ := range watchers {
w, err := watcher.conn.NextWriter(websocket.TextMessage)
if err != nil {
fmt.Println(err.Error())
//fmt.Println(err.Error())
admin_stats_mutex.Lock()
delete(admin_stats_watchers,watcher)
admin_stats_mutex.Unlock()
continue
}
if !no_stat_updates {
w.Write([]byte("set #dash-totonline " + strconv.Itoa(totonline) + totunit + " online\r"))
w.Write([]byte("set #dash-gonline " + strconv.Itoa(gonline) + gunit + " guests online\r"))
w.Write([]byte("set #dash-uonline " + strconv.Itoa(uonline) + uunit + " users online\r"))
w.Write([]byte("set-class #dash-totonline grid_item grid_stat " + onlineColour + "\r"))
w.Write([]byte("set-class #dash-gonline grid_item grid_stat " + onlineGuestsColour + "\r"))
w.Write([]byte("set-class #dash-uonline grid_item grid_stat " + onlineUsersColour + "\r"))
}
w.Write([]byte("set #dash-cpu CPU: " + cpustr + "%\r"))
w.Write([]byte("set-class #dash-cpu grid_item grid_istat " + cpuColour + "\r"))
if !no_ram_updates {
w.Write([]byte("set #dash-ram RAM: " + ramstr + "\r"))
w.Write([]byte("set-class #dash-ram grid_item grid_istat " + ramColour + "\r"))
}
w.Close()
}
last_uonline = uonline
last_gonline = gonline
last_totonline = totonline
last_cpu_perc = int(cpu_perc[0])
last_available_ram = int64(memres.Available)
//time.Sleep(time.Second)
}
}
}