Added the Online Users Context widget.

Commented out a couple lines of debug code.
Fixed a data race where the Online Users widget would sometimes show more users than it should.
Added a new phrase.
This commit is contained in:
Azareal 2019-01-22 22:06:55 +10:00
parent dc8079f581
commit 89a5eafa48
6 changed files with 62 additions and 19 deletions

View File

@ -34,6 +34,7 @@ var errWsNouser = errors.New("This user isn't connected via WebSockets")
func init() {
adminStatsWatchers = make(map[*websocket.Conn]*WSUser)
topicListWatchers = make(map[*WSUser]bool)
topicWatchers = make(map[int]map[*WSUser]bool)
}
type WsTopicList struct {
@ -122,6 +123,7 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
topicListMutex.Lock()
topicListWatchers[wsUser] = true
topicListMutex.Unlock()
// TODO: Evict from page when permissions change? Or check user perms every-time before sending data?
case strings.HasPrefix(page, "/topic/"):
//fmt.Println("entering topic prefix websockets zone")
_, tid, err := ParseSEOURL(page)
@ -132,13 +134,12 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
if err != nil {
return
}
var usercpy *User = BlankUser()
*usercpy = *wsUser.User
usercpy.Init()
if !Forums.Exists(topic.ParentID) {
return
}
var usercpy *User = BlankUser()
*usercpy = *wsUser.User
usercpy.Init()
/*skip, rerr := header.Hooks.VhookSkippable("ws_topic_check_pre_perms", w, r, usercpy, &fid, &header)
if skip || rerr != nil {
@ -155,6 +156,14 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
if !usercpy.Perms.ViewTopic {
return
}
topicMutex.Lock()
_, ok := topicWatchers[topic.ID]
if !ok {
topicWatchers[topic.ID] = make(map[*WSUser]bool)
}
topicWatchers[topic.ID][wsUser] = true
topicMutex.Unlock()
case page == "/panel/":
if !wsUser.User.IsSuperMod {
return
@ -180,9 +189,7 @@ func wsPageResponses(wsUser *WSUser, conn *websocket.Conn, page string) {
func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
if page == "/" {
page = Config.DefaultPath
}
if page != "" {
} else if page != "" {
DebugLog("Leaving page " + page)
}
switch {
@ -194,6 +201,26 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
})
case strings.HasPrefix(page, "/topic/"):
//fmt.Println("leaving topic prefix websockets zone")
wsUser.FinalizePage(page, func() {
_, tid, err := ParseSEOURL(page)
if err != nil {
return
}
topicMutex.Lock()
defer topicMutex.Unlock()
topic, ok := topicWatchers[tid]
if !ok {
return
}
_, ok = topic[wsUser]
if !ok {
return
}
delete(topic, wsUser)
if len(topic) == 0 {
delete(topicWatchers, tid)
}
})
case page == "/panel/":
adminStatsMutex.Lock()
delete(adminStatsWatchers, conn)
@ -209,6 +236,8 @@ func wsLeavePage(wsUser *WSUser, conn *websocket.Conn, page string) {
// TODO: Use odd-even sharding
var topicListWatchers map[*WSUser]bool
var topicListMutex sync.RWMutex
var topicWatchers map[int]map[*WSUser]bool // map[tid]watchers
var topicMutex sync.RWMutex
var adminStatsWatchers map[*websocket.Conn]*WSUser
var adminStatsMutex sync.RWMutex

View File

@ -25,6 +25,9 @@ func wolBuild(widget *Widget, hvars interface{}) (string, error) {
var users []*User
if ucount < 30 {
users = WsHub.AllUsers()
if len(users) >= 30 {
users = nil
}
}
wol := &wolUsers{hvars.(*Header), phrases.GetTmplPhrase("widget.online_name"), users, ucount}
err := wol.Header.Theme.RunTmpl("widget_online", wol, wol.Header.Writer)

View File

@ -3,13 +3,24 @@ package common
import "github.com/Azareal/Gosora/common/phrases"
func wolContextRender(widget *Widget, hvars interface{}) (string, error) {
ucount := WsHub.UserCount()
// We don't want a ridiculously long list, so we'll show the number if it's too high and only show staff individually
var users []*User
if ucount < 30 {
users = WsHub.AllUsers()
header := hvars.(*Header)
if header.Zone != "view_topic" {
return "", nil
}
wol := &wolUsers{hvars.(*Header), phrases.GetTmplPhrase("widget.online_name"), users, ucount}
err := wol.Header.Theme.RunTmpl("widget_online", wol, wol.Header.Writer)
var ucount int
var users []*User
topicMutex.RLock()
topic, ok := topicWatchers[header.ZoneID]
if ok {
ucount = len(topic)
if ucount < 30 {
for wsUser, _ := range topic {
users = append(users, wsUser.User)
}
}
}
topicMutex.RUnlock()
wol := &wolUsers{header, phrases.GetTmplPhrase("widget.online_view_topic_name"), users, ucount}
err := header.Theme.RunTmpl("widget_online", wol, header.Writer)
return "", err
}

View File

@ -669,6 +669,7 @@
"widget.online_name":"Online Users",
"widget.online_none_online":"No one is online.",
"widget.online_some_online":"There are %d users online.",
"widget.online_view_topic_name":"In Topic",
"option_yes":"Yes",
"option_no":"No",

View File

@ -4,7 +4,6 @@ import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
@ -386,7 +385,7 @@ func widgetsParseInputs(r *http.Request, widget *common.Widget) (*common.WidgetE
return nil, errors.New("You need to fill in the body for this widget.")
}
widget.Type = wtype // ? - Are we sure we should be directly assigning user provided data even if it's validated?
case "wol", "search_and_filter":
case "wol", "wol_context", "search_and_filter":
widget.Type = wtype // ? - Are we sure we should be directly assigning user provided data even if it's validated?
default:
return nil, errors.New("Unknown widget type")
@ -397,7 +396,7 @@ func widgetsParseInputs(r *http.Request, widget *common.Widget) (*common.WidgetE
// ThemesWidgetsEditSubmit is an action which is triggered when someone sends an update request for a widget
func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, swid string) common.RouteError {
fmt.Println("in ThemesWidgetsEditSubmit")
//fmt.Println("in ThemesWidgetsEditSubmit")
_, ferr := common.SimplePanelUserCheck(w, r, &user)
if ferr != nil {
return ferr
@ -434,7 +433,7 @@ func ThemesWidgetsEditSubmit(w http.ResponseWriter, r *http.Request, user common
// ThemesWidgetsCreateSubmit is an action which is triggered when someone sends a create request for a widget
func ThemesWidgetsCreateSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
fmt.Println("in ThemesWidgetsCreateSubmit")
//fmt.Println("in ThemesWidgetsCreateSubmit")
isJs := (r.PostFormValue("js") == "1")
_, ferr := common.SimplePanelUserCheck(w, r, &user)
if ferr != nil {

View File

@ -5,7 +5,7 @@
<option value="about"{{if eq .Type "about"}} selected{{end}}>{{lang "panel_themes_widgets_type_about"}}</option>
<option value="simple"{{if eq .Type "simple"}} selected{{end}}>{{lang "panel_themes_widgets_type_simple"}}</option>
<option value="wol"{{if eq .Type "wol"}} selected{{end}}>{{lang "panel_themes_widgets_type_wol"}}</option>
<!--<option value="wol_context"{{if eq .Type "wol_context"}} selected{{end}}>{{lang "panel_themes_widgets_type_wol_context"}}</option>-->
<option value="wol_context"{{if eq .Type "wol_context"}} selected{{end}}>{{lang "panel_themes_widgets_type_wol_context"}}</option>
<!--<option value="search_and_filter"{{if eq .Type "search_and_filter"}} selected{{end}}>{{lang "panel_themes_widgets_type_search_and_filter"}}</option>-->
</select>
</div>