summaryrefslogtreecommitdiffstats
path: root/app/status.go
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2017-01-13 13:53:37 -0500
committerGitHub <noreply@github.com>2017-01-13 13:53:37 -0500
commit97558f6a6ec4c53fa69035fb430ead209d9c222d (patch)
tree6fc57f5b75b15a025348c6e295cea6aedb9e69ae /app/status.go
parent07bad4d6d518a9012a20fec8309cd625f57c7a8c (diff)
downloadchat-97558f6a6ec4c53fa69035fb430ead209d9c222d.tar.gz
chat-97558f6a6ec4c53fa69035fb430ead209d9c222d.tar.bz2
chat-97558f6a6ec4c53fa69035fb430ead209d9c222d.zip
PLT-4938 Add app package and move logic over from api package (#4931)
* Add app package and move logic over from api package * Change app package functions to return errors * Move non-api tests into app package * Fix merge
Diffstat (limited to 'app/status.go')
-rw-r--r--app/status.go255
1 files changed, 255 insertions, 0 deletions
diff --git a/app/status.go b/app/status.go
new file mode 100644
index 000000000..98cdb0dc0
--- /dev/null
+++ b/app/status.go
@@ -0,0 +1,255 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ l4g "github.com/alecthomas/log4go"
+
+ "github.com/mattermost/platform/einterfaces"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
+)
+
+var statusCache *utils.Cache = utils.NewLru(model.STATUS_CACHE_SIZE)
+
+func ClearStatusCache() {
+ statusCache.Purge()
+}
+
+func AddStatusCacheSkipClusterSend(status *model.Status) {
+ statusCache.Add(status.UserId, status)
+}
+
+func AddStatusCache(status *model.Status) {
+ AddStatusCacheSkipClusterSend(status)
+
+ if einterfaces.GetClusterInterface() != nil {
+ einterfaces.GetClusterInterface().UpdateStatus(status)
+ }
+}
+
+func GetAllStatuses() map[string]*model.Status {
+ userIds := statusCache.Keys()
+ statusMap := map[string]*model.Status{}
+
+ for _, userId := range userIds {
+ if id, ok := userId.(string); !ok {
+ continue
+ } else {
+ status := GetStatusFromCache(id)
+ if status != nil {
+ statusMap[id] = status
+ }
+ }
+ }
+
+ return statusMap
+}
+
+func GetStatusesByIds(userIds []string) (map[string]interface{}, *model.AppError) {
+ statusMap := map[string]interface{}{}
+ metrics := einterfaces.GetMetricsInterface()
+
+ missingUserIds := []string{}
+ for _, userId := range userIds {
+ if result, ok := statusCache.Get(userId); ok {
+ statusMap[userId] = result.(*model.Status).Status
+ if metrics != nil {
+ metrics.IncrementMemCacheHitCounter("Status")
+ }
+ } else {
+ missingUserIds = append(missingUserIds, userId)
+ if metrics != nil {
+ metrics.IncrementMemCacheMissCounter("Status")
+ }
+ }
+ }
+
+ if len(missingUserIds) > 0 {
+ if result := <-Srv.Store.Status().GetByIds(missingUserIds); result.Err != nil {
+ return nil, result.Err
+ } else {
+ statuses := result.Data.([]*model.Status)
+
+ for _, s := range statuses {
+ AddStatusCache(s)
+ statusMap[s.UserId] = s.Status
+ }
+ }
+ }
+
+ // For the case where the user does not have a row in the Status table and cache
+ for _, userId := range missingUserIds {
+ if _, ok := statusMap[userId]; !ok {
+ statusMap[userId] = model.STATUS_OFFLINE
+ }
+ }
+
+ return statusMap, nil
+}
+
+func SetStatusOnline(userId string, sessionId string, manual bool) {
+ broadcast := false
+
+ var oldStatus string = model.STATUS_OFFLINE
+ var oldTime int64 = 0
+ var oldManual bool = false
+ var status *model.Status
+ var err *model.AppError
+
+ if status, err = GetStatus(userId); err != nil {
+ status = &model.Status{userId, model.STATUS_ONLINE, false, model.GetMillis(), ""}
+ broadcast = true
+ } else {
+ if status.Manual && !manual {
+ return // manually set status always overrides non-manual one
+ }
+
+ if status.Status != model.STATUS_ONLINE {
+ broadcast = true
+ }
+
+ oldStatus = status.Status
+ oldTime = status.LastActivityAt
+ oldManual = status.Manual
+
+ status.Status = model.STATUS_ONLINE
+ status.Manual = false // for "online" there's no manual setting
+ status.LastActivityAt = model.GetMillis()
+ }
+
+ AddStatusCache(status)
+
+ // Only update the database if the status has changed, the status has been manually set,
+ // or enough time has passed since the previous action
+ if status.Status != oldStatus || status.Manual != oldManual || status.LastActivityAt-oldTime > model.STATUS_MIN_UPDATE_TIME {
+ achan := Srv.Store.Session().UpdateLastActivityAt(sessionId, status.LastActivityAt)
+
+ var schan store.StoreChannel
+ if broadcast {
+ schan = Srv.Store.Status().SaveOrUpdate(status)
+ } else {
+ schan = Srv.Store.Status().UpdateLastActivityAt(status.UserId, status.LastActivityAt)
+ }
+
+ if result := <-achan; result.Err != nil {
+ l4g.Error(utils.T("api.status.last_activity.error"), userId, sessionId, result.Err)
+ }
+
+ if result := <-schan; result.Err != nil {
+ l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
+ }
+ }
+
+ if broadcast {
+ event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
+ event.Add("status", model.STATUS_ONLINE)
+ event.Add("user_id", status.UserId)
+ go Publish(event)
+ }
+}
+
+func SetStatusOffline(userId string, manual bool) {
+ status, err := GetStatus(userId)
+ if err == nil && status.Manual && !manual {
+ return // manually set status always overrides non-manual one
+ }
+
+ status = &model.Status{userId, model.STATUS_OFFLINE, manual, model.GetMillis(), ""}
+
+ AddStatusCache(status)
+
+ if result := <-Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
+ l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
+ }
+
+ event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
+ event.Add("status", model.STATUS_OFFLINE)
+ event.Add("user_id", status.UserId)
+ go Publish(event)
+}
+
+func SetStatusAwayIfNeeded(userId string, manual bool) {
+ status, err := GetStatus(userId)
+
+ if err != nil {
+ status = &model.Status{userId, model.STATUS_OFFLINE, manual, 0, ""}
+ }
+
+ if !manual && status.Manual {
+ return // manually set status always overrides non-manual one
+ }
+
+ if !manual {
+ if status.Status == model.STATUS_AWAY {
+ return
+ }
+
+ if !IsUserAway(status.LastActivityAt) {
+ return
+ }
+ }
+
+ status.Status = model.STATUS_AWAY
+ status.Manual = manual
+ status.ActiveChannel = ""
+
+ AddStatusCache(status)
+
+ if result := <-Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
+ l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
+ }
+
+ event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
+ event.Add("status", model.STATUS_AWAY)
+ event.Add("user_id", status.UserId)
+ go Publish(event)
+}
+
+func GetStatusFromCache(userId string) *model.Status {
+ if result, ok := statusCache.Get(userId); ok {
+ status := result.(*model.Status)
+ statusCopy := &model.Status{}
+ *statusCopy = *status
+ return statusCopy
+ }
+
+ return nil
+}
+
+func GetStatus(userId string) (*model.Status, *model.AppError) {
+ status := GetStatusFromCache(userId)
+ if status != nil {
+ return status, nil
+ }
+
+ if result := <-Srv.Store.Status().Get(userId); result.Err != nil {
+ return nil, result.Err
+ } else {
+ return result.Data.(*model.Status), nil
+ }
+}
+
+func IsUserAway(lastActivityAt int64) bool {
+ return model.GetMillis()-lastActivityAt >= *utils.Cfg.TeamSettings.UserStatusAwayTimeout*1000
+}
+
+func DoesStatusAllowPushNotification(user *model.User, status *model.Status, channelId string) bool {
+ props := user.NotifyProps
+
+ if props["push"] == "none" {
+ return false
+ }
+
+ if pushStatus, ok := props["push_status"]; (pushStatus == model.STATUS_ONLINE || !ok) && (status.ActiveChannel != channelId || model.GetMillis()-status.LastActivityAt > model.STATUS_CHANNEL_TIMEOUT) {
+ return true
+ } else if pushStatus == model.STATUS_AWAY && (status.Status == model.STATUS_AWAY || status.Status == model.STATUS_OFFLINE) {
+ return true
+ } else if pushStatus == model.STATUS_OFFLINE && status.Status == model.STATUS_OFFLINE {
+ return true
+ }
+
+ return false
+}