Skip to content

Commit

Permalink
[MM-8404] Channel notification setting for disabling channel mentions (
Browse files Browse the repository at this point in the history
…#9777)

* Channel notification setting for disabling channel mentions

* Updates unit tests (#MM-8404)

* Adds constants (#MM-8404)

* Refactors if statement and adds unit test (#MM-8404)

* Moves ignore_channel_mentions_notify_prop constant to channel model (#MM8484)
  • Loading branch information
letsila authored and hmhealey committed Nov 14, 2018
1 parent fa0aecc commit 4aca95f
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 20 deletions.
4 changes: 4 additions & 0 deletions app/channel.go
Expand Up @@ -663,6 +663,10 @@ func (a *App) UpdateChannelMemberNotifyProps(data map[string]string, channelId s
member.NotifyProps[model.PUSH_NOTIFY_PROP] = push
}

if ignoreChannelMentions, exists := data[model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP]; exists {
member.NotifyProps[model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP] = ignoreChannelMentions
}

result := <-a.Srv.Store.Channel().UpdateMember(member)
if result.Err != nil {
return nil, result.Err
Expand Down
13 changes: 10 additions & 3 deletions app/notification.go
Expand Up @@ -84,7 +84,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
}

} else {
keywords := a.GetMentionKeywordsInChannel(profileMap, post.Type != model.POST_HEADER_CHANGE && post.Type != model.POST_PURPOSE_CHANGE)
keywords := a.GetMentionKeywordsInChannel(profileMap, post.Type != model.POST_HEADER_CHANGE && post.Type != model.POST_PURPOSE_CHANGE, channelMemberNotifyPropsMap)

m := GetExplicitMentions(post, keywords)

Expand Down Expand Up @@ -555,7 +555,7 @@ func GetMentionsEnabledFields(post *model.Post) model.StringArray {

// Given a map of user IDs to profiles, returns a list of mention
// keywords for all users in the channel.
func (a *App) GetMentionKeywordsInChannel(profiles map[string]*model.User, lookForSpecialMentions bool) map[string][]string {
func (a *App) GetMentionKeywordsInChannel(profiles map[string]*model.User, lookForSpecialMentions bool, channelMemberNotifyPropsMap map[string]model.StringMap) map[string][]string {
keywords := make(map[string][]string)

for id, profile := range profiles {
Expand All @@ -577,9 +577,16 @@ func (a *App) GetMentionKeywordsInChannel(profiles map[string]*model.User, lookF
keywords[profile.FirstName] = append(keywords[profile.FirstName], profile.Id)
}

ignoreChannelMentions := false
if ignoreChannelMentionsNotifyProp, ok := channelMemberNotifyPropsMap[profile.Id][model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP]; ok {
if ignoreChannelMentionsNotifyProp == model.IGNORE_CHANNEL_MENTIONS_ON {
ignoreChannelMentions = true
}
}

// Add @channel and @all to keywords if user has them turned on
if lookForSpecialMentions {
if int64(len(profiles)) <= *a.Config().TeamSettings.MaxNotificationsPerChannel && profile.NotifyProps["channel"] == "true" {
if int64(len(profiles)) <= *a.Config().TeamSettings.MaxNotificationsPerChannel && profile.NotifyProps["channel"] == "true" && !ignoreChannelMentions {
keywords["@channel"] = append(keywords["@channel"], profile.Id)
keywords["@all"] = append(keywords["@all"], profile.Id)

Expand Down
112 changes: 105 additions & 7 deletions app/notification_test.go
Expand Up @@ -625,8 +625,14 @@ func TestGetMentionKeywords(t *testing.T) {
},
}

channelMemberNotifyPropsMap1Off := map[string]model.StringMap{
user1.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
}

profiles := map[string]*model.User{user1.Id: user1}
mentions := th.App.GetMentionKeywordsInChannel(profiles, true)
mentions := th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap1Off)
if len(mentions) != 3 {
t.Fatal("should've returned three mention keywords")
} else if ids, ok := mentions["user"]; !ok || ids[0] != user1.Id {
Expand All @@ -647,8 +653,14 @@ func TestGetMentionKeywords(t *testing.T) {
},
}

channelMemberNotifyPropsMap2Off := map[string]model.StringMap{
user2.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
}

profiles = map[string]*model.User{user2.Id: user2}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap2Off)
if len(mentions) != 2 {
t.Fatal("should've returned two mention keyword")
} else if ids, ok := mentions["First"]; !ok || ids[0] != user2.Id {
Expand All @@ -665,8 +677,42 @@ func TestGetMentionKeywords(t *testing.T) {
},
}

// Channel-wide mentions are not ignored on channel level
channelMemberNotifyPropsMap3Off := map[string]model.StringMap{
user3.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
}
profiles = map[string]*model.User{user3.Id: user3}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3Off)
if len(mentions) != 3 {
t.Fatal("should've returned three mention keywords")
} else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id {
t.Fatal("should've returned mention key of @channel")
} else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id {
t.Fatal("should've returned mention key of @all")
}

// Channel member notify props is set to default
channelMemberNotifyPropsMapDefault := map[string]model.StringMap{
user3.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_DEFAULT,
},
}
profiles = map[string]*model.User{user3.Id: user3}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapDefault)
if len(mentions) != 3 {
t.Fatal("should've returned three mention keywords")
} else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id {
t.Fatal("should've returned mention key of @channel")
} else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id {
t.Fatal("should've returned mention key of @all")
}

// Channel member notify props is empty
channelMemberNotifyPropsMapEmpty := map[string]model.StringMap{}
profiles = map[string]*model.User{user3.Id: user3}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapEmpty)
if len(mentions) != 3 {
t.Fatal("should've returned three mention keywords")
} else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id {
Expand All @@ -675,6 +721,17 @@ func TestGetMentionKeywords(t *testing.T) {
t.Fatal("should've returned mention key of @all")
}

// Channel-wide mentions are ignored channel level
channelMemberNotifyPropsMap3On := map[string]model.StringMap{
user3.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON,
},
}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3On)
if len(mentions) == 0 {
t.Fatal("should've not returned any keywords")
}

// user with all types of mentions enabled
user4 := &model.User{
Id: model.NewId(),
Expand All @@ -687,8 +744,15 @@ func TestGetMentionKeywords(t *testing.T) {
},
}

// Channel-wide mentions are not ignored on channel level
channelMemberNotifyPropsMap4Off := map[string]model.StringMap{
user4.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
}

profiles = map[string]*model.User{user4.Id: user4}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off)
if len(mentions) != 6 {
t.Fatal("should've returned six mention keywords")
} else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id {
Expand All @@ -705,6 +769,25 @@ func TestGetMentionKeywords(t *testing.T) {
t.Fatal("should've returned mention key of @all")
}

// Channel-wide mentions are ignored on channel level
channelMemberNotifyPropsMap4On := map[string]model.StringMap{
user4.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON,
},
}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4On)
if len(mentions) != 4 {
t.Fatal("should've returned four mention keywords")
} else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id {
t.Fatal("should've returned mention key of user")
} else if ids, ok := mentions["@user"]; !ok || ids[0] != user4.Id {
t.Fatal("should've returned mention key of @user")
} else if ids, ok := mentions["mention"]; !ok || ids[0] != user4.Id {
t.Fatal("should've returned mention key of mention")
} else if ids, ok := mentions["First"]; !ok || ids[0] != user4.Id {
t.Fatal("should've returned mention key of First")
}

dup_count := func(list []string) map[string]int {

duplicate_frequency := make(map[string]int)
Expand All @@ -731,7 +814,22 @@ func TestGetMentionKeywords(t *testing.T) {
user3.Id: user3,
user4.Id: user4,
}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
// Channel-wide mentions are not ignored on channel level for all users
channelMemberNotifyPropsMap5Off := map[string]model.StringMap{
user1.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
user2.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
user3.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
user4.Id: {
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
},
}
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap5Off)
if len(mentions) != 6 {
t.Fatal("should've returned six mention keywords")
} else if ids, ok := mentions["user"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) {
Expand All @@ -750,7 +848,7 @@ func TestGetMentionKeywords(t *testing.T) {

// multiple users and more than MaxNotificationsPerChannel
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxNotificationsPerChannel = 3 })
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off)
if len(mentions) != 4 {
t.Fatal("should've returned four mention keywords")
} else if _, ok := mentions["@channel"]; ok {
Expand All @@ -765,7 +863,7 @@ func TestGetMentionKeywords(t *testing.T) {
profiles = map[string]*model.User{
user1.Id: user1,
}
mentions = th.App.GetMentionKeywordsInChannel(profiles, false)
mentions = th.App.GetMentionKeywordsInChannel(profiles, false, channelMemberNotifyPropsMap4Off)
if len(mentions) != 3 {
t.Fatal("should've returned three mention keywords")
} else if ids, ok := mentions["user"]; !ok || len(ids) != 1 || ids[0] != user1.Id {
Expand Down
4 changes: 4 additions & 0 deletions i18n/en.json
Expand Up @@ -3750,6 +3750,10 @@
"id": "model.channel_member.is_valid.email_value.app_error",
"translation": "Invalid email notification value"
},
{
"id": "model.channel_member.is_valid.ignore_channel_mentions_value.app_error",
"translation": "Invalid ignore channel mentions status"
},
{
"id": "model.channel_member.is_valid.notify_level.app_error",
"translation": "Invalid notify level"
Expand Down
35 changes: 25 additions & 10 deletions model/channel_member.go
Expand Up @@ -11,12 +11,16 @@ import (
)

const (
CHANNEL_NOTIFY_DEFAULT = "default"
CHANNEL_NOTIFY_ALL = "all"
CHANNEL_NOTIFY_MENTION = "mention"
CHANNEL_NOTIFY_NONE = "none"
CHANNEL_MARK_UNREAD_ALL = "all"
CHANNEL_MARK_UNREAD_MENTION = "mention"
CHANNEL_NOTIFY_DEFAULT = "default"
CHANNEL_NOTIFY_ALL = "all"
CHANNEL_NOTIFY_MENTION = "mention"
CHANNEL_NOTIFY_NONE = "none"
CHANNEL_MARK_UNREAD_ALL = "all"
CHANNEL_MARK_UNREAD_MENTION = "mention"
IGNORE_CHANNEL_MENTIONS_DEFAULT = "default"
IGNORE_CHANNEL_MENTIONS_OFF = "off"
IGNORE_CHANNEL_MENTIONS_ON = "on"
IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP = "ignore_channel_mentions"
)

type ChannelUnread struct {
Expand Down Expand Up @@ -116,6 +120,12 @@ func (o *ChannelMember) IsValid() *AppError {
}
}

if ignoreChannelMentions, ok := o.NotifyProps[IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP]; ok {
if len(ignoreChannelMentions) > 40 || !IsIgnoreChannelMentionsValid(ignoreChannelMentions) {
return NewAppError("ChannelMember.IsValid", "model.channel_member.is_valid.ignore_channel_mentions_value.app_error", nil, "ignore_channel_mentions="+ignoreChannelMentions, http.StatusBadRequest)
}
}

return nil
}

Expand Down Expand Up @@ -146,11 +156,16 @@ func IsSendEmailValid(sendEmail string) bool {
return sendEmail == CHANNEL_NOTIFY_DEFAULT || sendEmail == "true" || sendEmail == "false"
}

func IsIgnoreChannelMentionsValid(ignoreChannelMentions string) bool {
return ignoreChannelMentions == IGNORE_CHANNEL_MENTIONS_ON || ignoreChannelMentions == IGNORE_CHANNEL_MENTIONS_OFF || ignoreChannelMentions == IGNORE_CHANNEL_MENTIONS_DEFAULT
}

func GetDefaultChannelNotifyProps() StringMap {
return StringMap{
DESKTOP_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
MARK_UNREAD_NOTIFY_PROP: CHANNEL_MARK_UNREAD_ALL,
PUSH_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
EMAIL_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
DESKTOP_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
MARK_UNREAD_NOTIFY_PROP: CHANNEL_MARK_UNREAD_ALL,
PUSH_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
EMAIL_NOTIFY_PROP: CHANNEL_NOTIFY_DEFAULT,
IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP: IGNORE_CHANNEL_MENTIONS_DEFAULT,
}
}

0 comments on commit 4aca95f

Please sign in to comment.