func sendReactionEvent(event string, channelId string, reaction *model.Reaction, postHadReactions bool) { // send out that a reaction has been added/removed go func() { message := model.NewWebSocketEvent(event, "", channelId, "", nil) message.Add("reaction", reaction.ToJson()) app.Publish(message) }() // send out that a post was updated if post.HasReactions has changed go func() { var post *model.Post if result := <-app.Srv.Store.Post().Get(reaction.PostId); result.Err != nil { l4g.Warn(utils.T("api.reaction.send_reaction_event.post.app_error")) return } else { post = result.Data.(*model.PostList).Posts[reaction.PostId] } if post.HasReactions != postHadReactions { message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", channelId, "", nil) message.Add("post", post.ToJson()) app.Publish(message) } }() }
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError { if channel.DeleteAt > 0 { return model.NewLocAppError("RemoveUserFromChannel", "api.channel.remove_user_from_channel.deleted.app_error", nil, "") } if channel.Name == model.DEFAULT_CHANNEL { return model.NewLocAppError("RemoveUserFromChannel", "api.channel.remove.default.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "") } if cmresult := <-app.Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil { return cmresult.Err } app.InvalidateCacheForUser(userIdToRemove) app.InvalidateCacheForChannel(channel.Id) message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil) message.Add("user_id", userIdToRemove) message.Add("remover_id", removerUserId) go app.Publish(message) // because the removed user no longer belongs to the channel we need to send a separate websocket event userMsg := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", "", userIdToRemove, nil) userMsg.Add("channel_id", channel.Id) userMsg.Add("remover_id", removerUserId) go app.Publish(userMsg) return nil }
func setCollapsePreference(c *Context, isCollapse bool) *model.CommandResponse { pref := model.Preference{ UserId: c.Session.UserId, Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, Name: model.PREFERENCE_NAME_COLLAPSE_SETTING, Value: strconv.FormatBool(isCollapse), } if result := <-app.Srv.Store.Preference().Save(&model.Preferences{pref}); result.Err != nil { return &model.CommandResponse{Text: c.T("api.command_expand_collapse.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } socketMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCE_CHANGED, "", "", c.Session.UserId, nil) socketMessage.Add("preference", pref.ToJson()) go app.Publish(socketMessage) var rmsg string if isCollapse { rmsg = c.T("api.command_collapse.success") } else { rmsg = c.T("api.command_expand.success") } return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg} }
func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] data := model.StringInterfaceFromJson(r.Body) newLastViewedAt := int64(data["last_viewed_at"].(float64)) app.Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt) chanPref := model.Preference{ UserId: c.Session.UserId, Category: c.TeamId, Name: model.PREFERENCE_NAME_LAST_CHANNEL, Value: id, } teamPref := model.Preference{ UserId: c.Session.UserId, Category: model.PREFERENCE_CATEGORY_LAST, Name: model.PREFERENCE_NAME_LAST_TEAM, Value: c.TeamId, } app.Srv.Store.Preference().Save(&model.Preferences{teamPref, chanPref}) message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil) message.Add("channel_id", id) go app.Publish(message) result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) }
func LeaveTeam(team *model.Team, user *model.User) *model.AppError { var teamMember model.TeamMember if result := <-app.Srv.Store.Team().GetMember(team.Id, user.Id); result.Err != nil { return model.NewLocAppError("RemoveUserFromTeam", "api.team.remove_user_from_team.missing.app_error", nil, result.Err.Error()) } else { teamMember = result.Data.(model.TeamMember) } var channelList *model.ChannelList if result := <-app.Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil { if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" { channelList = &model.ChannelList{} } else { return result.Err } } else { channelList = result.Data.(*model.ChannelList) } for _, channel := range *channelList { if channel.Type != model.CHANNEL_DIRECT { app.InvalidateCacheForChannel(channel.Id) if result := <-app.Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil { return result.Err } } } // Send the websocket message before we actually do the remove so the user being removed gets it. message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LEAVE_TEAM, team.Id, "", "", nil) message.Add("user_id", user.Id) message.Add("team_id", team.Id) app.Publish(message) teamMember.Roles = "" teamMember.DeleteAt = model.GetMillis() if result := <-app.Srv.Store.Team().UpdateMember(&teamMember); result.Err != nil { return result.Err } if uua := <-app.Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil { return uua.Err } // delete the preferences that set the last channel used in the team and other team specific preferences if result := <-app.Srv.Store.Preference().DeleteCategory(user.Id, team.Id); result.Err != nil { return result.Err } app.RemoveAllSessionsForUserId(user.Id) app.InvalidateCacheForUser(user.Id) return nil }
func webrtcMessage(req *model.WebSocketRequest) (map[string]interface{}, *model.AppError) { var ok bool var toUserId string if toUserId, ok = req.Data["to_user_id"].(string); !ok || len(toUserId) != 26 { return nil, NewInvalidWebSocketParamError(req.Action, "to_user_id") } event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_WEBRTC, "", "", toUserId, nil) event.Data = req.Data go app.Publish(event) return nil, nil }
func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) { view := model.ChannelViewFromJson(r.Body) if err := SetActiveChannel(c.Session.UserId, view.ChannelId); err != nil { c.Err = err return } if len(view.ChannelId) == 0 { ReturnStatusOK(w) return } channelIds := []string{view.ChannelId} var pchan store.StoreChannel if len(view.PrevChannelId) > 0 { channelIds = append(channelIds, view.PrevChannelId) if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() { pchan = app.Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, view.ChannelId) } } uchan := app.Srv.Store.Channel().UpdateLastViewedAt(channelIds, c.Session.UserId) if pchan != nil { if result := <-pchan; result.Err != nil { c.Err = result.Err return } else { if result.Data.(int64) > 0 { app.ClearPushNotification(c.Session.UserId, view.ChannelId) } } } if result := <-uchan; result.Err != nil { c.Err = result.Err return } message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil) message.Add("channel_id", view.ChannelId) go app.Publish(message) ReturnStatusOK(w) }
func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) { team := model.TeamFromJson(r.Body) if team == nil { c.SetInvalidParam("updateTeam", "team") return } team.Id = c.TeamId if !HasPermissionToTeamContext(c, team.Id, model.PERMISSION_MANAGE_TEAM) { c.Err = model.NewLocAppError("updateTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId) c.Err.StatusCode = http.StatusForbidden return } var oldTeam *model.Team if result := <-app.Srv.Store.Team().Get(team.Id); result.Err != nil { c.Err = result.Err return } else { oldTeam = result.Data.(*model.Team) } oldTeam.DisplayName = team.DisplayName oldTeam.Description = team.Description oldTeam.InviteId = team.InviteId oldTeam.AllowOpenInvite = team.AllowOpenInvite oldTeam.CompanyName = team.CompanyName oldTeam.AllowedDomains = team.AllowedDomains //oldTeam.Type = team.Type if result := <-app.Srv.Store.Team().Update(oldTeam); result.Err != nil { c.Err = result.Err return } oldTeam.Sanitize() message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_UPDATE_TEAM, "", "", "", nil) message.Add("team", oldTeam.ToJson()) go app.Publish(message) w.Write([]byte(oldTeam.ToJson())) }
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { post := model.PostFromJson(r.Body) if post == nil { c.SetInvalidParam("updatePost", "post") return } pchan := app.Srv.Store.Post().Get(post.Id) if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_EDIT_POST) { return } var oldPost *model.Post if result := <-pchan; result.Err != nil { c.Err = result.Err return } else { oldPost = result.Data.(*model.PostList).Posts[post.Id] if oldPost == nil { c.Err = model.NewLocAppError("updatePost", "api.post.update_post.find.app_error", nil, "id="+post.Id) c.Err.StatusCode = http.StatusBadRequest return } if oldPost.UserId != c.Session.UserId { c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil, "oldUserId="+oldPost.UserId) c.Err.StatusCode = http.StatusForbidden return } if oldPost.DeleteAt != 0 { c.Err = model.NewLocAppError("updatePost", "api.post.update_post.permissions.app_error", nil, c.T("api.post.update_post.permissions_details.app_error", map[string]interface{}{"PostId": post.Id})) c.Err.StatusCode = http.StatusForbidden return } if oldPost.IsSystemMessage() { c.Err = model.NewLocAppError("updatePost", "api.post.update_post.system_message.app_error", nil, "id="+post.Id) c.Err.StatusCode = http.StatusForbidden return } } newPost := &model.Post{} *newPost = *oldPost newPost.Message = post.Message newPost.EditAt = model.GetMillis() newPost.Hashtags, _ = model.ParseHashtags(post.Message) if result := <-app.Srv.Store.Post().Update(newPost, oldPost); result.Err != nil { c.Err = result.Err return } else { rpost := result.Data.(*model.Post) message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", rpost.ChannelId, "", nil) message.Add("post", rpost.ToJson()) go app.Publish(message) app.InvalidateCacheForChannelPosts(rpost.ChannelId) w.Write([]byte(rpost.ToJson())) } }
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) channelId := params["channel_id"] if len(channelId) != 26 { c.SetInvalidParam("deletePost", "channelId") return } postId := params["post_id"] if len(postId) != 26 { c.SetInvalidParam("deletePost", "postId") return } if !HasPermissionToChannelContext(c, channelId, model.PERMISSION_EDIT_POST) { return } pchan := app.Srv.Store.Post().Get(postId) if result := <-pchan; result.Err != nil { c.Err = result.Err return } else { post := result.Data.(*model.PostList).Posts[postId] if post == nil { c.SetInvalidParam("deletePost", "postId") return } if post.ChannelId != channelId { c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return } if post.UserId != c.Session.UserId && !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_EDIT_OTHERS_POSTS) { c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return } if dresult := <-app.Srv.Store.Post().Delete(postId, model.GetMillis()); dresult.Err != nil { c.Err = dresult.Err return } message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil) message.Add("post", post.ToJson()) go app.Publish(message) go DeletePostFiles(post) go DeleteFlaggedPost(c.Session.UserId, post) app.InvalidateCacheForChannelPosts(post.ChannelId) result := make(map[string]string) result["id"] = postId w.Write([]byte(model.MapToJson(result))) } }
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] data := model.StringInterfaceFromJson(r.Body) var active bool var ok bool if active, ok = data["active"].(bool); !ok { active = true } doClearPush := false if *utils.Cfg.EmailSettings.SendPushNotifications && !c.Session.IsMobileApp() && active { if result := <-app.Srv.Store.User().GetUnreadCountForChannel(c.Session.UserId, id); result.Err != nil { l4g.Error(utils.T("api.channel.update_last_viewed_at.get_unread_count_for_channel.error"), c.Session.UserId, id, result.Err.Error()) } else { if result.Data.(int64) > 0 { doClearPush = true } } } go func() { if err := SetActiveChannel(c.Session.UserId, id); err != nil { l4g.Error(err.Error()) } }() app.Srv.Store.Channel().UpdateLastViewedAt([]string{id}, c.Session.UserId) // Must be after update so that unread count is correct if doClearPush { go app.ClearPushNotification(c.Session.UserId, id) } chanPref := model.Preference{ UserId: c.Session.UserId, Category: c.TeamId, Name: model.PREFERENCE_NAME_LAST_CHANNEL, Value: id, } teamPref := model.Preference{ UserId: c.Session.UserId, Category: model.PREFERENCE_CATEGORY_LAST, Name: model.PREFERENCE_NAME_LAST_TEAM, Value: c.TeamId, } app.Srv.Store.Preference().Save(&model.Preferences{teamPref, chanPref}) message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, c.TeamId, "", c.Session.UserId, nil) message.Add("channel_id", id) go app.Publish(message) result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) }
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] sc := app.Srv.Store.Channel().Get(id, true) scm := app.Srv.Store.Channel().GetMember(id, c.Session.UserId) cmc := app.Srv.Store.Channel().GetMemberCount(id, false) uc := app.Srv.Store.User().Get(c.Session.UserId) ihc := app.Srv.Store.Webhook().GetIncomingByChannel(id) ohc := app.Srv.Store.Webhook().GetOutgoingByChannel(id) if cresult := <-sc; cresult.Err != nil { c.Err = cresult.Err return } else if uresult := <-uc; uresult.Err != nil { c.Err = cresult.Err return } else if scmresult := <-scm; scmresult.Err != nil { c.Err = scmresult.Err return } else if cmcresult := <-cmc; cmcresult.Err != nil { c.Err = cmcresult.Err return } else if ihcresult := <-ihc; ihcresult.Err != nil { c.Err = ihcresult.Err return } else if ohcresult := <-ohc; ohcresult.Err != nil { c.Err = ohcresult.Err return } else { channel := cresult.Data.(*model.Channel) memberCount := cmcresult.Data.(int64) user := uresult.Data.(*model.User) incomingHooks := ihcresult.Data.([]*model.IncomingWebhook) outgoingHooks := ohcresult.Data.([]*model.OutgoingWebhook) // Don't need to do anything with channel member, just wanted to confirm it exists // Allow delete if user is the only member left in channel if memberCount > 1 { if channel.Type == model.CHANNEL_OPEN && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_DELETE_PUBLIC_CHANNEL) { return } if channel.Type == model.CHANNEL_PRIVATE && !HasPermissionToChannelContext(c, channel.Id, model.PERMISSION_DELETE_PRIVATE_CHANNEL) { return } } if channel.DeleteAt > 0 { c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.deleted.app_error", nil, "") c.Err.StatusCode = http.StatusBadRequest return } if channel.Name == model.DEFAULT_CHANNEL { c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.cannot.app_error", map[string]interface{}{"Channel": model.DEFAULT_CHANNEL}, "") c.Err.StatusCode = http.StatusBadRequest return } post := &model.Post{ ChannelId: channel.Id, Message: fmt.Sprintf(c.T("api.channel.delete_channel.archived"), user.Username), Type: model.POST_CHANNEL_DELETED, UserId: c.Session.UserId, } if _, err := app.CreatePost(post, c.TeamId, false); err != nil { l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err) } now := model.GetMillis() for _, hook := range incomingHooks { if result := <-app.Srv.Store.Webhook().DeleteIncoming(hook.Id, now); result.Err != nil { l4g.Error(utils.T("api.channel.delete_channel.incoming_webhook.error"), hook.Id) } } for _, hook := range outgoingHooks { if result := <-app.Srv.Store.Webhook().DeleteOutgoing(hook.Id, now); result.Err != nil { l4g.Error(utils.T("api.channel.delete_channel.outgoing_webhook.error"), hook.Id) } } if dresult := <-app.Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil { c.Err = dresult.Err return } app.InvalidateCacheForChannel(channel.Id) c.LogAudit("name=" + channel.Name) message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, c.TeamId, "", "", nil) message.Add("channel_id", channel.Id) app.Publish(message) result := make(map[string]string) result["id"] = channel.Id w.Write([]byte(model.MapToJson(result))) } }
func TestWebSocketEvent(t *testing.T) { th := Setup().InitBasic() WebSocketClient, err := th.CreateWebSocketClient() if err != nil { t.Fatal(err) } defer WebSocketClient.Close() WebSocketClient.Listen() time.Sleep(300 * time.Millisecond) if resp := <-WebSocketClient.ResponseChannel; resp.Status != model.STATUS_OK { t.Fatal("should have responded OK to authentication challenge") } omitUser := make(map[string]bool, 1) omitUser["somerandomid"] = true evt1 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", th.BasicChannel.Id, "", omitUser) evt1.Add("user_id", "somerandomid") app.Publish(evt1) time.Sleep(300 * time.Millisecond) stop := make(chan bool) eventHit := false go func() { for { select { case resp := <-WebSocketClient.EventChannel: if resp.Event == model.WEBSOCKET_EVENT_TYPING && resp.Data["user_id"].(string) == "somerandomid" { eventHit = true } case <-stop: return } } }() time.Sleep(400 * time.Millisecond) stop <- true if !eventHit { t.Fatal("did not receive typing event") } evt2 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", "somerandomid", "", nil) go app.Publish(evt2) time.Sleep(300 * time.Millisecond) eventHit = false go func() { for { select { case resp := <-WebSocketClient.EventChannel: if resp.Event == model.WEBSOCKET_EVENT_TYPING { eventHit = true } case <-stop: return } } }() time.Sleep(400 * time.Millisecond) stop <- true if eventHit { t.Fatal("got typing event for bad channel id") } }