diff options
author | Carlos Tadeu Panato Junior <ctadeu@gmail.com> | 2018-04-16 14:23:58 +0200 |
---|---|---|
committer | Jesús Espino <jespinog@gmail.com> | 2018-04-16 14:23:58 +0200 |
commit | bf24f51c4e1cc6286885460672f7f449e8c6f5ef (patch) | |
tree | 147773b452dd017451dcc63f3f752b82d5c7694a /app | |
parent | 0759cf639d1872680c9fc204cdef91cb784fac72 (diff) | |
download | chat-bf24f51c4e1cc6286885460672f7f449e8c6f5ef.tar.gz chat-bf24f51c4e1cc6286885460672f7f449e8c6f5ef.tar.bz2 chat-bf24f51c4e1cc6286885460672f7f449e8c6f5ef.zip |
[MM-9904] Add /invite slash command to invite users to a channel (#8482)
* [MM-9904] Add /invite slash command to invite users to a channel
* Update en.json
Diffstat (limited to 'app')
-rw-r--r-- | app/apptestlib.go | 14 | ||||
-rw-r--r-- | app/command_invite.go | 102 | ||||
-rw-r--r-- | app/command_invite_test.go | 108 |
3 files changed, 224 insertions, 0 deletions
diff --git a/app/apptestlib.go b/app/apptestlib.go index 6c2273c6e..1b22831c9 100644 --- a/app/apptestlib.go +++ b/app/apptestlib.go @@ -218,6 +218,20 @@ func (me *TestHelper) createChannel(team *model.Team, channelType string) *model return channel } +func (me *TestHelper) CreateDmChannel(user *model.User) *model.Channel { + utils.DisableDebugLogForTest() + var err *model.AppError + var channel *model.Channel + if channel, err = me.App.CreateDirectChannel(me.BasicUser.Id, user.Id); err != nil { + l4g.Error(err.Error()) + l4g.Close() + time.Sleep(time.Second) + panic(err) + } + utils.EnableDebugLogForTest() + return channel +} + func (me *TestHelper) CreatePost(channel *model.Channel) *model.Post { id := model.NewId() diff --git a/app/command_invite.go b/app/command_invite.go new file mode 100644 index 000000000..ce443bf3d --- /dev/null +++ b/app/command_invite.go @@ -0,0 +1,102 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "strings" + + l4g "github.com/alecthomas/log4go" + "github.com/mattermost/mattermost-server/model" + goi18n "github.com/nicksnyder/go-i18n/i18n" +) + +type InviteProvider struct { +} + +const ( + CMD_INVITE = "invite" +) + +func init() { + RegisterCommandProvider(&InviteProvider{}) +} + +func (me *InviteProvider) GetTrigger() string { + return CMD_INVITE +} + +func (me *InviteProvider) GetCommand(a *App, T goi18n.TranslateFunc) *model.Command { + return &model.Command{ + Trigger: CMD_INVITE, + AutoComplete: true, + AutoCompleteDesc: T("api.command_invite.desc"), + AutoCompleteHint: T("api.command_invite.hint"), + DisplayName: T("api.command_invite.name"), + } +} + +func (me *InviteProvider) DoCommand(a *App, args *model.CommandArgs, message string) *model.CommandResponse { + if message == "" { + return &model.CommandResponse{Text: args.T("api.command_invite.missing_message.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + l4g.Debug(message) + + splitMessage := strings.SplitN(message, " ", 2) + targetUsername := splitMessage[0] + targetUsername = strings.TrimPrefix(targetUsername, "@") + + var userProfile *model.User + if result := <-a.Srv.Store.User().GetByUsername(targetUsername); result.Err != nil { + l4g.Error(result.Err.Error()) + return &model.CommandResponse{Text: args.T("api.command_invite.missing_user.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } else { + userProfile = result.Data.(*model.User) + } + + var channelToJoin *model.Channel + var err *model.AppError + // User set a channel to add the invited user + if len(splitMessage) > 1 && splitMessage[1] != "" { + targetChannelName := strings.TrimPrefix(strings.TrimSpace(splitMessage[1]), "~") + + if channelToJoin, err = a.GetChannelByName(targetChannelName, args.TeamId); err != nil { + return &model.CommandResponse{Text: args.T("api.command_invite.channel.error", map[string]interface{}{"Channel": targetChannelName}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + } else { + channelToJoin, err = a.GetChannel(args.ChannelId) + if err != nil { + return &model.CommandResponse{Text: args.T("api.command_invite.channel.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + } + + // Check if is a Direct Channel + if channelToJoin.Type == model.CHANNEL_DIRECT || channelToJoin.Type == model.CHANNEL_GROUP { + return &model.CommandResponse{Text: args.T("api.command_invite.directchannel.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + // Check if user is already in the channel + _, err = a.GetChannelMember(channelToJoin.Id, userProfile.Id) + if err == nil { + return &model.CommandResponse{Text: args.T("api.command_invite.user_already_in_channel.app_error", map[string]interface{}{"User": userProfile.Username}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + if channelToJoin.Type == model.CHANNEL_OPEN && !a.SessionHasPermissionToChannel(args.Session, channelToJoin.Id, model.PERMISSION_MANAGE_PUBLIC_CHANNEL_MEMBERS) { + return &model.CommandResponse{Text: args.T("api.command_invite.permission.app_error", map[string]interface{}{"User": userProfile.Username, "Channel": channelToJoin.Name}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + if channelToJoin.Type == model.CHANNEL_PRIVATE && !a.SessionHasPermissionToChannel(args.Session, channelToJoin.Id, model.PERMISSION_MANAGE_PRIVATE_CHANNEL_MEMBERS) { + return &model.CommandResponse{Text: args.T("api.command_invite.permission.app_error", map[string]interface{}{"User": userProfile.Username, "Channel": channelToJoin.Name}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + if _, err := a.AddChannelMember(userProfile.Id, channelToJoin, args.Session.UserId, ""); err != nil { + return &model.CommandResponse{Text: args.T("api.command_invite.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + if args.ChannelId != channelToJoin.Id { + return &model.CommandResponse{Text: args.T("api.command_invite.success", map[string]interface{}{"User": userProfile.Username, "Channel": channelToJoin.Name}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + + return &model.CommandResponse{} +} diff --git a/app/command_invite_test.go b/app/command_invite_test.go new file mode 100644 index 000000000..c46bc4628 --- /dev/null +++ b/app/command_invite_test.go @@ -0,0 +1,108 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package app + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/mattermost/mattermost-server/model" +) + +func TestInviteProvider(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + channel := th.createChannel(th.BasicTeam, model.CHANNEL_OPEN) + privateChannel := th.createChannel(th.BasicTeam, model.CHANNEL_PRIVATE) + dmChannel := th.CreateDmChannel(th.BasicUser2) + + basicUser3 := th.CreateUser() + th.LinkUserToTeam(basicUser3, th.BasicTeam) + basicUser4 := th.CreateUser() + + InviteP := InviteProvider{} + args := &model.CommandArgs{ + T: func(s string, args ...interface{}) string { return s }, + ChannelId: th.BasicChannel.Id, + TeamId: th.BasicTeam.Id, + Session: model.Session{UserId: th.BasicUser.Id, TeamMembers: []*model.TeamMember{{TeamId: th.BasicTeam.Id, Roles: model.TEAM_USER_ROLE_ID}}}, + } + + userAndWrongChannel := "@" + th.BasicUser2.Username + " wrongchannel1" + userAndChannel := "@" + th.BasicUser2.Username + " ~" + channel.Name + " " + userAndDisplayChannel := "@" + th.BasicUser2.Username + " ~" + channel.DisplayName + " " + userAndPrivateChannel := "@" + th.BasicUser2.Username + " ~" + privateChannel.Name + userAndDMChannel := "@" + basicUser3.Username + " ~" + dmChannel.Name + + tests := []struct { + desc string + expected string + msg string + }{ + { + desc: "Missing user and channel in the command", + expected: "api.command_invite.missing_message.app_error", + msg: "", + }, + { + desc: "User added in the current channel", + expected: "", + msg: th.BasicUser2.Username, + }, + { + desc: "Add user to another channel not the current", + expected: "api.command_invite.success", + msg: userAndChannel, + }, + { + desc: "try to add a user to a direct channel", + expected: "api.command_invite.directchannel.app_error", + msg: userAndDMChannel, + }, + { + desc: "Try to add a user to a invalid channel", + expected: "api.command_invite.channel.error", + msg: userAndWrongChannel, + }, + { + desc: "Try to add a user to an private channel", + expected: "api.command_invite.success", + msg: userAndPrivateChannel, + }, + { + desc: "Using display channel name which is different form Channel name", + expected: "api.command_invite.channel.error", + msg: userAndDisplayChannel, + }, + { + desc: "Invalid user to current channel", + expected: "api.command_invite.missing_user.app_error", + msg: "@invalidUser123", + }, + { + desc: "Invalid user to current channel without @", + expected: "api.command_invite.missing_user.app_error", + msg: "invalidUser321", + }, + { + desc: "try to add a user which is not part of the team", + expected: "api.command_invite.fail.app_error", + msg: basicUser4.Username, + }, + { + desc: "try to add a user to a direct channel", + expected: "api.command_invite.directchannel.app_error", + msg: userAndDMChannel, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + actual := InviteP.DoCommand(th.App, args, test.msg).Text + assert.Equal(t, test.expected, actual) + }) + } +} |