mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
Send ephemeral message when a mentioned user is not a member of the team (#26153)
Co-authored-by: Caleb Roseland <caleb@calebroseland.com> Co-authored-by: Carrie Warner (Mattermost) <74422101+cwarnermm@users.noreply.github.com> Co-authored-by: Sazzad Hossain <sazzad.hossain@marginedge.com> Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
parent
c5cf4101da
commit
5f6ef75fe7
@ -1119,17 +1119,21 @@ func (a *App) sendNoUsersNotifiedByGroupInChannel(c request.CTX, sender *model.U
|
|||||||
// sendOutOfChannelMentions sends an ephemeral post to the sender of a post if any of the given potential mentions
|
// sendOutOfChannelMentions sends an ephemeral post to the sender of a post if any of the given potential mentions
|
||||||
// are outside of the post's channel. Returns whether or not an ephemeral post was sent.
|
// are outside of the post's channel. Returns whether or not an ephemeral post was sent.
|
||||||
func (a *App) sendOutOfChannelMentions(c request.CTX, sender *model.User, post *model.Post, channel *model.Channel, potentialMentions []string) (bool, error) {
|
func (a *App) sendOutOfChannelMentions(c request.CTX, sender *model.User, post *model.Post, channel *model.Channel, potentialMentions []string) (bool, error) {
|
||||||
outOfChannelUsers, outOfGroupsUsers, err := a.filterOutOfChannelMentions(c, sender, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupsUsers, err := a.filterOutOfChannelMentions(c, sender, post, channel, potentialMentions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(outOfChannelUsers) == 0 && len(outOfGroupsUsers) == 0 {
|
if len(outOfTeamUsers) == 0 && len(outOfChannelUsers) == 0 && len(outOfGroupsUsers) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
a.SendEphemeralPost(c, post.UserId, makeOutOfChannelMentionPost(sender, post, outOfChannelUsers, outOfGroupsUsers))
|
if len(outOfChannelUsers) != 0 || len(outOfGroupsUsers) != 0 {
|
||||||
|
a.SendEphemeralPost(c, post.UserId, makeOutOfChannelMentionPost(sender, post, outOfChannelUsers, outOfGroupsUsers))
|
||||||
|
}
|
||||||
|
if len(outOfTeamUsers) != 0 {
|
||||||
|
a.SendEphemeralPost(c, post.UserId, makeOutOfTeamMentionPost(sender, post, outOfTeamUsers))
|
||||||
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1147,52 +1151,65 @@ func (a *App) FilterUsersByVisible(c request.CTX, viewer *model.User, otherUsers
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) filterOutOfChannelMentions(c request.CTX, sender *model.User, post *model.Post, channel *model.Channel, potentialMentions []string) ([]*model.User, []*model.User, error) {
|
func (a *App) filterOutOfChannelMentions(c request.CTX, sender *model.User, post *model.Post, channel *model.Channel, potentialMentions []string) ([]*model.User, []*model.User, []*model.User, error) {
|
||||||
if post.IsSystemMessage() {
|
if post.IsSystemMessage() {
|
||||||
return nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.TeamId == "" || channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
|
if channel.TeamId == "" || channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
|
||||||
return nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(potentialMentions) == 0 {
|
if len(potentialMentions) == 0 {
|
||||||
return nil, nil, nil
|
return nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := a.Srv().Store().User().GetProfilesByUsernames(potentialMentions, &model.ViewUsersRestrictions{Teams: []string{channel.TeamId}})
|
mentionedUsersInTheTeam, err := a.Srv().Store().User().GetProfilesByUsernames(potentialMentions, &model.ViewUsersRestrictions{Teams: []string{channel.TeamId}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out inactive users and bots
|
// Filter out inactive users and bots
|
||||||
allUsers := model.UserSlice(users).FilterByActive(true)
|
teamUsers := model.UserSlice(mentionedUsersInTheTeam).FilterByActive(true)
|
||||||
allUsers = allUsers.FilterWithoutBots()
|
teamUsers = teamUsers.FilterWithoutBots()
|
||||||
allUsers, appErr := a.FilterUsersByVisible(c, sender, allUsers)
|
teamUsers, appErr := a.FilterUsersByVisible(c, sender, teamUsers)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return nil, nil, appErr
|
return nil, nil, nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allUsers) == 0 {
|
allMentionedUsers, err := a.Srv().Store().User().GetProfilesByUsernames(potentialMentions, nil)
|
||||||
return nil, nil, nil
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Differentiate between users who can and can't be added to the channel
|
outOfTeamUsers := model.UserSlice(allMentionedUsers).FilterWithoutID(teamUsers.IDs())
|
||||||
|
outOfTeamUsers = outOfTeamUsers.FilterByActive(true)
|
||||||
|
outOfTeamUsers = outOfTeamUsers.FilterWithoutBots()
|
||||||
|
outOfTeamUsers, appErr = a.FilterUsersByVisible(c, sender, outOfTeamUsers)
|
||||||
|
if appErr != nil {
|
||||||
|
return nil, nil, nil, appErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(teamUsers) == 0 {
|
||||||
|
return outOfTeamUsers, nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Differentiate between mentionedUsersInTheTeam who can and can't be added to the channel
|
||||||
var outOfChannelUsers model.UserSlice
|
var outOfChannelUsers model.UserSlice
|
||||||
var outOfGroupsUsers model.UserSlice
|
var outOfGroupsUsers model.UserSlice
|
||||||
if channel.IsGroupConstrained() {
|
if channel.IsGroupConstrained() {
|
||||||
nonMemberIDs, err := a.FilterNonGroupChannelMembers(allUsers.IDs(), channel)
|
nonMemberIDs, err := a.FilterNonGroupChannelMembers(teamUsers.IDs(), channel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
outOfChannelUsers = allUsers.FilterWithoutID(nonMemberIDs)
|
outOfChannelUsers = teamUsers.FilterWithoutID(nonMemberIDs)
|
||||||
outOfGroupsUsers = allUsers.FilterByID(nonMemberIDs)
|
outOfGroupsUsers = teamUsers.FilterByID(nonMemberIDs)
|
||||||
} else {
|
} else {
|
||||||
outOfChannelUsers = allUsers
|
outOfChannelUsers = teamUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
return outOfChannelUsers, outOfGroupsUsers, nil
|
return outOfTeamUsers, outOfChannelUsers, outOfGroupsUsers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeOutOfChannelMentionPost(sender *model.User, post *model.Post, outOfChannelUsers, outOfGroupsUsers []*model.User) *model.Post {
|
func makeOutOfChannelMentionPost(sender *model.User, post *model.Post, outOfChannelUsers, outOfGroupsUsers []*model.User) *model.Post {
|
||||||
@ -1268,6 +1285,37 @@ func makeOutOfChannelMentionPost(sender *model.User, post *model.Post, outOfChan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeOutOfTeamMentionPost(sender *model.User, post *model.Post, outOfTeamUsers []*model.User) *model.Post {
|
||||||
|
otUsers := model.UserSlice(outOfTeamUsers)
|
||||||
|
otUsernames := otUsers.Usernames()
|
||||||
|
|
||||||
|
T := i18n.GetUserTranslations(sender.Locale)
|
||||||
|
|
||||||
|
ephemeralPostId := model.NewId()
|
||||||
|
var message string
|
||||||
|
|
||||||
|
if len(outOfTeamUsers) == 1 {
|
||||||
|
message += T("api.post.check_for_out_of_team_mentions.message.one", map[string]any{
|
||||||
|
"Username": otUsernames[0],
|
||||||
|
})
|
||||||
|
} else if len(outOfTeamUsers) > 1 {
|
||||||
|
preliminary, final := splitAtFinal(otUsernames)
|
||||||
|
|
||||||
|
message += T("api.post.check_for_out_of_team_mentions.message.multiple", map[string]any{
|
||||||
|
"Usernames": strings.Join(preliminary, ", @"),
|
||||||
|
"LastUsername": final,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &model.Post{
|
||||||
|
Id: ephemeralPostId,
|
||||||
|
RootId: post.RootId,
|
||||||
|
ChannelId: post.ChannelId,
|
||||||
|
Message: message,
|
||||||
|
CreateAt: post.CreateAt + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func splitAtFinal(items []string) (preliminary []string, final string) {
|
func splitAtFinal(items []string) (preliminary []string, final string) {
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
return
|
return
|
||||||
|
@ -644,6 +644,17 @@ func TestSendOutOfChannelMentions(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.False(t, sent)
|
assert.False(t, sent)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should send ephemeral post when there is an out of team mention", func(t *testing.T) {
|
||||||
|
outOfTeamUser := th.CreateUser()
|
||||||
|
post := &model.Post{}
|
||||||
|
potentialMentions := []string{outOfTeamUser.Username}
|
||||||
|
|
||||||
|
sent, err := th.App.sendOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, sent)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilterOutOfChannelMentions(t *testing.T) {
|
func TestFilterOutOfChannelMentions(t *testing.T) {
|
||||||
@ -670,22 +681,40 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
post := &model.Post{}
|
post := &model.Post{}
|
||||||
potentialMentions := []string{user2.Username, user3.Username}
|
potentialMentions := []string{user2.Username, user3.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Len(t, outOfChannelUsers, 2)
|
assert.Len(t, outOfChannelUsers, 2)
|
||||||
assert.True(t, (outOfChannelUsers[0].Id == user2.Id || outOfChannelUsers[1].Id == user2.Id))
|
assert.True(t, (outOfChannelUsers[0].Id == user2.Id || outOfChannelUsers[1].Id == user2.Id))
|
||||||
assert.True(t, (outOfChannelUsers[0].Id == user3.Id || outOfChannelUsers[1].Id == user3.Id))
|
assert.True(t, (outOfChannelUsers[0].Id == user3.Id || outOfChannelUsers[1].Id == user3.Id))
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("should return users not in the team", func(t *testing.T) {
|
||||||
|
notThisTeamUser1 := th.CreateUser()
|
||||||
|
notThisTeamUser2 := th.CreateUser()
|
||||||
|
post := &model.Post{}
|
||||||
|
potentialMentions := []string{notThisTeamUser1.Username, notThisTeamUser2.Username}
|
||||||
|
|
||||||
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 2)
|
||||||
|
assert.Nil(t, outOfChannelUsers)
|
||||||
|
assert.True(t, (outOfTeamUsers[0].Id == notThisTeamUser1.Id || outOfTeamUsers[1].Id == notThisTeamUser1.Id))
|
||||||
|
assert.True(t, (outOfTeamUsers[0].Id == notThisTeamUser2.Id || outOfTeamUsers[1].Id == notThisTeamUser2.Id))
|
||||||
|
assert.Nil(t, outOfGroupUsers)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("should return only visible users not in the channel (for guests)", func(t *testing.T) {
|
t.Run("should return only visible users not in the channel (for guests)", func(t *testing.T) {
|
||||||
post := &model.Post{}
|
post := &model.Post{}
|
||||||
potentialMentions := []string{user2.Username, user3.Username, user4.Username}
|
potentialMentions := []string{user2.Username, user3.Username, user4.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, guest, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, guest, post, channel, potentialMentions)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
require.Len(t, outOfChannelUsers, 1)
|
require.Len(t, outOfChannelUsers, 1)
|
||||||
assert.Equal(t, user4.Id, outOfChannelUsers[0].Id)
|
assert.Equal(t, user4.Id, outOfChannelUsers[0].Id)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
@ -697,9 +726,10 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
potentialMentions := []string{user2.Username, user3.Username}
|
potentialMentions := []string{user2.Username, user3.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Nil(t, outOfChannelUsers)
|
assert.Nil(t, outOfChannelUsers)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
@ -711,9 +741,10 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
potentialMentions := []string{user2.Username, user3.Username}
|
potentialMentions := []string{user2.Username, user3.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, directChannel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, directChannel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Nil(t, outOfChannelUsers)
|
assert.Nil(t, outOfChannelUsers)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
@ -725,9 +756,10 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
potentialMentions := []string{user2.Username, user3.Username}
|
potentialMentions := []string{user2.Username, user3.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, groupChannel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, groupChannel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Nil(t, outOfChannelUsers)
|
assert.Nil(t, outOfChannelUsers)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
@ -740,23 +772,24 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
post := &model.Post{}
|
post := &model.Post{}
|
||||||
potentialMentions := []string{inactiveUser.Username}
|
potentialMentions := []string{inactiveUser.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Nil(t, outOfChannelUsers)
|
assert.Nil(t, outOfChannelUsers)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not return bot users", func(t *testing.T) {
|
t.Run("should not return bot users", func(t *testing.T) {
|
||||||
botUser := th.CreateUser()
|
botUser := th.CreateBot()
|
||||||
botUser.IsBot = true
|
|
||||||
|
|
||||||
post := &model.Post{}
|
post := &model.Post{}
|
||||||
potentialMentions := []string{botUser.Username}
|
potentialMentions := []string{botUser.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Nil(t, outOfChannelUsers)
|
assert.Nil(t, outOfChannelUsers)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
@ -765,9 +798,10 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
post := &model.Post{}
|
post := &model.Post{}
|
||||||
potentialMentions := []string{"foo", "bar"}
|
potentialMentions := []string{"foo", "bar"}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, channel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Nil(t, outOfChannelUsers)
|
assert.Nil(t, outOfChannelUsers)
|
||||||
assert.Nil(t, outOfGroupUsers)
|
assert.Nil(t, outOfGroupUsers)
|
||||||
})
|
})
|
||||||
@ -799,9 +833,10 @@ func TestFilterOutOfChannelMentions(t *testing.T) {
|
|||||||
post := &model.Post{}
|
post := &model.Post{}
|
||||||
potentialMentions := []string{nonChannelMember.Username, nonGroupMember.Username}
|
potentialMentions := []string{nonChannelMember.Username, nonGroupMember.Username}
|
||||||
|
|
||||||
outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, constrainedChannel, potentialMentions)
|
outOfTeamUsers, outOfChannelUsers, outOfGroupUsers, err := th.App.filterOutOfChannelMentions(th.Context, user1, post, constrainedChannel, potentialMentions)
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, outOfTeamUsers, 0)
|
||||||
assert.Len(t, outOfChannelUsers, 1)
|
assert.Len(t, outOfChannelUsers, 1)
|
||||||
assert.Equal(t, nonChannelMember.Id, outOfChannelUsers[0].Id)
|
assert.Equal(t, nonChannelMember.Id, outOfChannelUsers[0].Id)
|
||||||
assert.Len(t, outOfGroupUsers, 1)
|
assert.Len(t, outOfGroupUsers, 1)
|
||||||
|
@ -2500,6 +2500,14 @@
|
|||||||
"id": "api.post.check_for_out_of_channel_mentions.message.one",
|
"id": "api.post.check_for_out_of_channel_mentions.message.one",
|
||||||
"translation": "@{{.Username}} did not get notified by this mention because they are not in the channel."
|
"translation": "@{{.Username}} did not get notified by this mention because they are not in the channel."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "api.post.check_for_out_of_team_mentions.message.multiple",
|
||||||
|
"translation": "@{{.Usernames}} and @{{.LastUsername}} didn't get notified by this mention because they aren't members of this team."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "api.post.check_for_out_of_team_mentions.message.one",
|
||||||
|
"translation": "@{{.Username}} didn't get notified by this mention because they aren't a member of this team."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "api.post.create_post.can_not_post_to_deleted.error",
|
"id": "api.post.create_post.can_not_post_to_deleted.error",
|
||||||
"translation": "Can not post to deleted channel."
|
"translation": "Can not post to deleted channel."
|
||||||
|
Loading…
Reference in New Issue
Block a user