Skip to content

Commit

Permalink
Merge 9a89d56 into c7bf422
Browse files Browse the repository at this point in the history
  • Loading branch information
enahum committed Feb 22, 2017
2 parents c7bf422 + 9a89d56 commit 690a2a5
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 3 deletions.
33 changes: 33 additions & 0 deletions api/channel_test.go
Expand Up @@ -1584,6 +1584,25 @@ func TestUpdateNotifyProps(t *testing.T) {
t.Fatal("NotifyProps[\"mark_unread\"] did not update properly")
}

// test updating push notification preferences
delete(data, "desktop")
delete(data, "mark_unread")
data["push"] = model.CHANNEL_NOTIFY_MENTION
if result, err := Client.UpdateNotifyProps(data); err != nil {
t.Fatal(err)
} else if notifyProps := result.Data.(map[string]string); notifyProps["push"] != model.CHANNEL_NOTIFY_MENTION {
t.Fatal("NotifyProps[\"push\"] did not update properly")
}

// test updating email preferences
delete(data, "push")
data["email"] = "true"
if result, err := Client.UpdateNotifyProps(data); err != nil {
t.Fatal(err)
} else if notifyProps := result.Data.(map[string]string); notifyProps["email"] != "true" {
t.Fatal("NotifyProps[\"email\"] did not update properly")
}

// test error cases
data["user_id"] = "junk"
if _, err := Client.UpdateNotifyProps(data); err == nil {
Expand Down Expand Up @@ -1618,6 +1637,20 @@ func TestUpdateNotifyProps(t *testing.T) {
t.Fatal("Should have errored - bad mark unread level")
}

data["desktop"] = model.CHANNEL_NOTIFY_ALL
data["mark_unread"] = model.CHANNEL_MARK_UNREAD_ALL
data["push"] = "junk"
data["email"] = "true"
if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - bad push level")
}

data["push"] = model.CHANNEL_NOTIFY_ALL
data["email"] = "junk"
if _, err := Client.UpdateNotifyProps(data); err == nil {
t.Fatal("Should have errored - bad email notification option")
}

th.LoginBasic2()

data["channel_id"] = channel1.Id
Expand Down
9 changes: 9 additions & 0 deletions app/channel.go
Expand Up @@ -234,10 +234,19 @@ func UpdateChannelMemberNotifyProps(data map[string]string, channelId string, us
member.NotifyProps["desktop"] = desktop
}

if email, exists := data["email"]; exists {
member.NotifyProps["email"] = email
}

if push, exists := data["push"]; exists {
member.NotifyProps["push"] = push
}

if result := <-Srv.Store.Channel().UpdateMember(member); result.Err != nil {
return nil, result.Err
} else {
InvalidateCacheForUser(userId)
InvalidateCacheForChannelMembersNotifyProps(channelId)
return member, nil
}
}
Expand Down
37 changes: 34 additions & 3 deletions app/notification.go
Expand Up @@ -26,6 +26,7 @@ import (

func SendNotifications(post *model.Post, team *model.Team, channel *model.Channel, sender *model.User) ([]string, *model.AppError) {
pchan := Srv.Store.User().GetAllProfilesInChannel(channel.Id, true)
cmnchan := Srv.Store.Channel().GetAllChannelMembersNotifyPropsForChannel(channel.Id, true)
var fchan store.StoreChannel

if len(post.FileIds) != 0 {
Expand All @@ -39,6 +40,13 @@ func SendNotifications(post *model.Post, team *model.Team, channel *model.Channe
profileMap = result.Data.(map[string]*model.User)
}

var channelMemberNotifyPropsMap map[string]model.StringMap
if result := <-cmnchan; result.Err != nil {
return nil, result.Err
} else {
channelMemberNotifyPropsMap = result.Data.(map[string]model.StringMap)
}

mentionedUserIds := make(map[string]bool)
allActivityPushUserIds := []string{}
hereNotification := false
Expand Down Expand Up @@ -138,6 +146,11 @@ func SendNotifications(post *model.Post, team *model.Team, channel *model.Channe
if utils.Cfg.EmailSettings.SendEmailNotifications {
for _, id := range mentionedUsersList {
userAllowsEmails := profileMap[id].NotifyProps["email"] != "false"
if channelEmail, ok := channelMemberNotifyPropsMap[id]["email"]; ok {
if channelEmail != model.CHANNEL_NOTIFY_DEFAULT {
userAllowsEmails = channelEmail != "false"
}
}

var status *model.Status
var err *model.AppError
Expand Down Expand Up @@ -245,7 +258,7 @@ func SendNotifications(post *model.Post, team *model.Team, channel *model.Channe
}

if DoesStatusAllowPushNotification(profileMap[id], status, post.ChannelId) {
sendPushNotification(post, profileMap[id], channel, senderName[id], true)
sendPushNotification(post, profileMap[id], channel, senderName[id], channelMemberNotifyPropsMap[id], true)
}
}

Expand All @@ -258,7 +271,7 @@ func SendNotifications(post *model.Post, team *model.Team, channel *model.Channe
}

if DoesStatusAllowPushNotification(profileMap[id], status, post.ChannelId) {
sendPushNotification(post, profileMap[id], channel, senderName[id], false)
sendPushNotification(post, profileMap[id], channel, senderName[id], channelMemberNotifyPropsMap[id], false)
}
}
}
Expand Down Expand Up @@ -442,14 +455,22 @@ func GetMessageForNotification(post *model.Post, translateFunc i18n.TranslateFun
}
}

func sendPushNotification(post *model.Post, user *model.User, channel *model.Channel, senderName string, wasMentioned bool) *model.AppError {
func sendPushNotification(post *model.Post, user *model.User, channel *model.Channel, senderName string, channelNotifyProps model.StringMap, wasMentioned bool) *model.AppError {
sessions, err := getMobileAppSessions(user.Id)
if err != nil {
return err
}

var channelName string

if channelNotify, ok := channelNotifyProps["push"]; ok {
if channelNotify == model.USER_NOTIFY_NONE {
return nil
} else if channelNotify == model.USER_NOTIFY_MENTION && !wasMentioned {
return nil
}
}

if channel.Type == model.CHANNEL_DIRECT {
channelName = senderName
} else {
Expand Down Expand Up @@ -712,3 +733,13 @@ func GetMentionKeywordsInChannel(profiles map[string]*model.User) map[string][]s

return keywords
}

func shouldSendPushNotification(channelNotifyProps model.StringMap) bool {
if push, exists := channelNotifyProps["push"]; exists {
if push == "none" {
return false
}
}

return true
}
12 changes: 12 additions & 0 deletions app/web_hub.go
Expand Up @@ -134,6 +134,18 @@ func InvalidateCacheForChannelMembersSkipClusterSend(channelId string) {
Srv.Store.Channel().InvalidateMemberCount(channelId)
}

func InvalidateCacheForChannelMembersNotifyProps(channelId string) {
InvalidateCacheForChannelMembersNotifyPropsSkipClusterSend(channelId)

if cluster := einterfaces.GetClusterInterface(); cluster != nil {
cluster.InvalidateCacheForChannelMembersNotifyProps(channelId)
}
}

func InvalidateCacheForChannelMembersNotifyPropsSkipClusterSend(channelId string) {
Srv.Store.Channel().InvalidateCacheForChannelMembersNotifyProps(channelId)
}

func InvalidateCacheForChannelByNameSkipClusterSend(teamId, name string) {
Srv.Store.Channel().InvalidateChannelByName(teamId, name)
}
Expand Down
1 change: 1 addition & 0 deletions einterfaces/cluster.go
Expand Up @@ -17,6 +17,7 @@ type ClusterInterface interface {
InvalidateCacheForChannel(channelId string)
InvalidateCacheForChannelByName(teamId, name string)
InvalidateCacheForChannelMembers(channelId string)
InvalidateCacheForChannelMembersNotifyProps(channelId string)
InvalidateCacheForChannelPosts(channelId string)
InvalidateCacheForWebhook(webhookId string)
Publish(event *model.WebSocketEvent)
Expand Down
8 changes: 8 additions & 0 deletions i18n/en.json
Expand Up @@ -3515,10 +3515,18 @@
"id": "model.channel_member.is_valid.role.app_error",
"translation": "Invalid role"
},
{
"id": "model.channel_member.is_valid.email_value.app_error",
"translation": "Invalid email notification value"
},
{
"id": "model.channel_member.is_valid.unread_level.app_error",
"translation": "Invalid mark unread level"
},
{
"id": "model.channel_member.is_valid.push_level.app_error",
"translation": "Invalid push notification level"
},
{
"id": "model.channel_member.is_valid.user_id.app_error",
"translation": "Invalid user id"
Expand Down
20 changes: 20 additions & 0 deletions model/channel_member.go
Expand Up @@ -100,6 +100,20 @@ func (o *ChannelMember) IsValid() *AppError {
nil, "mark_unread_level="+markUnreadLevel)
}

if pushLevel, ok := o.NotifyProps["push"]; ok {
if len(pushLevel) > 20 || !IsChannelNotifyLevelValid(pushLevel) {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.push_level.app_error",
nil, "push_notification_level="+pushLevel)
}
}

if sendEmail, ok := o.NotifyProps["email"]; ok {
if len(sendEmail) > 20 || !IsSendEmailValid(sendEmail) {
return NewLocAppError("ChannelMember.IsValid", "model.channel_member.is_valid.email_value.app_error",
nil, "push_notification_level="+sendEmail)
}
}

return nil
}

Expand All @@ -126,9 +140,15 @@ func IsChannelMarkUnreadLevelValid(markUnreadLevel string) bool {
return markUnreadLevel == CHANNEL_MARK_UNREAD_ALL || markUnreadLevel == CHANNEL_MARK_UNREAD_MENTION
}

func IsSendEmailValid(sendEmail string) bool {
return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false"
}

func GetDefaultChannelNotifyProps() StringMap {
return StringMap{
"desktop": CHANNEL_NOTIFY_DEFAULT,
"mark_unread": CHANNEL_MARK_UNREAD_ALL,
"push": CHANNEL_NOTIFY_DEFAULT,
"email": CHANNEL_NOTIFY_DEFAULT,
}
}
68 changes: 68 additions & 0 deletions store/sql_channel_store.go
Expand Up @@ -25,6 +25,9 @@ const (
ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE = model.SESSION_CACHE_SIZE
ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SEC = 900 // 15 mins

ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE = model.SESSION_CACHE_SIZE
ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SEC = 1800 // 30 mins

CHANNEL_MEMBERS_COUNTS_CACHE_SIZE = model.CHANNEL_CACHE_SIZE
CHANNEL_MEMBERS_COUNTS_CACHE_SEC = 1800 // 30 mins

Expand All @@ -37,12 +40,14 @@ type SqlChannelStore struct {

var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE)
var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE)
var allChannelMembersNotifyPropsForChannelCache = utils.NewLru(ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE)
var channelCache = utils.NewLru(model.CHANNEL_CACHE_SIZE)
var channelByNameCache = utils.NewLru(model.CHANNEL_CACHE_SIZE)

func ClearChannelCaches() {
channelMemberCountsCache.Purge()
allChannelMembersForUserCache.Purge()
allChannelMembersNotifyPropsForChannelCache.Purge()
channelCache.Purge()
channelByNameCache.Purge()
}
Expand Down Expand Up @@ -919,6 +924,69 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac
return storeChannel
}

func (us SqlChannelStore) InvalidateCacheForChannelMembersNotifyProps(channelId string) {
allChannelMembersNotifyPropsForChannelCache.Remove(channelId)
}

type allChannelMemberNotifyProps struct {
UserId string
NotifyProps model.StringMap
}

func (s SqlChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId string, allowFromCache bool) StoreChannel {
storeChannel := make(StoreChannel, 1)

go func() {
result := StoreResult{}
metrics := einterfaces.GetMetricsInterface()

if allowFromCache {
if cacheItem, ok := allChannelMembersNotifyPropsForChannelCache.Get(channelId); ok {
if metrics != nil {
metrics.IncrementMemCacheHitCounter("All Channel Members Notify Props for Channel")
}
result.Data = cacheItem.(map[string]model.StringMap)
storeChannel <- result
close(storeChannel)
return
} else {
if metrics != nil {
metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel")
}
}
} else {
if metrics != nil {
metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel")
}
}

var data []allChannelMemberNotifyProps
_, err := s.GetReplica().Select(&data, `
SELECT ChannelMembers.UserId, ChannelMembers.NotifyProps
FROM Channels, ChannelMembers
WHERE Channels.Id = ChannelMembers.ChannelId AND ChannelMembers.ChannelId = :ChannelId`, map[string]interface{}{"ChannelId": channelId})

if err != nil {
result.Err = model.NewLocAppError("SqlChannelStore.GetAllChannelMembersPropsForChannel", "store.sql_channel.get_members.app_error", nil, "channelId="+channelId+", err="+err.Error())
} else {

props := make(map[string]model.StringMap)
for i := range data {
props[data[i].UserId] = data[i].NotifyProps
}

result.Data = props

allChannelMembersNotifyPropsForChannelCache.AddWithExpiresInSecs(channelId, props, ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SEC)
}

storeChannel <- result
close(storeChannel)
}()

return storeChannel
}

func (us SqlChannelStore) InvalidateMemberCount(channelId string) {
channelMemberCountsCache.Remove(channelId)
}
Expand Down
2 changes: 2 additions & 0 deletions store/store.go
Expand Up @@ -110,6 +110,8 @@ type ChannelStore interface {
GetAllChannelMembersForUser(userId string, allowFromCache bool) StoreChannel
InvalidateAllChannelMembersForUser(userId string)
IsUserInChannelUseCache(userId string, channelId string) bool
GetAllChannelMembersNotifyPropsForChannel(channelId string, allowFromCache bool) StoreChannel
InvalidateCacheForChannelMembersNotifyProps(channelId string)
GetMemberForPost(postId string, userId string) StoreChannel
InvalidateMemberCount(channelId string)
GetMemberCountFromCache(channelId string) int64
Expand Down

0 comments on commit 690a2a5

Please sign in to comment.