func makeDirectChannelVisible(teamId string, channelId string) { var members []model.ChannelMember if result := <-Srv.Store.Channel().GetMembers(channelId); result.Err != nil { l4g.Error(utils.T("api.post.make_direct_channel_visible.get_members.error"), channelId, result.Err.Message) return } else { members = result.Data.([]model.ChannelMember) } if len(members) != 2 { l4g.Error(utils.T("api.post.make_direct_channel_visible.get_2_members.error"), channelId) return } // make sure the channel is visible to both members for i, member := range members { otherUserId := members[1-i].UserId if result := <-Srv.Store.Preference().Get(member.UserId, model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, otherUserId); result.Err != nil { // create a new preference since one doesn't exist yet preference := &model.Preference{ UserId: member.UserId, Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, Name: otherUserId, Value: "true", } if saveResult := <-Srv.Store.Preference().Save(&model.Preferences{*preference}); saveResult.Err != nil { l4g.Error(utils.T("api.post.make_direct_channel_visible.save_pref.error"), member.UserId, otherUserId, saveResult.Err.Message) } else { message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED) message.Add("preference", preference.ToJson()) PublishAndForget(message) } } else { preference := result.Data.(model.Preference) if preference.Value != "true" { // update the existing preference to make the channel visible preference.Value = "true" if updateResult := <-Srv.Store.Preference().Save(&model.Preferences{preference}); updateResult.Err != nil { l4g.Error(utils.T("api.post.make_direct_channel_visible.update_pref.error"), member.UserId, otherUserId, updateResult.Err.Message) } else { message := model.NewMessage(teamId, channelId, member.UserId, model.ACTION_PREFERENCE_CHANGED) message.Add("preference", preference.ToJson()) PublishAndForget(message) } } } } }
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { if !utils.Cfg.TeamSettings.EnableUserCreation { c.Err = model.NewAppError("CreateUser", "User creation has been disabled. Please ask your systems administrator for details.", "") return nil } channelRole := "" if team.Email == user.Email { user.Roles = model.ROLE_TEAM_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN // Below is a speical case where the first user in the entire // system is granted the system_admin role instead of admin if result := <-Srv.Store.User().GetTotalUsersCount(); result.Err != nil { c.Err = result.Err return nil } else { count := result.Data.(int64) if count <= 0 { user.Roles = model.ROLE_SYSTEM_ADMIN } } } else { user.Roles = "" } user.MakeNonNil() if result := <-Srv.Store.User().Save(user); result.Err != nil { c.Err = result.Err l4g.Error("Couldn't save the user err=%v", result.Err) return nil } else { ruser := result.Data.(*model.User) // Soft error if there is an issue joining the default channels if err := JoinDefaultChannels(ruser, channelRole); err != nil { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } addDirectChannelsAndForget(ruser) if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } ruser.Sanitize(map[string]bool{}) // This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) PublishAndForget(message) return ruser } }
func removeChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] data := model.MapFromJson(r.Body) userId := data["user_id"] if len(userId) != 26 { c.SetInvalidParam("addChannelMember", "user_id") return } sc := Srv.Store.Channel().Get(id) cmc := Srv.Store.Channel().GetMember(id, c.Session.UserId) if cresult := <-sc; cresult.Err != nil { c.Err = cresult.Err return } else if cmcresult := <-cmc; cmcresult.Err != nil { c.Err = cmcresult.Err return } else { channel := cresult.Data.(*model.Channel) channelMember := cmcresult.Data.(model.ChannelMember) if !c.HasPermissionsToTeam(channel.TeamId, "removeChannelMember") { return } if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !strings.Contains(c.Session.Roles, model.ROLE_ADMIN) { c.Err = model.NewAppError("updateChannel", "You do not have the appropriate permissions ", "") c.Err.StatusCode = http.StatusForbidden return } if channel.DeleteAt > 0 { c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "") c.Err.StatusCode = http.StatusBadRequest return } if cmresult := <-Srv.Store.Channel().RemoveMember(id, userId); cmresult.Err != nil { c.Err = cmresult.Err return } message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_REMOVED) message.Add("channel_id", id) message.Add("remover", c.Session.UserId) PublishAndForget(message) c.LogAudit("name=" + channel.Name + " user_id=" + userId) result := make(map[string]string) result["channel_id"] = channel.Id result["removed_user_id"] = userId w.Write([]byte(model.MapToJson(result))) } }
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) { if channel.DeleteAt > 0 { return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.deleted.app_error", nil, "") } if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE { return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.type.app_error", nil, "") } if result := <-Srv.Store.Channel().GetMember(channel.Id, user.Id); result.Err != nil { if result.Err.Id != store.MISSING_MEMBER_ERROR { return nil, result.Err } } else { channelMember := result.Data.(model.ChannelMember) return &channelMember, nil } newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()} if result := <-Srv.Store.Channel().SaveMember(newMember); result.Err != nil { l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, result.Err) return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "") } go func() { InvalidateCacheForUser(user.Id) message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED) go Publish(message) }() return newMember, nil }
func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError { tm := &model.TeamMember{TeamId: team.Id, UserId: user.Id} channelRole := "" if team.Email == user.Email { tm.Roles = model.ROLE_TEAM_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN } if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil { if tmr.Err.Id == store.TEAM_MEMBER_EXISTS_ERROR { return nil } return tmr.Err } if uua := <-Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil { return uua.Err } // Soft error if there is an issue joining the default channels if err := JoinDefaultChannels(team.Id, user, channelRole); err != nil { l4g.Error(utils.T("api.user.create_user.joining.error"), user.Id, team.Id, err) } RemoveAllSessionsForUserId(user.Id) InvalidateCacheForUser(user.Id) // This message goes to every channel, so the channelId is irrelevant go Publish(model.NewMessage("", "", user.Id, model.ACTION_NEW_USER)) return nil }
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { post := model.PostFromJson(r.Body) if post == nil { c.SetInvalidParam("updatePost", "post") return } cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId) pchan := Srv.Store.Post().Get(post.Id) if !c.HasPermissionsToChannel(cchan, "updatePost") { 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.NewAppError("updatePost", "We couldn't find the existing post or comment to update.", "id="+post.Id) c.Err.StatusCode = http.StatusBadRequest return } if oldPost.UserId != c.Session.UserId { c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "oldUserId="+oldPost.UserId) c.Err.StatusCode = http.StatusForbidden return } if oldPost.DeleteAt != 0 { c.Err = model.NewAppError("updatePost", "You do not have the appropriate permissions", "Already delted id="+post.Id) c.Err.StatusCode = http.StatusForbidden return } } hashtags, _ := model.ParseHashtags(post.Message) if result := <-Srv.Store.Post().Update(oldPost, post.Message, hashtags); result.Err != nil { c.Err = result.Err return } else { rpost := result.Data.(*model.Post) message := model.NewMessage(c.Session.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED) message.Add("post_id", rpost.Id) message.Add("channel_id", rpost.ChannelId) message.Add("message", rpost.Message) store.PublishAndForget(message) w.Write([]byte(rpost.ToJson())) } }
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) { post := model.PostFromJson(r.Body) if post == nil { c.SetInvalidParam("updatePost", "post") return } cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, post.ChannelId, c.Session.UserId) pchan := Srv.Store.Post().Get(post.Id) if !c.HasPermissionsToChannel(cchan, "updatePost") { 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 } } hashtags, _ := model.ParseHashtags(post.Message) if result := <-Srv.Store.Post().Update(oldPost, post.Message, hashtags); result.Err != nil { c.Err = result.Err return } else { rpost := result.Data.(*model.Post) message := model.NewMessage(c.TeamId, rpost.ChannelId, c.Session.UserId, model.ACTION_POST_EDITED) message.Add("post", rpost.ToJson()) PublishAndForget(message) w.Write([]byte(rpost.ToJson())) } }
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { channelRole := "" if team.Email == user.Email { user.Roles = model.ROLE_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN } else { user.Roles = "" } user.MakeNonNil() if len(user.Props["theme"]) == 0 { user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor) } if result := <-Srv.Store.User().Save(user); result.Err != nil { c.Err = result.Err return nil } else { ruser := result.Data.(*model.User) // Do not error if user cannot be added to the town-square channel if cresult := <-Srv.Store.Channel().GetByName(team.Id, "town-square"); cresult.Err != nil { l4g.Error("Failed to get town-square err=%v", cresult.Err) } else { cm := &model.ChannelMember{ChannelId: cresult.Data.(*model.Channel).Id, UserId: ruser.Id, NotifyLevel: model.CHANNEL_NOTIFY_ALL, Roles: channelRole} if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil { l4g.Error("Failed to add member town-square err=%v", cmresult.Err) } } //fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to get town-square err=%v", cresult.Err) } } else { FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl) } ruser.Sanitize(map[string]bool{}) //This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) store.PublishAndForget(message) return ruser } }
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId) message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_VIEWED) message.Add("channel_id", id) store.PublishAndForget(message) result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) }
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { if !utils.Cfg.TeamSettings.EnableUserCreation { c.Err = model.NewAppError("CreateUser", "User creation has been disabled. Please ask your systems administrator for details.", "") return nil } channelRole := "" if team.Email == user.Email { user.Roles = model.ROLE_TEAM_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN } else { user.Roles = "" } user.MakeNonNil() if result := <-Srv.Store.User().Save(user); result.Err != nil { c.Err = result.Err l4g.Error("Couldn't save the user err=%v", result.Err) return nil } else { ruser := result.Data.(*model.User) // Soft error if there is an issue joining the default channels if err := JoinDefaultChannels(ruser, channelRole); err != nil { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } //fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { FireAndForgetVerifyEmail(result.Data.(*model.User).Id, ruser.Email, team.Name, team.DisplayName, c.GetSiteURL(), c.GetTeamURLFromTeam(team)) } ruser.Sanitize(map[string]bool{}) // This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) PublishAndForget(message) return ruser } }
func TestRedis(t *testing.T) { utils.LoadConfig("config.json") c := RedisClient() if c == nil { t.Fatal("should have a valid redis connection") } pubsub := c.PubSub() defer pubsub.Close() m := model.NewMessage(model.NewId(), model.NewId(), model.NewId(), model.ACTION_TYPING) m.Add("RootId", model.NewId()) err := pubsub.Subscribe(m.TeamId) if err != nil { t.Fatal(err) } // should be the subscribe success message // lets gobble that up if _, err := pubsub.Receive(); err != nil { t.Fatal(err) } PublishAndForget(m) fmt.Println("here1") if msg, err := pubsub.Receive(); err != nil { t.Fatal(err) } else { rmsg := GetMessageFromPayload(msg) if m.TeamId != rmsg.TeamId { t.Fatal("Ids do not match") } if m.Props["RootId"] != rmsg.Props["RootId"] { t.Fatal("Ids do not match") } } RedisClose() }
func CreateUser(c *Context, team *model.Team, user *model.User) *model.User { channelRole := "" if team.Email == user.Email { user.Roles = model.ROLE_ADMIN channelRole = model.CHANNEL_ROLE_ADMIN } else { user.Roles = "" } user.MakeNonNil() if len(user.Props["theme"]) == 0 { user.AddProp("theme", utils.Cfg.TeamSettings.DefaultThemeColor) } if result := <-Srv.Store.User().Save(user); result.Err != nil { c.Err = result.Err return nil } else { ruser := result.Data.(*model.User) // Soft error if there is an issue joining the default channels if err := JoinDefaultChannels(c, ruser, channelRole); err != nil { l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err) } //fireAndForgetWelcomeEmail(strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl+"/channels/town-square") if user.EmailVerified { if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { l4g.Error("Failed to set email verified err=%v", cresult.Err) } } else { FireAndForgetVerifyEmail(result.Data.(*model.User).Id, strings.Split(ruser.FullName, " ")[0], ruser.Email, team.Name, c.TeamUrl) } ruser.Sanitize(map[string]bool{}) // This message goes to every channel, so the channelId is irrelevant message := model.NewMessage(team.Id, "", ruser.Id, model.ACTION_NEW_USER) store.PublishAndForget(message) return ruser } }
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError { if channel.DeleteAt > 0 { return model.NewLocAppError("updateChannel", "api.channel.remove_user_from_channel.deleted.app_error", nil, "") } if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil { return cmresult.Err } InvalidateCacheForUser(userIdToRemove) message := model.NewMessage(channel.TeamId, channel.Id, userIdToRemove, model.ACTION_USER_REMOVED) message.Add("remover_id", removerUserId) go Publish(message) return nil }
func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel *model.Channel) *model.AppError { if channel.DeleteAt > 0 { return model.NewAppError("updateChannel", "The channel has been archived or deleted", "") } if cmresult := <-Srv.Store.Channel().RemoveMember(channel.Id, userIdToRemove); cmresult.Err != nil { return cmresult.Err } UpdateChannelAccessCacheAndForget(channel.TeamId, userIdToRemove, channel.Id) message := model.NewMessage(channel.TeamId, channel.Id, userIdToRemove, model.ACTION_USER_REMOVED) message.Add("remover_id", removerUserId) PublishAndForget(message) return nil }
func setCollapsePreference(c *Context, value string) *model.CommandResponse { pref := model.Preference{ UserId: c.Session.UserId, Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, Name: model.PREFERENCE_NAME_COLLAPSE_SETTING, Value: value, } if result := <-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.NewMessage("", "", c.Session.UserId, model.ACTION_PREFERENCE_CHANGED) socketMessage.Add("preference", pref.ToJson()) go Publish(socketMessage) return &model.CommandResponse{} }
func SendEphemeralPost(teamId, userId string, post *model.Post) { post.Type = model.POST_EPHEMERAL // fill in fields which haven't been specified which have sensible defaults if post.Id == "" { post.Id = model.NewId() } if post.CreateAt == 0 { post.CreateAt = model.GetMillis() } if post.Props == nil { post.Props = model.StringInterface{} } if post.Filenames == nil { post.Filenames = []string{} } message := model.NewMessage(teamId, post.ChannelId, userId, model.ACTION_EPHEMERAL_MESSAGE) message.Add("post", post.ToJson()) PublishAndForget(message) }
func CreateDirectChannel(userId string, otherUserId string) (*model.Channel, *model.AppError) { uc := Srv.Store.User().Get(otherUserId) channel := new(model.Channel) channel.DisplayName = "" channel.Name = model.GetDMNameFromIds(otherUserId, userId) channel.Header = "" channel.Type = model.CHANNEL_DIRECT if uresult := <-uc; uresult.Err != nil { return nil, model.NewLocAppError("CreateDirectChannel", "api.channel.create_direct_channel.invalid_user.app_error", nil, otherUserId) } cm1 := &model.ChannelMember{ UserId: userId, NotifyProps: model.GetDefaultChannelNotifyProps(), } cm2 := &model.ChannelMember{ UserId: otherUserId, NotifyProps: model.GetDefaultChannelNotifyProps(), } if result := <-Srv.Store.Channel().SaveDirectChannel(channel, cm1, cm2); result.Err != nil { if result.Err.Id == store.CHANNEL_EXISTS_ERROR { return result.Data.(*model.Channel), nil } else { return nil, result.Err } } else { message := model.NewMessage("", channel.Id, userId, model.ACTION_DIRECT_ADDED) message.Add("teammate_id", otherUserId) go Publish(message) return result.Data.(*model.Channel), nil } }
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] Srv.Store.Channel().UpdateLastViewedAt(id, c.Session.UserId) preference := model.Preference{ UserId: c.Session.UserId, Category: model.PREFERENCE_CATEGORY_LAST, Name: model.PREFERENCE_NAME_LAST_CHANNEL, Value: id, } Srv.Store.Preference().Save(&model.Preferences{preference}) message := model.NewMessage(c.Session.TeamId, id, c.Session.UserId, model.ACTION_CHANNEL_VIEWED) message.Add("channel_id", id) PublishAndForget(message) result := make(map[string]string) result["id"] = id w.Write([]byte(model.MapToJson(result))) }
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) { if channel.DeleteAt > 0 { return nil, model.NewAppError("AddUserToChannel", "The channel has been archived or deleted", "") } if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE { return nil, model.NewAppError("AddUserToChannel", "Can not add user to this channel type", "") } newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()} if cmresult := <-Srv.Store.Channel().SaveMember(newMember); cmresult.Err != nil { l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, cmresult.Err) return nil, model.NewAppError("AddUserToChannel", "Failed to add user to channel", "") } go func() { UpdateChannelAccessCache(channel.TeamId, user.Id, channel.Id) message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED) PublishAndForget(message) }() return newMember, nil }
func AddUserToChannel(user *model.User, channel *model.Channel) (*model.ChannelMember, *model.AppError) { if channel.DeleteAt > 0 { return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.deleted.app_error", nil, "") } if channel.Type != model.CHANNEL_OPEN && channel.Type != model.CHANNEL_PRIVATE { return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user_to_channel.type.app_error", nil, "") } newMember := &model.ChannelMember{ChannelId: channel.Id, UserId: user.Id, NotifyProps: model.GetDefaultChannelNotifyProps()} if cmresult := <-Srv.Store.Channel().SaveMember(newMember); cmresult.Err != nil { l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", user.Id, channel.Id, cmresult.Err) return nil, model.NewLocAppError("AddUserToChannel", "api.channel.add_user.to.channel.failed.app_error", nil, "") } go func() { UpdateChannelAccessCache(channel.TeamId, user.Id, channel.Id) message := model.NewMessage(channel.TeamId, channel.Id, user.Id, model.ACTION_USER_ADDED) PublishAndForget(message) }() return newMember, nil }
func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) { var channelName string var bodyText string var subjectText string var mentionedUsers []string if _, ok := profileMap[post.UserId]; !ok { l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel, only keep those profiles tempProfileMap := make(map[string]*model.User) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } // Add @all to keywords if user has them turned on // if profile.NotifyProps["all"] == "true" { // keywordMap["@all"] = append(keywordMap["@all"], profile.Id) // } // Add @channel to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id) } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.Fields(post.Message) for _, word := range splitMessage { var userIds []string // Non-case-sensitive check for regular keys if ids, match := keywordMap[strings.ToLower(word)]; match { userIds = append(userIds, ids...) } // Case-sensitive check for first name if ids, match := keywordMap[word]; match { userIds = append(userIds, ids...) } if len(userIds) == 0 { // No matches were found with the string split just on whitespace so try further splitting // the message on punctuation splitWords := strings.FieldsFunc(word, splitF) for _, splitWord := range splitWords { // Non-case-sensitive check for regular keys if ids, match := keywordMap[strings.ToLower(splitWord)]; match { userIds = append(userIds, ids...) } // Case-sensitive check for first name if ids, match := keywordMap[splitWord]; match { userIds = append(userIds, ids...) } } } for _, userId := range userIds { if post.UserId == userId && post.Props["from_webhook"] != "true" { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } for id := range toEmailMap { updateMentionCountAndForget(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } teamURL := c.GetSiteURL() + "/" + team.Name // Build and send the emails tm := time.Unix(post.CreateAt/1000, 0) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } userLocale := utils.GetUserTranslations(profileMap[id].Locale) if channel.Type == model.CHANNEL_DIRECT { bodyText = userLocale("api.post.send_notifications_and_forget.message_body") subjectText = userLocale("api.post.send_notifications_and_forget.message_subject") } else { bodyText = userLocale("api.post.send_notifications_and_forget.mention_body") subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject") channelName = channel.DisplayName } month := userLocale(tm.Month().String()) day := fmt.Sprintf("%d", tm.Day()) year := fmt.Sprintf("%d", tm.Year()) zone, _ := tm.Zone() subjectPage := utils.NewHTMLTemplate("post_subject", profileMap[id].Locale) subjectPage.Props["Subject"] = userLocale("api.templates.post_subject", map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName, "Month": month[:3], "Day": day, "Year": year}) subjectPage.Props["SiteName"] = utils.Cfg.TeamSettings.SiteName bodyPage := utils.NewHTMLTemplate("post_body", profileMap[id].Locale) bodyPage.Props["SiteURL"] = c.GetSiteURL() bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name bodyPage.Props["BodyText"] = bodyText bodyPage.Props["Button"] = userLocale("api.templates.post_body.button") bodyPage.Html["Info"] = template.HTML(userLocale("api.templates.post_body.info", map[string]interface{}{"ChannelName": channelName, "SenderName": senderName, "Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()), "TimeZone": zone, "Month": month, "Day": day})) // attempt to fill in a message body if the post doesn't have any text if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 { // extract the filenames from their paths and determine what type of files are attached filenames := make([]string, len(post.Filenames)) onlyImages := true for i, filename := range post.Filenames { var err error if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil { // this should never error since filepath was escaped using url.QueryEscape filenames[i] = filepath.Base(filename) } ext := filepath.Ext(filename) onlyImages = onlyImages && model.IsFileExtImage(ext) } filenamesString := strings.Join(filenames, ", ") var attachmentPrefix string if onlyImages { attachmentPrefix = "Image" } else { attachmentPrefix = "File" } if len(post.Filenames) > 1 { attachmentPrefix += "s" } bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent", map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString}) } if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err) } if *utils.Cfg.EmailSettings.SendPushNotifications { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) pushServer := *utils.Cfg.EmailSettings.PushNotificationServer if pushServer == model.MHPNS && (!utils.IsLicensed || !*utils.License.Features.MHPNS) { l4g.Warn(utils.T("api.post.send_notifications_and_forget.push_notification.mhpnsWarn")) } else { for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && (strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) { alreadySeen[session.DeviceId] = session.DeviceId msg := model.PushNotification{} if badge := <-Srv.Store.User().GetUnreadCount(id); badge.Err != nil { msg.Badge = 1 l4g.Error(utils.T("store.sql_user.get_unread_count.app_error"), id, badge.Err) } else { msg.Badge = int(badge.Data.(int64)) } msg.ServerId = utils.CfgDiagnosticId msg.ChannelId = channel.Id msg.ChannelName = channel.Name if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") { msg.Platform = model.PUSH_NOTIFY_APPLE msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") } else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") { msg.Platform = model.PUSH_NOTIFY_ANDROID msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") } if *utils.Cfg.EmailSettings.PushNotificationContents == model.FULL_NOTIFICATION { if channel.Type == model.CHANNEL_DIRECT { msg.Category = model.CATEGORY_DM msg.Message = "@" + senderName + ": " + model.ClearMentionTags(post.Message) } else { msg.Message = "@" + senderName + " @ " + channelName + ": " + model.ClearMentionTags(post.Message) } } else { if channel.Type == model.CHANNEL_DIRECT { msg.Category = model.CATEGORY_DM msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message") } else { msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName } } tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: *utils.Cfg.ServiceSettings.EnableInsecureOutgoingConnections}, } httpClient := &http.Client{Transport: tr} request, _ := http.NewRequest("POST", pushServer+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson())) l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message) if _, err := httpClient.Do(request); err != nil { l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err) } } } } } } } } message := model.NewMessage(c.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) message.Add("channel_type", channel.Type) if len(post.Filenames) != 0 { message.Add("otherFile", "true") for _, filename := range post.Filenames { ext := filepath.Ext(filename) if model.IsFileExtImage(ext) { message.Add("image", "true") break } } } if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } PublishAndForget(message) }
func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, channel *model.Channel) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team uchan := Srv.Store.User().GetProfiles(c.Session.TeamId) echan := Srv.Store.Channel().GetMembers(post.ChannelId) var channelName string var bodyText string var subjectText string if channel.Type == model.CHANNEL_DIRECT { bodyText = "You have one new message." subjectText = "New Direct Message" } else { bodyText = "You have one new mention." subjectText = "New Mention" channelName = channel.DisplayName } var mentionedUsers []string if result := <-uchan; result.Err != nil { l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", c.Session.TeamId, result.Err) return } else { profileMap := result.Data.(map[string]*model.User) if _, ok := profileMap[post.UserId]; !ok { l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel, only keep those profiles if eResult := <-echan; eResult.Err != nil { l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message) return } else { tempProfileMap := make(map[string]*model.User) members := eResult.Data.([]model.ChannelMember) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap } // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } // Add @all to keywords if user has them turned on // if profile.NotifyProps["all"] == "true" { // keywordMap["@all"] = append(keywordMap["@all"], profile.Id) // } // Add @channel to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id) } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.Fields(post.Message) for _, word := range splitMessage { var userIds []string // Non-case-sensitive check for regular keys if ids, match := keywordMap[strings.ToLower(word)]; match { userIds = append(userIds, ids...) } // Case-sensitive check for first name if ids, match := keywordMap[word]; match { userIds = append(userIds, ids...) } if len(userIds) == 0 { // No matches were found with the string split just on whitespace so try further splitting // the message on punctuation splitWords := strings.FieldsFunc(word, splitF) for _, splitWord := range splitWords { // Non-case-sensitive check for regular keys if ids, match := keywordMap[strings.ToLower(splitWord)]; match { userIds = append(userIds, ids...) } // Case-sensitive check for first name if ids, match := keywordMap[splitWord]; match { userIds = append(userIds, ids...) } } } for _, userId := range userIds { if post.UserId == userId { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } for id := range toEmailMap { updateMentionCountAndForget(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } teamURL := c.GetSiteURL() + "/" + team.Name // Build and send the emails tm := time.Unix(post.CreateAt/1000, 0) subjectPage := NewServerTemplatePage("post_subject") subjectPage.Props["SiteURL"] = c.GetSiteURL() subjectPage.Props["TeamDisplayName"] = team.DisplayName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year()) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } bodyPage := NewServerTemplatePage("post_body") bodyPage.Props["SiteURL"] = c.GetSiteURL() bodyPage.Props["Nickname"] = profileMap[id].FirstName bodyPage.Props["TeamDisplayName"] = team.DisplayName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour()) bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute()) bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["TimeZone"], _ = tm.Zone() bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name // attempt to fill in a message body if the post doesn't have any text if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 { // extract the filenames from their paths and determine what type of files are attached filenames := make([]string, len(post.Filenames)) onlyImages := true for i, filename := range post.Filenames { var err error if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil { // this should never error since filepath was escaped using url.QueryEscape filenames[i] = filepath.Base(filename) } ext := filepath.Ext(filename) onlyImages = onlyImages && model.IsFileExtImage(ext) } filenamesString := strings.Join(filenames, ", ") var attachmentPrefix string if onlyImages { attachmentPrefix = "Image" } else { attachmentPrefix = "File" } if len(post.Filenames) > 1 { attachmentPrefix += "s" } bodyPage.Props["PostMessage"] = fmt.Sprintf("%s: %s sent", attachmentPrefix, filenamesString) } if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } if *utils.Cfg.EmailSettings.SendPushNotifications { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && strings.HasPrefix(session.DeviceId, "apple:") { alreadySeen[session.DeviceId] = session.DeviceId msg := model.PushNotification{} msg.Platform = model.PUSH_NOTIFY_APPLE msg.Badge = 1 msg.DeviceId = strings.TrimPrefix(session.DeviceId, "apple:") msg.ServerId = utils.CfgDiagnosticId if channel.Type == model.CHANNEL_DIRECT { msg.Message = senderName + " sent you a direct message" } else { msg.Message = senderName + " mentioned you in " + channelName } httpClient := http.Client{} request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson())) l4g.Debug("Sending push notification to " + msg.DeviceId + " with msg of '" + msg.Message + "'") if _, err := httpClient.Do(request); err != nil { l4g.Error("Failed to send push notificationid=%v, err=%v", id, err) } } } } } } } } message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) if len(post.Filenames) != 0 { message.Add("otherFile", "true") for _, filename := range post.Filenames { ext := filepath.Ext(filename) if model.IsFileExtImage(ext) { message.Add("image", "true") break } } } if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } PublishAndForget(message) }() }
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 } cchan := Srv.Store.Channel().CheckPermissionsTo(c.TeamId, channelId, c.Session.UserId) pchan := 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 !c.HasPermissionsToChannel(cchan, "deletePost") && !c.IsTeamAdmin() { return } 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 && !c.IsTeamAdmin() { c.Err = model.NewLocAppError("deletePost", "api.post.delete_post.permissions.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden return } if dresult := <-Srv.Store.Post().Delete(postId, model.GetMillis()); dresult.Err != nil { c.Err = dresult.Err return } message := model.NewMessage(c.TeamId, post.ChannelId, c.Session.UserId, model.ACTION_POST_DELETED) message.Add("post", post.ToJson()) PublishAndForget(message) DeletePostFilesAndForget(c.TeamId, post) result := make(map[string]string) result["id"] = postId w.Write([]byte(model.MapToJson(result))) } }
func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) { if _, ok := profileMap[post.UserId]; !ok { l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId) return } mentionedUserIds := make(map[string]bool) alwaysNotifyUserIds := []string{} if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] } else { otherUserId = userIds[0] } mentionedUserIds[otherUserId] = true } else { // Find out who is a member of the channel, only keep those profiles tempProfileMap := make(map[string]*model.User) for _, member := range members { if profile, ok := profileMap[member.UserId]; ok { tempProfileMap[member.UserId] = profile } } profileMap = tempProfileMap // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } // Add @channel and @all to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id) keywordMap["@all"] = append(keywordMap["@all"], profile.Id) } if profile.NotifyProps["push"] == model.USER_NOTIFY_ALL && (post.UserId != profile.Id || post.Props["from_webhook"] == "true") && !post.IsSystemMessage() { alwaysNotifyUserIds = append(alwaysNotifyUserIds, profile.Id) } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.Fields(post.Message) for _, word := range splitMessage { var userIds []string // Non-case-sensitive check for regular keys if ids, match := keywordMap[strings.ToLower(word)]; match { userIds = append(userIds, ids...) } // Case-sensitive check for first name if ids, match := keywordMap[word]; match { userIds = append(userIds, ids...) } if len(userIds) == 0 { // No matches were found with the string split just on whitespace so try further splitting // the message on punctuation splitWords := strings.FieldsFunc(word, splitF) for _, splitWord := range splitWords { // Non-case-sensitive check for regular keys if ids, match := keywordMap[strings.ToLower(splitWord)]; match { userIds = append(userIds, ids...) } // Case-sensitive check for first name if ids, match := keywordMap[splitWord]; match { userIds = append(userIds, ids...) } } } for _, userId := range userIds { if post.UserId == userId && post.Props["from_webhook"] != "true" { continue } mentionedUserIds[userId] = true } } for id := range mentionedUserIds { go updateMentionCount(post.ChannelId, id) } } mentionedUsersList := make([]string, 0, len(mentionedUserIds)) senderName := "" if post.IsSystemMessage() { senderName = c.T("system.message.name") } else if profile, ok := profileMap[post.UserId]; ok { senderName = profile.Username } for id := range mentionedUserIds { mentionedUsersList = append(mentionedUsersList, id) } if utils.Cfg.EmailSettings.SendEmailNotifications { for _, id := range mentionedUsersList { userAllowsEmails := profileMap[id].NotifyProps["email"] != "false" if userAllowsEmails && (profileMap[id].IsAway() || profileMap[id].IsOffline()) { sendNotificationEmail(c, post, profileMap[id], channel, team, senderName) } } } sendPushNotifications := false if *utils.Cfg.EmailSettings.SendPushNotifications { pushServer := *utils.Cfg.EmailSettings.PushNotificationServer if pushServer == model.MHPNS && (!utils.IsLicensed || !*utils.License.Features.MHPNS) { l4g.Warn(utils.T("api.post.send_notifications_and_forget.push_notification.mhpnsWarn")) sendPushNotifications = false } else { sendPushNotifications = true } } if sendPushNotifications { for _, id := range mentionedUsersList { if profileMap[id].NotifyProps["push"] != "none" { sendPushNotification(post, profileMap[id], channel, senderName, true) } } for _, id := range alwaysNotifyUserIds { if _, ok := mentionedUserIds[id]; !ok { sendPushNotification(post, profileMap[id], channel, senderName, false) } } } message := model.NewMessage(c.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) message.Add("channel_type", channel.Type) message.Add("channel_display_name", channel.DisplayName) message.Add("sender_name", senderName) message.Add("team_id", team.Id) if len(post.Filenames) != 0 { message.Add("otherFile", "true") for _, filename := range post.Filenames { ext := filepath.Ext(filename) if model.IsFileExtImage(ext) { message.Add("image", "true") break } } } if len(mentionedUsersList) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsersList)) } go Publish(message) }
func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team uchan := Srv.Store.User().GetProfiles(teamId) echan := Srv.Store.Channel().GetMembers(post.ChannelId) cchan := Srv.Store.Channel().Get(post.ChannelId) tchan := Srv.Store.Team().Get(teamId) var channel *model.Channel var channelName string var bodyText string var subjectText string if result := <-cchan; result.Err != nil { l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err) return } else { channel = result.Data.(*model.Channel) if channel.Type == model.CHANNEL_DIRECT { bodyText = "You have one new message." subjectText = "New Direct Message" } else { bodyText = "You have one new mention." subjectText = "New Mention" channelName = channel.DisplayName } } var mentionedUsers []string if result := <-uchan; result.Err != nil { l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err) return } else { profileMap := result.Data.(map[string]*model.User) if _, ok := profileMap[post.UserId]; !ok { l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel, only keep those profiles if eResult := <-echan; eResult.Err != nil { l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message) return } else { tempProfileMap := make(map[string]*model.User) members := eResult.Data.([]model.ChannelMember) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap } // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } // Add @all to keywords if user has them turned on if profile.NotifyProps["all"] == "true" { keywordMap["@all"] = append(keywordMap["@all"], profile.Id) } // Add @channel to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id) } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.FieldsFunc(post.Message, splitF) for _, word := range splitMessage { // Non-case-sensitive check for regular keys userIds1, keyMatch := keywordMap[strings.ToLower(word)] // Case-sensitive check for first name userIds2, firstNameMatch := keywordMap[word] userIds := append(userIds1, userIds2...) // If one of the non-case-senstive keys or the first name matches the word // then we add en entry to the sendEmail map if keyMatch || firstNameMatch { for _, userId := range userIds { if post.UserId == userId { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } } for id := range toEmailMap { fireAndForgetMentionUpdate(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } var teamDisplayName string var teamURL string if result := <-tchan; result.Err != nil { l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) return } else { teamDisplayName = result.Data.(*model.Team).DisplayName teamURL = siteURL + "/" + result.Data.(*model.Team).Name } // Build and send the emails location, _ := time.LoadLocation("UTC") tm := time.Unix(post.CreateAt/1000, 0).In(location) subjectPage := NewServerTemplatePage("post_subject") subjectPage.Props["SiteURL"] = siteURL subjectPage.Props["TeamDisplayName"] = teamDisplayName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year()) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } bodyPage := NewServerTemplatePage("post_body") bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Nickname"] = profileMap[id].FirstName bodyPage.Props["TeamDisplayName"] = teamDisplayName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour()) bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute()) bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name // attempt to fill in a message body if the post doesn't have any text if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 { // extract the filenames from their paths and determine what type of files are attached filenames := make([]string, len(post.Filenames)) onlyImages := true for i, filename := range post.Filenames { var err error if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil { // this should never error since filepath was escaped using url.QueryEscape filenames[i] = filepath.Base(filename) } ext := filepath.Ext(filename) onlyImages = onlyImages && model.IsFileExtImage(ext) } filenamesString := strings.Join(filenames, ", ") var attachmentPrefix string if onlyImages { attachmentPrefix = "Image" } else { attachmentPrefix = "File" } if len(post.Filenames) > 1 { attachmentPrefix += "s" } bodyPage.Props["PostMessage"] = fmt.Sprintf("%s: %s sent", attachmentPrefix, filenamesString) } if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" { alreadySeen[session.DeviceId] = session.DeviceId utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1) } } } } } } } message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) if len(post.Filenames) != 0 { message.Add("otherFile", "true") for _, filename := range post.Filenames { ext := filepath.Ext(filename) if model.IsFileExtImage(ext) { message.Add("image", "true") break } } } if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } PublishAndForget(message) }() }
func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team uchan := Srv.Store.User().GetProfiles(teamId) echan := Srv.Store.Channel().GetMembers(post.ChannelId) cchan := Srv.Store.Channel().Get(post.ChannelId) tchan := Srv.Store.Team().Get(teamId) var channel *model.Channel var channelName string var bodyText string var subjectText string if result := <-cchan; result.Err != nil { l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err) return } else { channel = result.Data.(*model.Channel) if channel.Type == model.CHANNEL_DIRECT { bodyText = "You have one new message." subjectText = "New Direct Message" } else { bodyText = "You have one new mention." subjectText = "New Mention" channelName = channel.DisplayName } } var mentionedUsers []string if result := <-uchan; result.Err != nil { l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err) return } else { profileMap := result.Data.(map[string]*model.User) if _, ok := profileMap[post.UserId]; !ok { l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel only keep those profiles if eResult := <-echan; eResult.Err != nil { l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message) return } else { tempProfileMap := make(map[string]*model.User) members := eResult.Data.([]model.ChannelMember) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap } // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { splitName := strings.Split(profile.FullName, " ") if len(splitName) > 0 && splitName[0] != "" { keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id) } } } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.FieldsFunc(strings.Replace(post.Message, "<br>", " ", -1), splitF) for _, word := range splitMessage { // Non-case-sensitive check for regular keys userIds1, keyMatch := keywordMap[strings.ToLower(word)] // Case-sensitive check for first name userIds2, firstNameMatch := keywordMap[word] userIds := append(userIds1, userIds2...) // If one of the non-case-senstive keys or the first name matches the word // then we add en entry to the sendEmail map if keyMatch || firstNameMatch { for _, userId := range userIds { if post.UserId == userId { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } } for id, _ := range toEmailMap { fireAndForgetMentionUpdate(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } var teamName string if result := <-tchan; result.Err != nil { l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) return } else { teamName = result.Data.(*model.Team).Name } // Build and send the emails location, _ := time.LoadLocation("UTC") tm := time.Unix(post.CreateAt/1000, 0).In(location) subjectPage := NewServerTemplatePage("post_subject", teamUrl) subjectPage.Props["TeamName"] = teamName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year()) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } firstName := strings.Split(profileMap[id].FullName, " ")[0] bodyPage := NewServerTemplatePage("post_body", teamUrl) bodyPage.Props["FullName"] = firstName bodyPage.Props["TeamName"] = teamName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour()) bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute()) bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" { alreadySeen[session.DeviceId] = session.DeviceId utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1) } } } } } } } message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } store.PublishAndForget(message) }() }
func TestSocket(t *testing.T) { Setup() url := "ws://*****:*****@nowhere.com", Type: model.TEAM_OPEN} team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team) user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "*****@*****.**", Nickname: "Corey Hulen", Password: "******"} user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user1.Id)) Client.LoginByEmail(team.Domain, user1.Email, "pwd") channel1 := &model.Channel{DisplayName: "Test Web Scoket 1", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel) channel2 := &model.Channel{DisplayName: "Test Web Scoket 2", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id} channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel) header1 := http.Header{} header1.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) c1, _, err := websocket.DefaultDialer.Dial(url, header1) if err != nil { t.Fatal(err) } user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "*****@*****.**", Nickname: "Corey Hulen", Password: "******"} user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User) store.Must(Srv.Store.User().VerifyEmail(user2.Id)) Client.LoginByEmail(team.Domain, user2.Email, "pwd") header2 := http.Header{} header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) c2, _, err := websocket.DefaultDialer.Dial(url, header2) if err != nil { t.Fatal(err) } time.Sleep(300 * time.Millisecond) Client.Must(Client.JoinChannel(channel1.Id)) // Read the join channel message that gets generated var rmsg model.Message if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } // Test sending message without a channelId m := model.NewMessage("", "", "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) c1.WriteJSON(m) if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } if team.Id != rmsg.TeamId { t.Fatal("Ids do not match") } if m.Props["RootId"] != rmsg.Props["RootId"] { t.Fatal("Ids do not match") } // Test sending messsage to Channel you have access to m = model.NewMessage("", channel1.Id, "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) c1.WriteJSON(m) if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } if team.Id != rmsg.TeamId { t.Fatal("Ids do not match") } if m.Props["RootId"] != rmsg.Props["RootId"] { t.Fatal("Ids do not match") } // Test sending message to Channel you *do not* have access too m = model.NewMessage("", channel2.Id, "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) c1.WriteJSON(m) go func() { if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } t.Fatal(err) }() time.Sleep(2 * time.Second) }
func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] sc := Srv.Store.Channel().Get(id) scm := Srv.Store.Channel().GetMember(id, c.Session.UserId) uc := Srv.Store.User().Get(c.Session.UserId) ihc := Srv.Store.Webhook().GetIncomingByChannel(id) ohc := 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 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) user := uresult.Data.(*model.User) channelMember := scmresult.Data.(model.ChannelMember) incomingHooks := ihcresult.Data.([]*model.IncomingWebhook) outgoingHooks := ohcresult.Data.([]*model.OutgoingWebhook) if !c.HasPermissionsToTeam(channel.TeamId, "deleteChannel") { return } if !strings.Contains(channelMember.Roles, model.CHANNEL_ROLE_ADMIN) && !c.IsTeamAdmin() { c.Err = model.NewLocAppError("deleteChannel", "api.channel.delete_channel.permissions.app_error", nil, "") c.Err.StatusCode = http.StatusForbidden 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 } now := model.GetMillis() for _, hook := range incomingHooks { go func() { if result := <-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 { go func() { if result := <-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 := <-Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil { c.Err = dresult.Err return } c.LogAudit("name=" + channel.Name) go func() { InvalidateCacheForChannel(channel.Id) message := model.NewMessage(c.TeamId, channel.Id, c.Session.UserId, model.ACTION_CHANNEL_DELETED) go Publish(message) post := &model.Post{ ChannelId: channel.Id, Message: fmt.Sprintf(c.T("api.channel.delete_channel.archived"), user.Username), Type: model.POST_CHANNEL_DELETED, } if _, err := CreatePost(c, post, false); err != nil { l4g.Error(utils.T("api.channel.delete_channel.failed_post.error"), err) } }() result := make(map[string]string) result["id"] = channel.Id w.Write([]byte(model.MapToJson(result))) } }
func addChannelMember(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["id"] data := model.MapFromJson(r.Body) userId := data["user_id"] if len(userId) != 26 { c.SetInvalidParam("addChannelMember", "user_id") return } cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, id, c.Session.UserId) sc := Srv.Store.Channel().Get(id) ouc := Srv.Store.User().Get(c.Session.UserId) nuc := Srv.Store.User().Get(userId) // Only need to be a member of the channel to add a new member if !c.HasPermissionsToChannel(cchan, "addChannelMember") { return } if nresult := <-nuc; nresult.Err != nil { c.Err = model.NewAppError("addChannelMember", "Failed to find user to be added", "") return } else if cresult := <-sc; cresult.Err != nil { c.Err = model.NewAppError("addChannelMember", "Failed to find channel", "") return } else { channel := cresult.Data.(*model.Channel) nUser := nresult.Data.(*model.User) if channel.DeleteAt > 0 { c.Err = model.NewAppError("updateChannel", "The channel has been archived or deleted", "") c.Err.StatusCode = http.StatusBadRequest return } if oresult := <-ouc; oresult.Err != nil { c.Err = model.NewAppError("addChannelMember", "Failed to find user doing the adding", "") return } else { oUser := oresult.Data.(*model.User) cm := &model.ChannelMember{ChannelId: channel.Id, UserId: userId, NotifyLevel: model.CHANNEL_NOTIFY_ALL} if cmresult := <-Srv.Store.Channel().SaveMember(cm); cmresult.Err != nil { l4g.Error("Failed to add member user_id=%v channel_id=%v err=%v", userId, id, cmresult.Err) c.Err = model.NewAppError("addChannelMember", "Failed to add user to channel", "") return } post := &model.Post{ChannelId: id, Message: fmt.Sprintf( `%v added to the channel by %v`, nUser.Username, oUser.Username), Type: model.POST_JOIN_LEAVE} if _, err := CreatePost(c, post, false); err != nil { l4g.Error("Failed to post add message %v", err) c.Err = model.NewAppError("addChannelMember", "Failed to add member to channel", "") return } c.LogAudit("name=" + channel.Name + " user_id=" + userId) message := model.NewMessage(c.Session.TeamId, "", userId, model.ACTION_USER_ADDED) PublishAndForget(message) <-Srv.Store.Channel().UpdateLastViewedAt(id, oUser.Id) w.Write([]byte(cm.ToJson())) } } }
func TestSocket(t *testing.T) { th := Setup().InitBasic() Client := th.BasicClient team := th.BasicTeam channel1 := th.BasicChannel channel2 := th.CreateChannel(Client, team) Client.Must(Client.AddChannelMember(channel1.Id, th.BasicUser2.Id)) url := "ws://localhost" + utils.Cfg.ServiceSettings.ListenAddress + model.API_URL_SUFFIX + "/users/websocket" header1 := http.Header{} header1.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) c1, _, err := websocket.DefaultDialer.Dial(url, header1) if err != nil { t.Fatal(err) } th.LoginBasic2() header2 := http.Header{} header2.Set(model.HEADER_AUTH, "BEARER "+Client.AuthToken) c2, _, err := websocket.DefaultDialer.Dial(url, header2) if err != nil { t.Fatal(err) } time.Sleep(300 * time.Millisecond) var rmsg model.Message // Test sending message without a channelId m := model.NewMessage(team.Id, "", "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) c1.WriteJSON(m) if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } t.Log(rmsg.ToJson()) if team.Id != rmsg.TeamId { t.Fatal("Ids do not match") } if m.Props["RootId"] != rmsg.Props["RootId"] { t.Fatal("Ids do not match") } // Test sending messsage to Channel you have access to m = model.NewMessage(team.Id, channel1.Id, "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) c1.WriteJSON(m) if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } if team.Id != rmsg.TeamId { t.Fatal("Ids do not match") } if m.Props["RootId"] != rmsg.Props["RootId"] { t.Fatal("Ids do not match") } // Test sending message to Channel you *do not* have access too m = model.NewMessage("", channel2.Id, "", model.ACTION_TYPING) m.Add("RootId", model.NewId()) m.Add("ParentId", model.NewId()) c1.WriteJSON(m) go func() { if err := c2.ReadJSON(&rmsg); err != nil { t.Fatal(err) } t.Fatal(err) }() time.Sleep(2 * time.Second) }