github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/app/status.go (about) 1 // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package app 5 6 import ( 7 "fmt" 8 9 "github.com/mattermost/mattermost-server/mlog" 10 "github.com/mattermost/mattermost-server/model" 11 "github.com/mattermost/mattermost-server/store" 12 "github.com/mattermost/mattermost-server/utils" 13 ) 14 15 var statusCache *utils.Cache = utils.NewLru(model.STATUS_CACHE_SIZE) 16 17 func ClearStatusCache() { 18 statusCache.Purge() 19 } 20 21 func (a *App) AddStatusCacheSkipClusterSend(status *model.Status) { 22 statusCache.Add(status.UserId, status) 23 } 24 25 func (a *App) AddStatusCache(status *model.Status) { 26 a.AddStatusCacheSkipClusterSend(status) 27 28 if a.Cluster != nil { 29 msg := &model.ClusterMessage{ 30 Event: model.CLUSTER_EVENT_UPDATE_STATUS, 31 SendType: model.CLUSTER_SEND_BEST_EFFORT, 32 Data: status.ToJson(), 33 } 34 a.Cluster.SendClusterMessage(msg) 35 } 36 } 37 38 func (a *App) GetAllStatuses() map[string]*model.Status { 39 if !*a.Config().ServiceSettings.EnableUserStatuses { 40 return map[string]*model.Status{} 41 } 42 43 userIds := statusCache.Keys() 44 statusMap := map[string]*model.Status{} 45 46 for _, userId := range userIds { 47 if id, ok := userId.(string); !ok { 48 continue 49 } else { 50 status := GetStatusFromCache(id) 51 if status != nil { 52 statusMap[id] = status 53 } 54 } 55 } 56 57 return statusMap 58 } 59 60 func (a *App) GetStatusesByIds(userIds []string) (map[string]interface{}, *model.AppError) { 61 if !*a.Config().ServiceSettings.EnableUserStatuses { 62 return map[string]interface{}{}, nil 63 } 64 65 statusMap := map[string]interface{}{} 66 metrics := a.Metrics 67 68 missingUserIds := []string{} 69 for _, userId := range userIds { 70 if result, ok := statusCache.Get(userId); ok { 71 statusMap[userId] = result.(*model.Status).Status 72 if metrics != nil { 73 metrics.IncrementMemCacheHitCounter("Status") 74 } 75 } else { 76 missingUserIds = append(missingUserIds, userId) 77 if metrics != nil { 78 metrics.IncrementMemCacheMissCounter("Status") 79 } 80 } 81 } 82 83 if len(missingUserIds) > 0 { 84 if result := <-a.Srv.Store.Status().GetByIds(missingUserIds); result.Err != nil { 85 return nil, result.Err 86 } else { 87 statuses := result.Data.([]*model.Status) 88 89 for _, s := range statuses { 90 a.AddStatusCache(s) 91 statusMap[s.UserId] = s.Status 92 } 93 } 94 } 95 96 // For the case where the user does not have a row in the Status table and cache 97 for _, userId := range missingUserIds { 98 if _, ok := statusMap[userId]; !ok { 99 statusMap[userId] = model.STATUS_OFFLINE 100 } 101 } 102 103 return statusMap, nil 104 } 105 106 //GetUserStatusesByIds used by apiV4 107 func (a *App) GetUserStatusesByIds(userIds []string) ([]*model.Status, *model.AppError) { 108 if !*a.Config().ServiceSettings.EnableUserStatuses { 109 return []*model.Status{}, nil 110 } 111 112 var statusMap []*model.Status 113 metrics := a.Metrics 114 115 missingUserIds := []string{} 116 for _, userId := range userIds { 117 if result, ok := statusCache.Get(userId); ok { 118 statusMap = append(statusMap, result.(*model.Status)) 119 if metrics != nil { 120 metrics.IncrementMemCacheHitCounter("Status") 121 } 122 } else { 123 missingUserIds = append(missingUserIds, userId) 124 if metrics != nil { 125 metrics.IncrementMemCacheMissCounter("Status") 126 } 127 } 128 } 129 130 if len(missingUserIds) > 0 { 131 if result := <-a.Srv.Store.Status().GetByIds(missingUserIds); result.Err != nil { 132 return nil, result.Err 133 } else { 134 statuses := result.Data.([]*model.Status) 135 136 for _, s := range statuses { 137 a.AddStatusCache(s) 138 } 139 140 statusMap = append(statusMap, statuses...) 141 } 142 } 143 144 // For the case where the user does not have a row in the Status table and cache 145 // remove the existing ids from missingUserIds and then create a offline state for the missing ones 146 // This also return the status offline for the non-existing Ids in the system 147 for i := 0; i < len(missingUserIds); i++ { 148 missingUserId := missingUserIds[i] 149 for _, userMap := range statusMap { 150 if missingUserId == userMap.UserId { 151 missingUserIds = append(missingUserIds[:i], missingUserIds[i+1:]...) 152 i-- 153 break 154 } 155 } 156 } 157 for _, userId := range missingUserIds { 158 statusMap = append(statusMap, &model.Status{UserId: userId, Status: "offline"}) 159 } 160 161 return statusMap, nil 162 } 163 164 func (a *App) SetStatusOnline(userId string, sessionId string, manual bool) { 165 if !*a.Config().ServiceSettings.EnableUserStatuses { 166 return 167 } 168 169 broadcast := false 170 171 var oldStatus string = model.STATUS_OFFLINE 172 var oldTime int64 = 0 173 var oldManual bool = false 174 var status *model.Status 175 var err *model.AppError 176 177 if status, err = a.GetStatus(userId); err != nil { 178 status = &model.Status{UserId: userId, Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} 179 broadcast = true 180 } else { 181 if status.Manual && !manual { 182 return // manually set status always overrides non-manual one 183 } 184 185 if status.Status != model.STATUS_ONLINE { 186 broadcast = true 187 } 188 189 oldStatus = status.Status 190 oldTime = status.LastActivityAt 191 oldManual = status.Manual 192 193 status.Status = model.STATUS_ONLINE 194 status.Manual = false // for "online" there's no manual setting 195 status.LastActivityAt = model.GetMillis() 196 } 197 198 a.AddStatusCache(status) 199 200 // Only update the database if the status has changed, the status has been manually set, 201 // or enough time has passed since the previous action 202 if status.Status != oldStatus || status.Manual != oldManual || status.LastActivityAt-oldTime > model.STATUS_MIN_UPDATE_TIME { 203 204 var schan store.StoreChannel 205 if broadcast { 206 schan = a.Srv.Store.Status().SaveOrUpdate(status) 207 } else { 208 schan = a.Srv.Store.Status().UpdateLastActivityAt(status.UserId, status.LastActivityAt) 209 } 210 211 if result := <-schan; result.Err != nil { 212 mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", userId, result.Err), mlog.String("user_id", userId)) 213 } 214 } 215 216 if broadcast { 217 a.BroadcastStatus(status) 218 } 219 } 220 221 func (a *App) BroadcastStatus(status *model.Status) { 222 event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil) 223 event.Add("status", status.Status) 224 event.Add("user_id", status.UserId) 225 a.Publish(event) 226 } 227 228 func (a *App) SetStatusOffline(userId string, manual bool) { 229 if !*a.Config().ServiceSettings.EnableUserStatuses { 230 return 231 } 232 233 status, err := a.GetStatus(userId) 234 if err == nil && status.Manual && !manual { 235 return // manually set status always overrides non-manual one 236 } 237 238 status = &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: manual, LastActivityAt: model.GetMillis(), ActiveChannel: ""} 239 240 a.SaveAndBroadcastStatus(status) 241 } 242 243 func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) { 244 if !*a.Config().ServiceSettings.EnableUserStatuses { 245 return 246 } 247 248 status, err := a.GetStatus(userId) 249 250 if err != nil { 251 status = &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: manual, LastActivityAt: 0, ActiveChannel: ""} 252 } 253 254 if !manual && status.Manual { 255 return // manually set status always overrides non-manual one 256 } 257 258 if !manual { 259 if status.Status == model.STATUS_AWAY { 260 return 261 } 262 263 if !a.IsUserAway(status.LastActivityAt) { 264 return 265 } 266 } 267 268 status.Status = model.STATUS_AWAY 269 status.Manual = manual 270 status.ActiveChannel = "" 271 272 a.SaveAndBroadcastStatus(status) 273 } 274 275 func (a *App) SetStatusDoNotDisturb(userId string) { 276 if !*a.Config().ServiceSettings.EnableUserStatuses { 277 return 278 } 279 280 status, err := a.GetStatus(userId) 281 282 if err != nil { 283 status = &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""} 284 } 285 286 status.Status = model.STATUS_DND 287 status.Manual = true 288 289 a.SaveAndBroadcastStatus(status) 290 } 291 292 func (a *App) SaveAndBroadcastStatus(status *model.Status) *model.AppError { 293 a.AddStatusCache(status) 294 295 if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil { 296 mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", status.UserId, result.Err)) 297 } 298 299 event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil) 300 event.Add("status", status.Status) 301 event.Add("user_id", status.UserId) 302 a.Publish(event) 303 304 return nil 305 } 306 307 func (a *App) SetStatusOutOfOffice(userId string) { 308 if !*a.Config().ServiceSettings.EnableUserStatuses { 309 return 310 } 311 312 status, err := a.GetStatus(userId) 313 314 if err != nil { 315 status = &model.Status{UserId: userId, Status: model.STATUS_OUT_OF_OFFICE, Manual: false, LastActivityAt: 0, ActiveChannel: ""} 316 } 317 318 status.Status = model.STATUS_OUT_OF_OFFICE 319 status.Manual = true 320 321 a.AddStatusCache(status) 322 323 if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil { 324 mlog.Error(fmt.Sprintf("Failed to save status for user_id=%v, err=%v", userId, result.Err), mlog.String("user_id", userId)) 325 } 326 327 event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil) 328 event.Add("status", model.STATUS_OUT_OF_OFFICE) 329 event.Add("user_id", status.UserId) 330 a.Publish(event) 331 } 332 333 func GetStatusFromCache(userId string) *model.Status { 334 if result, ok := statusCache.Get(userId); ok { 335 status := result.(*model.Status) 336 statusCopy := &model.Status{} 337 *statusCopy = *status 338 return statusCopy 339 } 340 341 return nil 342 } 343 344 func (a *App) GetStatus(userId string) (*model.Status, *model.AppError) { 345 if !*a.Config().ServiceSettings.EnableUserStatuses { 346 return &model.Status{}, nil 347 } 348 349 status := GetStatusFromCache(userId) 350 if status != nil { 351 return status, nil 352 } 353 354 if result := <-a.Srv.Store.Status().Get(userId); result.Err != nil { 355 return nil, result.Err 356 } else { 357 return result.Data.(*model.Status), nil 358 } 359 } 360 361 func (a *App) IsUserAway(lastActivityAt int64) bool { 362 return model.GetMillis()-lastActivityAt >= *a.Config().TeamSettings.UserStatusAwayTimeout*1000 363 }