mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
#4755 Combining consecutive user join/leave system messages to single message and few other changes. (#5945)
fix 7 and 8
remove @ at "{username} joined the channel"
refactor and update test
This commit is contained in:
committed by
Saturnino Abril
parent
59992ae4a4
commit
8a91235fb3
@@ -394,7 +394,7 @@ func TestUpdatePost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a", Type: model.POST_JOIN_LEAVE}
|
||||
post3 := &model.Post{ChannelId: channel1.Id, Message: "zz" + model.NewId() + "a", Type: model.POST_HEADER_CHANGE}
|
||||
rpost3, err := Client.CreatePost(post3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -359,7 +359,7 @@ func TestUpdatePost(t *testing.T) {
|
||||
t.Fatal("failed to updates")
|
||||
}
|
||||
|
||||
post2 := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", Type: model.POST_JOIN_LEAVE}
|
||||
post2 := &model.Post{ChannelId: channel.Id, Message: "zz" + model.NewId() + "a", Type: model.POST_HEADER_CHANGE}
|
||||
rpost2, resp := Client.CreatePost(post2)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
|
||||
161
app/channel.go
161
app/channel.go
@@ -897,19 +897,75 @@ func JoinChannel(channel *model.Channel, userId string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func postJoinChannelMessage(user *model.User, channel *model.Channel) *model.AppError {
|
||||
func createUserActivitySystemMessage(channel *model.Channel, user *model.User, otherUsername, messageType, message string) *model.AppError {
|
||||
props := model.StringInterface{"username": user.Username}
|
||||
if messageType == model.POST_ADD_TO_CHANNEL {
|
||||
props["addedUsername"] = otherUsername
|
||||
}
|
||||
|
||||
if messageType == model.POST_REMOVE_FROM_CHANNEL {
|
||||
props["removedUsername"] = user.Username
|
||||
delete(props, "username")
|
||||
}
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf(utils.T("api.channel.join_channel.post_and_forget"), user.Username),
|
||||
Type: model.POST_JOIN_CHANNEL,
|
||||
Message: message,
|
||||
Type: messageType,
|
||||
UserId: user.Id,
|
||||
Props: model.StringInterface{
|
||||
"username": user.Username,
|
||||
},
|
||||
Props: props,
|
||||
}
|
||||
|
||||
if _, err := CreatePost(post, channel.TeamId, false); err != nil {
|
||||
return model.NewLocAppError("postJoinChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, err.Error())
|
||||
return model.NewLocAppError("createUserActivitySystemMessage", "api.channel.create_user_activity_post.error", nil, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateUserActivitySystemMessage(post *model.Post, username, otherUsername, messageType, messageToAdd string) *model.AppError {
|
||||
post.Message += " " + messageToAdd
|
||||
props := model.StringMap{"type": messageType, "username": username}
|
||||
if messageType == model.POST_ADD_TO_CHANNEL {
|
||||
props["addedUsername"] = otherUsername
|
||||
}
|
||||
|
||||
if messageType == model.POST_REMOVE_FROM_CHANNEL {
|
||||
props["removedUsername"] = username
|
||||
delete(props, "username")
|
||||
}
|
||||
|
||||
if val, ok := post.Props["messages"]; ok {
|
||||
post.Props["messages"] = append(val.([]interface{}), props)
|
||||
} else {
|
||||
oldProps := make(model.StringInterface)
|
||||
for key, value := range post.Props {
|
||||
oldProps[key] = value
|
||||
}
|
||||
oldProps["type"] = post.Type
|
||||
post.Props["messages"] = []interface{}{oldProps, props}
|
||||
}
|
||||
|
||||
if _, err := UpdatePost(post, false); err != nil {
|
||||
return model.NewLocAppError("updateUserActivitySystemMessage", "api.channel.update_user_activity_post.error", nil, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func postJoinChannelMessage(user *model.User, channel *model.Channel) *model.AppError {
|
||||
message := fmt.Sprintf(utils.T("api.channel.join_channel.post_and_forget"), user.Username)
|
||||
|
||||
if post, err := GetLastPostForChannel(channel.Id); err != nil {
|
||||
return err
|
||||
} else if post.IsUserActivitySystemMessage() {
|
||||
if err := updateUserActivitySystemMessage(post, user.Username, "", model.POST_JOIN_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := createUserActivitySystemMessage(channel, user, "", model.POST_JOIN_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -954,55 +1010,64 @@ func LeaveChannel(channelId string, userId string) *model.AppError {
|
||||
}
|
||||
|
||||
func postLeaveChannelMessage(user *model.User, channel *model.Channel) *model.AppError {
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf(utils.T("api.channel.leave.left"), user.Username),
|
||||
Type: model.POST_LEAVE_CHANNEL,
|
||||
UserId: user.Id,
|
||||
Props: model.StringInterface{
|
||||
"username": user.Username,
|
||||
},
|
||||
}
|
||||
message := fmt.Sprintf(utils.T("api.channel.leave.left"), user.Username)
|
||||
|
||||
if _, err := CreatePost(post, channel.TeamId, false); err != nil {
|
||||
return model.NewLocAppError("postLeaveChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, err.Error())
|
||||
if post, err := GetLastPostForChannel(channel.Id); err != nil {
|
||||
return err
|
||||
} else if post.IsUserActivitySystemMessage() {
|
||||
if err := updateUserActivitySystemMessage(post, user.Username, "", model.POST_LEAVE_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := createUserActivitySystemMessage(channel, user, "", model.POST_LEAVE_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func PostAddToChannelMessage(user *model.User, addedUser *model.User, channel *model.Channel) *model.AppError {
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf(utils.T("api.channel.add_member.added"), addedUser.Username, user.Username),
|
||||
Type: model.POST_ADD_TO_CHANNEL,
|
||||
UserId: user.Id,
|
||||
Props: model.StringInterface{
|
||||
"username": user.Username,
|
||||
"addedUsername": addedUser.Username,
|
||||
},
|
||||
}
|
||||
message := fmt.Sprintf(utils.T("api.channel.add_member.added"), user.Username, addedUser.Username)
|
||||
|
||||
if _, err := CreatePost(post, channel.TeamId, false); err != nil {
|
||||
return model.NewLocAppError("postAddToChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, err.Error())
|
||||
if post, err := GetLastPostForChannel(channel.Id); err != nil {
|
||||
return err
|
||||
} else if post.IsUserActivitySystemMessage() {
|
||||
if err := updateUserActivitySystemMessage(post, user.Username, addedUser.Username, model.POST_ADD_TO_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
SendNotificationsForSystemMessageAddRemove(user, addedUser, channel, post)
|
||||
} else {
|
||||
if err := createUserActivitySystemMessage(channel, user, addedUser.Username, model.POST_ADD_TO_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func PostRemoveFromChannelMessage(removerUserId string, removedUser *model.User, channel *model.Channel) *model.AppError {
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf(utils.T("api.channel.remove_member.removed"), removedUser.Username),
|
||||
Type: model.POST_REMOVE_FROM_CHANNEL,
|
||||
UserId: removerUserId,
|
||||
Props: model.StringInterface{
|
||||
"removedUsername": removedUser.Username,
|
||||
},
|
||||
message := fmt.Sprintf(utils.T("api.channel.remove_member.removed"), removedUser.Username)
|
||||
|
||||
removerUser, err := GetUser(removerUserId)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := CreatePost(post, channel.TeamId, false); err != nil {
|
||||
return model.NewLocAppError("postRemoveFromChannelMessage", "api.channel.post_user_add_remove_message_and_forget.error", nil, err.Error())
|
||||
if post, err := GetLastPostForChannel(channel.Id); err != nil {
|
||||
return err
|
||||
} else if post.IsUserActivitySystemMessage() {
|
||||
if err := updateUserActivitySystemMessage(post, removedUser.Username, removerUser.Username, model.POST_REMOVE_FROM_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
SendNotificationsForSystemMessageAddRemove(removerUser, removedUser, channel, post)
|
||||
} else {
|
||||
if err := createUserActivitySystemMessage(channel, removedUser, removerUser.Username, model.POST_ADD_TO_CHANNEL, message); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1046,20 +1111,28 @@ func RemoveUserFromChannel(userIdToRemove string, removerUserId string, channel
|
||||
return err
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if user, err = GetUser(userIdToRemove); err != nil {
|
||||
var userToRemove *model.User
|
||||
if userToRemove, err = GetUser(userIdToRemove); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if userIdToRemove == removerUserId {
|
||||
postLeaveChannelMessage(user, channel)
|
||||
postLeaveChannelMessage(userToRemove, channel)
|
||||
} else {
|
||||
go PostRemoveFromChannelMessage(removerUserId, user, channel)
|
||||
go PostRemoveFromChannelMessage(removerUserId, userToRemove, channel)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetLastPostForChannel(channelId string) (*model.Post, *model.AppError) {
|
||||
result := <-Srv.Store.Post().GetLastPostForChannel(channelId)
|
||||
if result.Err != nil {
|
||||
return nil, result.Err
|
||||
}
|
||||
return result.Data.(*model.Post), nil
|
||||
}
|
||||
|
||||
func GetNumberOfChannelsOnTeam(teamId string) (int, *model.AppError) {
|
||||
// Get total number of channels on current team
|
||||
if result := <-Srv.Store.Channel().GetTeamChannels(teamId); result.Err != nil {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
@@ -64,3 +69,193 @@ func TestPermanentDeleteChannel(t *testing.T) {
|
||||
t.Error("outgoing webhook wasn't deleted")
|
||||
}
|
||||
}
|
||||
|
||||
func PostUserActivitySystemMessage(t *testing.T, testFunc func(user *model.User, channel *model.Channel) *model.AppError, postType string, messageKey string) {
|
||||
th := Setup().InitBasic()
|
||||
|
||||
// Test when last post was a leave/join post
|
||||
user := &model.User{Id: model.NewId(), Email: "test@example.com", Username: "test"}
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: th.BasicChannel.Id, UserId: user.Id, Type: postType, Message: "message",
|
||||
Props: model.StringInterface{"username": user.Username},
|
||||
}
|
||||
post = (<-Srv.Store.Post().Save(post)).Data.(*model.Post)
|
||||
|
||||
testFunc(user, th.BasicChannel)
|
||||
post = (<-Srv.Store.Post().GetSingle(post.Id)).Data.(*model.Post)
|
||||
if post.Message != "message "+fmt.Sprintf(utils.T(messageKey), user.Username) {
|
||||
t.Fatal("Leave/join a channel message wasn't appended to last leave/join post")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(
|
||||
post.Props["messages"].([]interface{}),
|
||||
[]interface{}{
|
||||
map[string]interface{}{"type": postType, "username": user.Username},
|
||||
map[string]interface{}{"type": postType, "username": user.Username},
|
||||
},
|
||||
) {
|
||||
t.Fatal("Invalid leave/join a channel props")
|
||||
}
|
||||
|
||||
// Test when last post was not a leave/join post
|
||||
post.Id = ""
|
||||
post.Message = "message1"
|
||||
post.Type = model.POST_DEFAULT
|
||||
post.Props = nil
|
||||
post = (<-Srv.Store.Post().Save(post)).Data.(*model.Post)
|
||||
|
||||
testFunc(user, th.BasicChannel)
|
||||
post = (<-Srv.Store.Post().GetSingle(post.Id)).Data.(*model.Post)
|
||||
if post.Message == "message1 "+fmt.Sprintf(utils.T(messageKey), user.Username) {
|
||||
t.Fatal("Leave/join a channel message was appended to last non leave/join post")
|
||||
}
|
||||
|
||||
if _, ok := post.Props["messages"]; ok {
|
||||
t.Fatal("Invalid leave/join a channel props. \"message\" shouldn't be present")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostJoinChannelMessage(t *testing.T) {
|
||||
PostUserActivitySystemMessage(t, postJoinChannelMessage, model.POST_JOIN_CHANNEL, "api.channel.join_channel.post_and_forget")
|
||||
}
|
||||
|
||||
func TestPostLeaveChannelMessage(t *testing.T) {
|
||||
PostUserActivitySystemMessage(t, postLeaveChannelMessage, model.POST_LEAVE_CHANNEL, "api.channel.leave.left")
|
||||
}
|
||||
|
||||
func TestPostAddToChannelMessage(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
|
||||
// Test when last post was a user activity system message post
|
||||
user := &model.User{Id: model.NewId(), Email: "test@example.com", Username: "test"}
|
||||
addedUser := &model.User{Id: model.NewId(), Email: "test1@example.com", Username: "test1"}
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: th.BasicChannel.Id, UserId: user.Id, Type: model.POST_ADD_TO_CHANNEL, Message: "message",
|
||||
Props: model.StringInterface{"username": user.Username, "addedUsername": addedUser.Username},
|
||||
}
|
||||
post = (<-Srv.Store.Post().Save(post)).Data.(*model.Post)
|
||||
|
||||
PostAddToChannelMessage(user, addedUser, th.BasicChannel)
|
||||
post = (<-Srv.Store.Post().GetSingle(post.Id)).Data.(*model.Post)
|
||||
if post.Message != "message "+fmt.Sprintf(utils.T("api.channel.add_member.added"), user.Username, addedUser.Username) {
|
||||
t.Fatal("Add message wasn't appended to last user activity system message post")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(
|
||||
post.Props["messages"].([]interface{}),
|
||||
[]interface{}{
|
||||
map[string]interface{}{"type": model.POST_ADD_TO_CHANNEL, "username": user.Username, "addedUsername": addedUser.Username},
|
||||
map[string]interface{}{"type": model.POST_ADD_TO_CHANNEL, "username": user.Username, "addedUsername": addedUser.Username},
|
||||
},
|
||||
) {
|
||||
t.Fatal("Invalid added to channel props")
|
||||
}
|
||||
|
||||
// Test when last post was not a user activity system message post
|
||||
post.Id = ""
|
||||
post.Message = "message1"
|
||||
post.Type = model.POST_DEFAULT
|
||||
post.Props = nil
|
||||
post = (<-Srv.Store.Post().Save(post)).Data.(*model.Post)
|
||||
|
||||
PostAddToChannelMessage(user, addedUser, th.BasicChannel)
|
||||
post = (<-Srv.Store.Post().GetSingle(post.Id)).Data.(*model.Post)
|
||||
if post.Message == "message1\n"+fmt.Sprintf(utils.T("api.channel.add_member.added"), addedUser.Username, user.Username) {
|
||||
t.Fatal("Added to channel message was appended to last non user activity system message post")
|
||||
}
|
||||
|
||||
if _, ok := post.Props["messages"]; ok {
|
||||
t.Fatal("Invalid added to channel props. \"message\" shouldn't be present")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostRemoveFromChannelMessage(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
user := th.BasicUser
|
||||
|
||||
// // Test when last post was a user activity system message post
|
||||
removedUser := &model.User{Id: model.NewId(), Email: "test1@example.com", Username: "test1"}
|
||||
|
||||
post := &model.Post{
|
||||
ChannelId: th.BasicChannel.Id, UserId: user.Id, Type: model.POST_REMOVE_FROM_CHANNEL, Message: "message",
|
||||
Props: model.StringInterface{"removedUsername": removedUser.Username},
|
||||
}
|
||||
post = (<-Srv.Store.Post().Save(post)).Data.(*model.Post)
|
||||
|
||||
PostRemoveFromChannelMessage(user.Id, removedUser, th.BasicChannel)
|
||||
post = (<-Srv.Store.Post().GetSingle(post.Id)).Data.(*model.Post)
|
||||
if post.Message != "message "+fmt.Sprintf(utils.T("api.channel.remove_member.removed"), removedUser.Username) {
|
||||
t.Fatal("Post removed from a channel message wasn't appended to last user activity system message post")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(
|
||||
post.Props["messages"].([]interface{}),
|
||||
[]interface{}{
|
||||
map[string]interface{}{"type": model.POST_REMOVE_FROM_CHANNEL, "removedUsername": removedUser.Username},
|
||||
map[string]interface{}{"type": model.POST_REMOVE_FROM_CHANNEL, "removedUsername": removedUser.Username},
|
||||
},
|
||||
) {
|
||||
t.Fatal("Invalid removed from a channel props")
|
||||
}
|
||||
|
||||
// Test when last post was not a user activity system message post
|
||||
post.Id = ""
|
||||
post.Message = "message1"
|
||||
post.Type = model.POST_DEFAULT
|
||||
post.Props = nil
|
||||
post = (<-Srv.Store.Post().Save(post)).Data.(*model.Post)
|
||||
|
||||
PostRemoveFromChannelMessage(user.Id, removedUser, th.BasicChannel)
|
||||
post = (<-Srv.Store.Post().GetSingle(post.Id)).Data.(*model.Post)
|
||||
if post.Message == "message1 "+fmt.Sprintf(utils.T("api.channel.remove_member.removed"), removedUser.Username) {
|
||||
t.Fatal("Post removed from a channel message was appended to last non user activity system message post")
|
||||
}
|
||||
|
||||
if _, ok := post.Props["messages"]; ok {
|
||||
t.Fatal("Invalid removed from a channel props. \"message\" shouldn't be present")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLastPostForChannel(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
user := th.BasicUser
|
||||
|
||||
post1 := &model.Post{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
UserId: user.Id,
|
||||
Message: "message",
|
||||
}
|
||||
post1 = (<-Srv.Store.Post().Save(post1)).Data.(*model.Post)
|
||||
|
||||
rpost1, err := GetLastPostForChannel(th.BasicChannel.Id)
|
||||
if err != nil {
|
||||
t.Fatal("Last post should have been returned")
|
||||
}
|
||||
|
||||
if post1.Message != rpost1.Message {
|
||||
t.Fatal("Should match post message")
|
||||
}
|
||||
|
||||
post2 := &model.Post{
|
||||
ChannelId: th.BasicChannel.Id,
|
||||
UserId: user.Id,
|
||||
Message: "message",
|
||||
}
|
||||
post2 = (<-Srv.Store.Post().Save(post2)).Data.(*model.Post)
|
||||
|
||||
rpost2, err := GetLastPostForChannel(th.BasicChannel.Id)
|
||||
if err != nil {
|
||||
t.Fatal("Last post should have been returned")
|
||||
}
|
||||
|
||||
if post2.Message != rpost2.Message {
|
||||
t.Fatal("Should match post message")
|
||||
}
|
||||
|
||||
_, err = GetLastPostForChannel(model.NewId())
|
||||
if err != nil {
|
||||
t.Fatal("Should not return err")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +306,91 @@ func SendNotifications(post *model.Post, team *model.Team, channel *model.Channe
|
||||
return mentionedUsersList, nil
|
||||
}
|
||||
|
||||
func SendNotificationsForSystemMessageAddRemove(user *model.User, otherUser *model.User, channel *model.Channel, post *model.Post) *model.AppError {
|
||||
otherUserChannelMember, _ := GetChannelMember(channel.Id, otherUser.Id)
|
||||
team, err := GetTeam(channel.TeamId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = (<-Srv.Store.Channel().IncrementMentionCount(channel.Id, otherUser.Id)).Err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if utils.Cfg.EmailSettings.SendEmailNotifications {
|
||||
userAllowsEmails := otherUser.NotifyProps[model.EMAIL_NOTIFY_PROP] != "false"
|
||||
if otherUserChannelMember != nil {
|
||||
channelEmail, ok := otherUserChannelMember.NotifyProps[model.EMAIL_NOTIFY_PROP]
|
||||
if ok && channelEmail != model.CHANNEL_NOTIFY_DEFAULT {
|
||||
userAllowsEmails = channelEmail != "false"
|
||||
}
|
||||
}
|
||||
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = GetStatus(otherUser.Id); err != nil {
|
||||
status = &model.Status{
|
||||
UserId: otherUser.Id,
|
||||
Status: model.STATUS_OFFLINE,
|
||||
Manual: false,
|
||||
LastActivityAt: 0,
|
||||
ActiveChannel: "",
|
||||
}
|
||||
}
|
||||
|
||||
senderName := utils.T("system.message.name")
|
||||
if userAllowsEmails && status.Status != model.STATUS_ONLINE && otherUser.DeleteAt == 0 {
|
||||
if err = sendNotificationEmail(post, otherUser, channel, team, senderName, user); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if otherUserChannelMember != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
channelName := channel.DisplayName
|
||||
senderUsername := user.Username
|
||||
|
||||
if sendPushNotifications {
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = GetStatus(otherUser.Id); err != nil {
|
||||
status = &model.Status{UserId: otherUser.Id, Status: model.STATUS_OFFLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
if ShouldSendPushNotification(otherUser, otherUserChannelMember.NotifyProps, true, status, post) {
|
||||
if err = sendPushNotification(post, otherUser, channel, senderUsername, channelName, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POSTED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", post.ToJson())
|
||||
message.Add("channel_type", channel.Type)
|
||||
message.Add("channel_display_name", channelName)
|
||||
message.Add("channel_name", channel.Name)
|
||||
message.Add("sender_name", senderUsername)
|
||||
message.Add("team_id", channel.TeamId)
|
||||
message.Add("mentions", otherUser.Username)
|
||||
|
||||
Publish(message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendNotificationEmail(post *model.Post, user *model.User, channel *model.Channel, team *model.Team, senderName string, sender *model.User) *model.AppError {
|
||||
if channel.IsGroupOrDirect() {
|
||||
if result := <-Srv.Store.Team().GetTeamsByUserId(user.Id); result.Err != nil {
|
||||
|
||||
@@ -250,8 +250,8 @@ func UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if oldPost.IsSystemMessage() {
|
||||
err := model.NewAppError("UpdatePost", "api.post.update_post.system_message.app_error", nil, "id="+post.Id, http.StatusBadRequest)
|
||||
if oldPost.IsSystemMessage() && !oldPost.IsUserActivitySystemMessage() {
|
||||
err := model.NewAppError("updatePost", "api.post.update_post.system_message.app_error", nil, "id="+post.Id, http.StatusBadRequest)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
14
i18n/en.json
14
i18n/en.json
@@ -153,7 +153,7 @@
|
||||
},
|
||||
{
|
||||
"id": "api.channel.add_member.added",
|
||||
"translation": "%v added to the channel by %v"
|
||||
"translation": "%v added %v to the channel."
|
||||
},
|
||||
{
|
||||
"id": "api.channel.add_member.find_channel.app_error",
|
||||
@@ -297,7 +297,7 @@
|
||||
},
|
||||
{
|
||||
"id": "api.channel.join_channel.post_and_forget",
|
||||
"translation": "%v has joined the channel."
|
||||
"translation": "%v joined the channel."
|
||||
},
|
||||
{
|
||||
"id": "api.channel.leave.default.app_error",
|
||||
@@ -313,7 +313,7 @@
|
||||
},
|
||||
{
|
||||
"id": "api.channel.leave.left",
|
||||
"translation": "%v has left the channel."
|
||||
"translation": "%v left the channel."
|
||||
},
|
||||
{
|
||||
"id": "api.channel.post_update_channel_displayname_message_and_forget.create_post.error",
|
||||
@@ -347,6 +347,14 @@
|
||||
"id": "api.channel.post_update_channel_header_message_and_forget.updated_to",
|
||||
"translation": "%s updated the channel header to: %s"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.create_user_activity_post.error",
|
||||
"translation": "Failed to create user activity system message like add, remove, join or leave"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.update_user_activity_post.error",
|
||||
"translation": "Failed to update user activity system message like add, remove, join or leave"
|
||||
},
|
||||
{
|
||||
"id": "api.channel.post_user_add_remove_message_and_forget.error",
|
||||
"translation": "Failed to post join/leave message"
|
||||
|
||||
@@ -205,6 +205,15 @@ func (o *Post) IsSystemMessage() bool {
|
||||
return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
|
||||
}
|
||||
|
||||
func (o *Post) IsUserActivitySystemMessage() bool {
|
||||
return o.Type == POST_JOIN_LEAVE ||
|
||||
o.Type == POST_JOIN_CHANNEL ||
|
||||
o.Type == POST_LEAVE_CHANNEL ||
|
||||
o.Type == POST_ADD_REMOVE ||
|
||||
o.Type == POST_ADD_TO_CHANNEL ||
|
||||
o.Type == POST_REMOVE_FROM_CHANNEL
|
||||
}
|
||||
|
||||
func (p *Post) Patch(patch *PostPatch) {
|
||||
if patch.IsPinned != nil {
|
||||
p.IsPinned = *patch.IsPinned
|
||||
|
||||
@@ -113,3 +113,48 @@ func TestPostIsSystemMessage(t *testing.T) {
|
||||
t.Fatalf("TestPostIsSystemMessage failed, expected post2.IsSystemMessage() to be true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostIsUserActivitySystemMessage(t *testing.T) {
|
||||
post1 := Post{Message: "test_1"}
|
||||
post1.PreSave()
|
||||
|
||||
if post1.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post1.IsSystemMessage() to be false")
|
||||
}
|
||||
|
||||
post2 := Post{Message: "test_2", Type: POST_JOIN_LEAVE}
|
||||
post2.PreSave()
|
||||
if !post2.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post2.IsSystemMessage() to be true")
|
||||
}
|
||||
|
||||
post3 := Post{Message: "test_3", Type: POST_JOIN_CHANNEL}
|
||||
post3.PreSave()
|
||||
if !post3.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post3.IsSystemMessage() to be true")
|
||||
}
|
||||
|
||||
post4 := Post{Message: "test_4", Type: POST_LEAVE_CHANNEL}
|
||||
post4.PreSave()
|
||||
if !post4.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post4.IsSystemMessage() to be true")
|
||||
}
|
||||
|
||||
post5 := Post{Message: "test_5", Type: POST_ADD_REMOVE}
|
||||
post5.PreSave()
|
||||
if !post5.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post5.IsSystemMessage() to be true")
|
||||
}
|
||||
|
||||
post6 := Post{Message: "test_6", Type: POST_ADD_TO_CHANNEL}
|
||||
post6.PreSave()
|
||||
if !post6.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post6.IsSystemMessage() to be true")
|
||||
}
|
||||
|
||||
post7 := Post{Message: "test_7", Type: POST_REMOVE_FROM_CHANNEL}
|
||||
post7.PreSave()
|
||||
if !post7.IsUserActivitySystemMessage() {
|
||||
t.Fatalf("TestPostIsUserActivitySystemMessage failed, expected post7.IsSystemMessage() to be true")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,7 @@ func (s SqlPostStore) Save(post *model.Post) StoreChannel {
|
||||
} else {
|
||||
time := post.UpdateAt
|
||||
|
||||
if post.Type != model.POST_JOIN_LEAVE && post.Type != model.POST_JOIN_CHANNEL && post.Type != model.POST_LEAVE_CHANNEL &&
|
||||
post.Type != model.POST_ADD_REMOVE && post.Type != model.POST_ADD_TO_CHANNEL && post.Type != model.POST_REMOVE_FROM_CHANNEL {
|
||||
if !post.IsUserActivitySystemMessage() {
|
||||
s.GetMaster().Exec("UPDATE Channels SET LastPostAt = :LastPostAt, TotalMsgCount = TotalMsgCount + 1 WHERE Id = :ChannelId", map[string]interface{}{"LastPostAt": time, "ChannelId": post.ChannelId})
|
||||
} else {
|
||||
// don't update TotalMsgCount for unimportant messages so that the channel isn't marked as unread
|
||||
@@ -1355,3 +1354,29 @@ func (s SqlPostStore) GetPostsBatchForIndexing(startTime int64, limit int) Store
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlPostStore) GetLastPostForChannel(channelId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel, 1)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var post model.Post
|
||||
query := `
|
||||
SELECT * FROM Posts
|
||||
WHERE ChannelId = :channelId AND DeleteAt = 0
|
||||
ORDER BY CreateAt DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
if err := s.GetReplica().SelectOne(&post, query, map[string]interface{}{"channelId": channelId}); err != nil {
|
||||
l4g.Error(err)
|
||||
}
|
||||
|
||||
result.Data = &post
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
@@ -1661,3 +1661,37 @@ func TestPostStoreGetPostsBatchForIndexing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLastPostForChannel(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Post{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.UserId = model.NewId()
|
||||
o1.Message = ""
|
||||
o1.Type = model.POST_JOIN_CHANNEL
|
||||
|
||||
o1 = (<-store.Post().Save(o1)).Data.(*model.Post)
|
||||
|
||||
if r1 := <-store.Post().GetLastPostForChannel(o1.ChannelId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Post).Id != o1.Id {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
|
||||
o2 := &model.Post{}
|
||||
o2.ChannelId = o1.ChannelId
|
||||
o2.UserId = model.NewId()
|
||||
o2.Message = ""
|
||||
|
||||
o2 = (<-store.Post().Save(o2)).Data.(*model.Post)
|
||||
if r2 := <-store.Post().GetLastPostForChannel(o2.ChannelId); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
} else {
|
||||
if r2.Data.(*model.Post).Id != o2.Id {
|
||||
t.Fatal("invalid returned post")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ type PostStore interface {
|
||||
Overwrite(post *model.Post) StoreChannel
|
||||
GetPostsByIds(postIds []string) StoreChannel
|
||||
GetPostsBatchForIndexing(startTime int64, limit int) StoreChannel
|
||||
GetLastPostForChannel(channelId string) StoreChannel
|
||||
}
|
||||
|
||||
type UserStore interface {
|
||||
|
||||
@@ -7,6 +7,13 @@ import {FormattedMessage} from 'react-intl';
|
||||
import {PostTypes} from 'utils/constants.jsx';
|
||||
import {formatText} from 'utils/text_formatting.jsx';
|
||||
|
||||
const joinLeaveMessageGetters = {
|
||||
[PostTypes.JOIN_CHANNEL]: getJoinChannelMessage,
|
||||
[PostTypes.LEAVE_CHANNEL]: getLeaveChannelMessage,
|
||||
[PostTypes.ADD_TO_CHANNEL]: getAddToChannelMessage,
|
||||
[PostTypes.REMOVE_FROM_CHANNEL]: getRemoveFromChannelMessage
|
||||
};
|
||||
|
||||
function renderUsername(value, options) {
|
||||
return renderFormattedText(value, {...options, markdown: false});
|
||||
}
|
||||
@@ -15,38 +22,60 @@ function renderFormattedText(value, options) {
|
||||
return <span dangerouslySetInnerHTML={{__html: formatText(value, options)}}/>;
|
||||
}
|
||||
|
||||
function renderJoinLeaveMessage(post, options, messageFunction) {
|
||||
if (post.props.messages) {
|
||||
return (
|
||||
<div>
|
||||
{post.props.messages.map((message, key) => <span key={key}> {joinLeaveMessageGetters[message.type](message, options)}</span>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return messageFunction(post.props, options);
|
||||
}
|
||||
|
||||
function renderJoinChannelMessage(post, options) {
|
||||
const username = renderUsername(post.props.username, options);
|
||||
return renderJoinLeaveMessage(post, options, getJoinChannelMessage);
|
||||
}
|
||||
|
||||
function getJoinChannelMessage(messageProps, options) {
|
||||
const username = renderUsername(messageProps.username, options);
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='api.channel.join_channel.post_and_forget'
|
||||
defaultMessage='{username} has joined the channel.'
|
||||
defaultMessage='{username} joined the channel.'
|
||||
values={{username}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderLeaveChannelMessage(post, options) {
|
||||
const username = renderUsername(post.props.username, options);
|
||||
return renderJoinLeaveMessage(post, options, getLeaveChannelMessage);
|
||||
}
|
||||
|
||||
function getLeaveChannelMessage(messageProps, options) {
|
||||
const username = renderUsername(messageProps.username, options);
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='api.channel.leave.left'
|
||||
defaultMessage='{username} has left the channel.'
|
||||
defaultMessage='{username} left the channel.'
|
||||
values={{username}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function renderAddToChannelMessage(post, options) {
|
||||
const username = renderUsername(post.props.username, options);
|
||||
const addedUsername = renderUsername(post.props.addedUsername, options);
|
||||
return renderJoinLeaveMessage(post, options, getAddToChannelMessage);
|
||||
}
|
||||
|
||||
function getAddToChannelMessage(messageProps, options) {
|
||||
const username = renderUsername(messageProps.username, options);
|
||||
const addedUsername = renderUsername(messageProps.addedUsername, options);
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='api.channel.add_member.added'
|
||||
defaultMessage='{addedUsername} added to the channel by {username}'
|
||||
defaultMessage='{username} added {addedUsername} to the channel.'
|
||||
values={{
|
||||
username,
|
||||
addedUsername
|
||||
@@ -56,12 +85,16 @@ function renderAddToChannelMessage(post, options) {
|
||||
}
|
||||
|
||||
function renderRemoveFromChannelMessage(post, options) {
|
||||
const removedUsername = renderUsername(post.props.removedUsername, options);
|
||||
return renderJoinLeaveMessage(post, options, getRemoveFromChannelMessage);
|
||||
}
|
||||
|
||||
function getRemoveFromChannelMessage(messageProps, options) {
|
||||
const removedUsername = renderUsername(messageProps.removedUsername, options);
|
||||
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='api.channel.remove_member.removed'
|
||||
defaultMessage='{removedUsername} was removed from the channel'
|
||||
defaultMessage='{removedUsername} was removed from the channel.'
|
||||
values={{
|
||||
removedUsername
|
||||
}}
|
||||
|
||||
@@ -1015,15 +1015,15 @@
|
||||
"analytics.team.title": "Team Statistics for {team}",
|
||||
"analytics.team.totalPosts": "Total Posts",
|
||||
"analytics.team.totalUsers": "Total Users",
|
||||
"api.channel.add_member.added": "{addedUsername} added to the channel by {username}",
|
||||
"api.channel.add_member.added": " {username} added {addedUsername} to the channel.",
|
||||
"api.channel.delete_channel.archived": "{username} has archived the channel.",
|
||||
"api.channel.join_channel.post_and_forget": "{username} has joined the channel.",
|
||||
"api.channel.leave.left": "{username} has left the channel.",
|
||||
"api.channel.join_channel.post_and_forget": "{username} joined the channel.",
|
||||
"api.channel.leave.left": "{username} left the channel.",
|
||||
"api.channel.post_update_channel_displayname_message_and_forget.updated_from": "{username} updated the channel display name from: {old} to: {new}",
|
||||
"api.channel.post_update_channel_header_message_and_forget.removed": "{username} removed the channel header (was: {old})",
|
||||
"api.channel.post_update_channel_header_message_and_forget.updated_from": "{username} updated the channel header from: {old} to: {new}",
|
||||
"api.channel.post_update_channel_header_message_and_forget.updated_to": "{username} updated the channel header to: {new}",
|
||||
"api.channel.remove_member.removed": "{removedUsername} was removed from the channel",
|
||||
"api.channel.remove_member.removed": "{removedUsername} was removed from the channel.",
|
||||
"app.channel.post_update_channel_purpose_message.removed": "{username} removed the channel purpose (was: {old})",
|
||||
"app.channel.post_update_channel_purpose_message.updated_from": "{username} updated the channel purpose from: {old} to: {new}",
|
||||
"app.channel.post_update_channel_purpose_message.updated_to": "{username} updated the channel purpose to: {new}",
|
||||
|
||||
Reference in New Issue
Block a user