From c7d058fe90bf8e4143bcdc46872c7efa20f3af60 Mon Sep 17 00:00:00 2001 From: Azareal Date: Fri, 3 Mar 2017 16:28:49 +0000 Subject: [PATCH] The alert system is now fully functional. --- data.sql | 4 +- mysql.go | 21 ++++++ permissions.go | 46 ++++--------- public/global.js | 128 ++++++++++++++++++++--------------- routes.go | 65 +++++++++++++++++- templates.go | 12 ++-- themes/cosmo/public/main.css | 25 ++++--- 7 files changed, 192 insertions(+), 109 deletions(-) diff --git a/data.sql b/data.sql index 0c9854a9..f9adb149 100644 --- a/data.sql +++ b/data.sql @@ -136,8 +136,8 @@ CREATE TABLE `activity_stream`( CREATE TABLE `activity_subscriptions`( `user` int not null, - `targetID` int not null, - `targetType` varchar(50) not null, + `targetID` int not null, /* the ID of the element being acted upon */ + `targetType` varchar(50) not null, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */ `level` tinyint DEFAULT 0 not null /* 0: Mentions (aka the global default for any post), 1: Replies, 2: Everyone*/ ); diff --git a/mysql.go b/mysql.go index 1c970743..a3b29376 100644 --- a/mysql.go +++ b/mysql.go @@ -33,6 +33,9 @@ var update_forum_cache_stmt *sql.Stmt var create_like_stmt *sql.Stmt var add_likes_to_topic_stmt *sql.Stmt var add_likes_to_reply_stmt *sql.Stmt +var add_activity_stmt *sql.Stmt +var notify_watchers_stmt *sql.Stmt +var notify_one_stmt *sql.Stmt var edit_topic_stmt *sql.Stmt var edit_reply_stmt *sql.Stmt var delete_reply_stmt *sql.Stmt @@ -238,6 +241,24 @@ func init_database(err error) { log.Fatal(err) } + log.Print("Preparing add_activity statement.") + add_activity_stmt, err = db.Prepare("INSERT INTO activity_stream(actor,targetUser,event,elementType,elementID) VALUES(?,?,?,?,?)") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing notify_watchers statement.") + notify_watchers_stmt, err = db.Prepare("INSERT INTO activity_stream_matches(watcher, asid) SELECT activity_subscriptions.user, ? AS asid FROM activity_subscriptions LEFT JOIN activity_stream ON activity_subscriptions.targetType=activity_stream.elementType and activity_subscriptions.targetID=activity_stream.elementID") + if err != nil { + log.Fatal(err) + } + + log.Print("Preparing notify_one statement.") + notify_one_stmt, err = db.Prepare("INSERT INTO activity_stream_matches(watcher,asid) VALUES(?,?)") + if err != nil { + log.Fatal(err) + } + log.Print("Preparing edit_topic statement.") edit_topic_stmt, err = db.Prepare("UPDATE topics SET title = ?, content = ?, parsed_content = ?, is_closed = ? WHERE tid = ?") if err != nil { diff --git a/permissions.go b/permissions.go index 69360475..eb17e3ef 100644 --- a/permissions.go +++ b/permissions.go @@ -310,51 +310,33 @@ func build_forum_permissions() error { func strip_invalid_preset(preset string) string { switch(preset) { - case "all": - case "announce": - case "members": - case "staff": - case "admins": - case "archive": + case "all","announce","members","staff","admins","archive": break - default: - return "" + default: return "" } return preset } func preset_to_lang(preset string) string { switch(preset) { - case "all": - return ""//return "Everyone" - case "announce": - return "Announcements" - case "members": - return "Member Only" - case "staff": - return "Staff Only" - case "admins": - return "Admin Only" - case "archive": - return "Archive" + case "all": return ""//return "Everyone" + case "announce": return "Announcements" + case "members": return "Member Only" + case "staff": return "Staff Only" + case "admins": return "Admin Only" + case "archive": return "Archive" } return "" } func preset_to_emoji(preset string) string { switch(preset) { - case "all": - return ""//return "Everyone" - case "announce": - return "📣" - case "members": - return "👪" - case "staff": - return "👮" - case "admins": - return "👑" - case "archive": - return "☠️" + case "all": return ""//return "Everyone" + case "announce": return "📣" + case "members": return "👪" + case "staff": return "👮" + case "admins": return "👑" + case "archive": return "☠️" } return "" } diff --git a/public/global.js b/public/global.js index 3638e1c0..bf5b84e9 100644 --- a/public/global.js +++ b/public/global.js @@ -8,6 +8,70 @@ function post_link(event) $.ajax({ url: form_action, type: "POST", dataType: "json", data: {js: "1"} }); } +function load_alerts(menu_alerts) +{ + menu_alerts.find(".alert_counter").text(""); + $.ajax({ + type: 'get', + dataType: 'json', + url:'/api/?action=get&module=alerts&format=json', + success: function(data) { + if("errmsg" in data) { + console.log(data.errmsg); + menu_alerts.find(".alertList").html("
"+data.errmsg+"
"); + return; + } + + var alist = ""; + for(var i in data.msgs) { + var msg = data.msgs[i]; + var mmsg = msg.msg; + + if("sub" in msg) { + for(var i = 0; i < msg.sub.length; i++) { + mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]); + console.log("Sub #" + i); + console.log(msg.sub[i]); + } + } + + if(mmsg.length > 46) mmsg = mmsg.substring(0,43) + "..."; + else if(mmsg.length > 35) size_dial = " smaller"; //9px + else size_dial = ""; // 10px + + if("avatar" in msg) { + alist += "
"+mmsg+"
"; + console.log(msg.avatar); + } else { + alist += "
"+mmsg+"
"; + } + console.log(msg); + //console.log(mmsg); + } + + if(alist == "") { + alist = "
You don't have any alerts
" + } + menu_alerts.find(".alertList").html(alist); + if(data.msgs.length != 0) { + menu_alerts.find(".alert_counter").text(data.msgs.length); + } + }, + error: function(magic,theStatus,error) { + try { + var data = JSON.parse(magic.responseText); + if("errmsg" in data) + { + console.log(data.errmsg); + errtxt = data.errmsg; + } + else errtxt = "Unable to get the alerts" + } catch(e) { errtxt = "Unable to get the alerts"; } + menu_alerts.find(".alertList").html("
"+errtxt+"
"); + } + }); +} + $(document).ready(function(){ $(".open_edit").click(function(event){ //console.log("Clicked on edit"); @@ -162,63 +226,19 @@ $(document).ready(function(){ } }); + $('body').click(function() { + $(".selectedAlert").removeClass("selectedAlert"); + }); + + $(".menu_alerts").ready(function(){ + load_alerts($(this)); + }); + $(".menu_alerts").click(function(event) { + event.stopPropagation(); if($(this).hasClass("selectedAlert")) return; - var menu_alerts = $(this); - this.className += " selectedAlert"; - $.ajax({ - type: 'get', - dataType: 'json', - url:'/api/?action=get&module=alerts&format=json', - success: function(data) { - if("errmsg" in data) { - console.log(data.errmsg); - menu_alerts.find(".alertList").html("
"+data.errmsg+"
"); - return; - } - - var alist = ""; - for(var i in data.msgs) { - var msg = data.msgs[i]; - var mmsg = msg.msg; - - if("sub" in msg) { - for(var i = 0; i < msg.sub.length; i++) { - mmsg = mmsg.replace("\{"+i+"\}", msg.sub[i]); - console.log("Sub #" + i); - console.log(msg.sub[i]); - } - } - - if("avatar" in msg) { - alist += "
"+mmsg+"
"; - console.log(msg.avatar); - } else { - alist += "
"+mmsg+"
"; - } - console.log(msg); - console.log(mmsg); - } - - if(alist == "") { - alist = "
You don't have any alerts
" - } - menu_alerts.find(".alertList").html(alist); - }, - error: function(magic,theStatus,error) { - try { - var data = JSON.parse(magic.responseText); - if("errmsg" in data) - { - console.log(data.errmsg); - errtxt = data.errmsg; - } - else errtxt = "Unable to get the alerts" - } catch(e) { errtxt = "Unable to get the alerts"; } - menu_alerts.find(".alertList").html("
"+errtxt+"
"); - } - }); + load_alerts($(this)); }); this.onkeyup = function(event){ diff --git a/routes.go b/routes.go index 9bb7c54f..9fce8a0d 100644 --- a/routes.go +++ b/routes.go @@ -727,7 +727,8 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) { var words int var fid int - err = db.QueryRow("select parentID, words from topics where tid = ?", tid).Scan(&fid,&words) + var createdBy int + err = db.QueryRow("select parentID, words, createdBy from topics where tid = ?", tid).Scan(&fid,&words,&createdBy) if err == sql.ErrNoRows { PreError("The requested topic doesn't exist.",w,r) return @@ -754,6 +755,15 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) { return } + _, err = users.CascadeGet(createdBy) + if err != nil && err != sql.ErrNoRows { + LocalError("The target user doesn't exist",w,r,user) + return + } else if err != nil { + InternalError(err,w,r) + return + } + //score := words_to_score(words,true) score := 1 _, err = create_like_stmt.Exec(score,tid,"topics",user.ID) @@ -768,6 +778,28 @@ func route_like_topic(w http.ResponseWriter, r *http.Request) { return } + res, err := add_activity_stmt.Exec(user.ID,createdBy,"like","topic",tid) + if err != nil { + InternalError(err,w,r) + return + } + lastId, err := res.LastInsertId() + if err != nil { + InternalError(err,w,r) + 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 + } + http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther) } @@ -786,7 +818,8 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) { var tid int var words int - err = db.QueryRow("select tid, words from replies where rid = ?", rid).Scan(&tid, &words) + var createdBy int + err = db.QueryRow("select tid, words, createdBy from replies where rid = ?", rid).Scan(&tid, &words, &createdBy) if err == sql.ErrNoRows { PreError("You can't like something which doesn't exist!",w,r) return @@ -823,6 +856,15 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) { return } + _, err = users.CascadeGet(createdBy) + if err != nil && err != sql.ErrNoRows { + LocalError("The target user doesn't exist",w,r,user) + return + } else if err != nil { + InternalError(err,w,r) + return + } + //score := words_to_score(words,false) score := 1 _, err = create_like_stmt.Exec(score,rid,"replies",user.ID) @@ -837,6 +879,23 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request) { return } + res, err := add_activity_stmt.Exec(user.ID,createdBy,"like","post",rid) + if err != nil { + InternalError(err,w,r) + return + } + lastId, err := res.LastInsertId() + if err != nil { + InternalError(err,w,r) + return + } + + _, err = notify_one_stmt.Exec(createdBy,lastId) + if err != nil { + InternalError(err,w,r) + return + } + http.Redirect(w,r,"/topic/" + strconv.Itoa(tid),http.StatusSeeOther) } @@ -1748,7 +1807,7 @@ func route_api(w http.ResponseWriter, r *http.Request) { LocalErrorJS("Unable to find the target reply or parent topic",w,r) return } - url = build_topic_url(elementID) + url = build_topic_url(topic.ID) area = topic.Title if targetUser_id == user.ID { post_act = " your post in" diff --git a/templates.go b/templates.go index 7e61540b..e8126475 100644 --- a/templates.go +++ b/templates.go @@ -743,14 +743,10 @@ func (c *CTemplateSet) compile_if_varsub(varname string, varholder string, templ func (c *CTemplateSet) compile_boolsub(varname string, varholder string, template_name string, val reflect.Value) string { out, val := c.compile_if_varsub(varname, varholder, template_name, val) switch val.Kind() { - case reflect.Int: - out += " > 0" - case reflect.Bool: - // Do nothing - case reflect.String: - out += " != \"\"" - case reflect.Int64: - out += " > 0" + case reflect.Int: out += " > 0" + case reflect.Bool: // Do nothing + case reflect.String: out += " != \"\"" + case reflect.Int64: out += " > 0" default: fmt.Println(varname) fmt.Println(varholder) diff --git a/themes/cosmo/public/main.css b/themes/cosmo/public/main.css index 510f1907..3ccdc7c1 100644 --- a/themes/cosmo/public/main.css +++ b/themes/cosmo/public/main.css @@ -106,6 +106,10 @@ li:hover .selectedAlert:hover { background: white; color: black; + font-weight: bold; +} +.selectedAlert .alert_counter { + display: none; } .menu_alerts .alertList { display: none; @@ -117,7 +121,7 @@ li:hover background: white; font-size: 10px; line-height: 16px; - width: 135px; + width: 156px; right: -15px; border-top: 1px solid #ccc; border-left: 1px solid #ccc; @@ -130,21 +134,25 @@ li:hover } .alertItem.withAvatar { /*background-image: url('/uploads/avatar_1.jpg');*/ - background-size: auto 56px; + background-size: 36px; background-repeat: no-repeat; - text-align: right; + text-align: center; padding-right: 12px; + padding-left: 42px; height: 46px; } .alertItem.withAvatar:not(:last-child) { border-bottom: 1px solid rgb(230,230,230); } -.alertItem.withAvatar .text { +.alertItem .text { overflow: hidden; text-overflow: ellipsis; - float: right; - width: calc(100% - 20px); height: 30px; + width: 100%; + color: black; +} +.alertItem .text.smaller { + font-size: 9px; } #footer @@ -967,10 +975,7 @@ blockquote p border: 1px solid rgba(90,90,90,0.75); transition: transform 0.7s; } - ul:hover - { - transform: rotateX(-15deg); - } + ul:hover { transform: rotateX(-15deg); } li {