diff --git a/.meteor/packages b/.meteor/packages
index ad4d8c5a198..b56ad45f922 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -61,6 +61,7 @@ rocketchat:oembed
rocketchat:slashcommands-invite
rocketchat:slashcommands-join
rocketchat:slashcommands-leave
+rocketchat:slashcommands-kick
rocketchat:spotify
rocketchat:statistics
rocketchat:theme
diff --git a/.meteor/versions b/.meteor/versions
index 1a0b0849829..5ec2cf20d5f 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -150,6 +150,7 @@ rocketchat:message-star@0.0.1
rocketchat:oembed@0.0.1
rocketchat:slashcommands-invite@0.0.1
rocketchat:slashcommands-join@0.0.1
+rocketchat:slashcommands-kick@0.0.1
rocketchat:slashcommands-leave@0.0.1
rocketchat:spotify@0.0.1
rocketchat:statistics@0.0.1
diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json
index 85fb340d789..018e3f6aaef 100644
--- a/i18n/en.i18n.json
+++ b/i18n/en.i18n.json
@@ -368,6 +368,7 @@
"Remove" : "Remove",
"Remove_Admin" : "Remove Admin",
"Remove_custom_oauth" : "Remove custom oauth",
+ "Remove_from_room" : "Remove from room",
"Reset_password" : "Reset password",
"Restart" : "Restart",
"Restart_the_server" : "Restart the server",
@@ -494,6 +495,7 @@
"User_not_found_or_incorrect_password" : "User not found or incorrect password",
"User_or_channel_name" : "User or channel name",
"User_removed_by" : "User __user_removed__ removed by __user_by__.",
+ "User_removed_from_room" : "The user has been removed from the room",
"User_Settings" : "User Settings",
"User_updated_successfully" : "User updated successfully",
"Username" : "Username",
diff --git a/packages/rocketchat-authorization/server/startup.coffee b/packages/rocketchat-authorization/server/startup.coffee
index 905a43410e6..d7043df9ba7 100644
--- a/packages/rocketchat-authorization/server/startup.coffee
+++ b/packages/rocketchat-authorization/server/startup.coffee
@@ -60,6 +60,9 @@ Meteor.startup ->
{ _id: 'delete-message',
roles : ['admin', 'site-moderator', 'moderator']}
+ { _id: 'remove-user',
+ roles : ['admin', 'site-moderator', 'moderator']}
+
{ _id: 'ban-user',
roles : ['admin', 'site-moderator', 'moderator']}
diff --git a/packages/rocketchat-slashcommands-kick/client.coffee b/packages/rocketchat-slashcommands-kick/client.coffee
new file mode 100644
index 00000000000..acc40d33db0
--- /dev/null
+++ b/packages/rocketchat-slashcommands-kick/client.coffee
@@ -0,0 +1,11 @@
+RocketChat.slashCommands.add 'kick', (command, params, item) ->
+ username = params.trim()
+ if username is ''
+ return
+ username = username.replace('@', '')
+
+ if Session.get('showUserInfo') is username
+ Session.set('showUserInfo', null)
+,
+ description: TAPi18n.__ 'Remove_someone_from_room'
+ params: '@username'
diff --git a/packages/rocketchat-slashcommands-kick/i18n/en.i18n.json b/packages/rocketchat-slashcommands-kick/i18n/en.i18n.json
new file mode 100644
index 00000000000..5f448b30f89
--- /dev/null
+++ b/packages/rocketchat-slashcommands-kick/i18n/en.i18n.json
@@ -0,0 +1,5 @@
+{
+ "Username_doesnt_exist" : "The username `#%s` doesn't exist.",
+ "Username_is_not_in_this_room" : "The user `#%s` is not in this room.",
+ "Remove_someone_from_room" : "Remove someone from the room"
+}
diff --git a/packages/rocketchat-slashcommands-kick/package.js b/packages/rocketchat-slashcommands-kick/package.js
new file mode 100644
index 00000000000..a5ac2b24a42
--- /dev/null
+++ b/packages/rocketchat-slashcommands-kick/package.js
@@ -0,0 +1,37 @@
+Package.describe({
+ name: 'rocketchat:slashcommands-kick',
+ version: '0.0.1',
+ summary: 'Command handler for the /kick command',
+ git: ''
+});
+
+Package.onUse(function(api) {
+
+ api.versionsFrom('1.0');
+
+ api.use([
+ 'coffeescript',
+ 'check',
+ 'rocketchat:lib@0.0.1'
+ ]);
+
+ api.addFiles('client.coffee', 'client');
+ api.addFiles('server.coffee', 'server');
+
+ // TAPi18n
+ api.use('templating', 'client');
+ var _ = Npm.require('underscore');
+ var fs = Npm.require('fs');
+ tapi18nFiles = _.compact(_.map(fs.readdirSync('packages/rocketchat-slashcommands-kick/i18n'), function(filename) {
+ if (fs.statSync('packages/rocketchat-slashcommands-kick/i18n/' + filename).size > 16) {
+ return 'i18n/' + filename;
+ }
+ }));
+ api.use('tap:i18n@1.6.1', ['client', 'server']);
+ api.imply('tap:i18n');
+ api.addFiles(tapi18nFiles, ['client', 'server']);
+});
+
+Package.onTest(function(api) {
+
+});
diff --git a/packages/rocketchat-slashcommands-kick/server.coffee b/packages/rocketchat-slashcommands-kick/server.coffee
new file mode 100644
index 00000000000..112313be987
--- /dev/null
+++ b/packages/rocketchat-slashcommands-kick/server.coffee
@@ -0,0 +1,40 @@
+###
+# Kick is a named function that will replace /kick commands
+###
+
+class Kick
+ constructor: (command, params, item) ->
+ if command isnt 'kick' or not Match.test params, String
+ return
+
+ username = params.trim()
+ if username is ''
+ return
+
+ username = username.replace('@', '')
+
+ user = Meteor.users.findOne Meteor.userId()
+ kickedUser = RocketChat.models.Users.findOneByUsername username
+ room = RocketChat.models.Rooms.findOneById item.rid
+
+ if not kickedUser?
+ RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
+ _id: Random.id()
+ rid: item.rid
+ ts: new Date
+ msg: TAPi18n.__('Username_doesnt_exist', { postProcess: 'sprintf', sprintf: [ username ] }, user.language);
+ }
+ return
+
+ if username not in (room.usernames or [])
+ RocketChat.Notifications.notifyUser Meteor.userId(), 'message', {
+ _id: Random.id()
+ rid: item.rid
+ ts: new Date
+ msg: TAPi18n.__('Username_is_not_in_this_room', { postProcess: 'sprintf', sprintf: [ username ] }, user.language);
+ }
+ return
+
+ Meteor.call 'removeUserFromRoom', { rid: item.rid, username: username }
+
+RocketChat.slashCommands.add 'kick', Kick
diff --git a/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.coffee b/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.coffee
index 341af443b10..00d323ef43c 100644
--- a/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.coffee
+++ b/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.coffee
@@ -33,6 +33,9 @@ Template.userInfo.helpers
if @utcOffset?
return Template.instance().now.get().utcOffset(@utcOffset).format('HH:mm')
+ canRemoveUser: ->
+ return RocketChat.authz.hasAllPermission('remove-user', Session.get('openedRoom'))
+
Template.userInfo.events
'click .pvt-msg': (e) ->
Meteor.call 'createDirectMessage', Session.get('showUserInfo'), (error, result) ->
@@ -72,6 +75,19 @@ Template.userInfo.events
'click .back': (e) ->
Session.set('showUserInfo', null)
+ 'click .remove-user': (e, t) ->
+ e.preventDefault()
+ rid = Session.get('openedRoom')
+ room = ChatRoom.findOne rid
+ if RocketChat.authz.hasAllPermission('remove-user', rid)
+ Meteor.call 'removeUserFromRoom', { rid: rid, username: @user.username }, (err, result) ->
+ if err
+ return toastr.error(err.reason or err.message)
+ toastr.success TAPi18n.__ 'User_removed_from_room'
+ Session.set('showUserInfo', null)
+ else
+ toastr.error(TAPi18n.__ 'Not_allowed')
+
Template.userInfo.onCreated ->
@now = new ReactiveVar moment()
diff --git a/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.html b/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.html
index 844f5126c3e..85fa40e7a79 100644
--- a/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.html
+++ b/packages/rocketchat-ui-flextab/flex-tab/tabs/userInfo.html
@@ -32,7 +32,10 @@
{{#if showAll}}
{{#if canDirectMessage user.username}}
-
+
+ {{/if}}
+ {{#if canRemoveUser}}
+
{{/if}}
{{/if}}
diff --git a/packages/rocketchat-ui/views/app/room.coffee b/packages/rocketchat-ui/views/app/room.coffee
index b7c9ede601c..e382d70faa3 100644
--- a/packages/rocketchat-ui/views/app/room.coffee
+++ b/packages/rocketchat-ui/views/app/room.coffee
@@ -301,7 +301,7 @@ Template.room.events
"click .flex-tab .user-image > a" : (e) ->
RocketChat.TabBar.openFlex()
- Session.set('showUserInfo', $(e.currentTarget).data('username'))
+ Session.set('showUserInfo', @username)
'click .user-card-message': (e) ->
roomData = Session.get('roomData' + this._arguments[1].rid)
diff --git a/server/methods/removeUserFromRoom.coffee b/server/methods/removeUserFromRoom.coffee
index d600b6d991c..c77598660c0 100644
--- a/server/methods/removeUserFromRoom.coffee
+++ b/server/methods/removeUserFromRoom.coffee
@@ -1,25 +1,31 @@
Meteor.methods
removeUserFromRoom: (data) ->
fromId = Meteor.userId()
- # console.log '[methods] removeUserFromRoom -> '.green, 'fromId:', fromId, 'data:', data
+ console.log '[methods] removeUserFromRoom -> '.green, 'fromId:', fromId, 'data:', data
+
+ check(data, Match.ObjectIncluding({ rid: String, username: String }))
+
+ unless RocketChat.authz.hasPermission(fromId, 'remove-user', data.rid)
+ throw new Meteor.Error 'not-allowed', 'Not allowed'
room = RocketChat.models.Rooms.findOneById data.rid
- if room.u?._id isnt Meteor.userId() and room.t is 'c'
- throw new Meteor.Error 403, 'Not allowed'
+ if data.username not in (room?.usernames or [])
+ throw new Meteor.Error 'not-in-room', 'User is not in this room'
removedUser = RocketChat.models.Users.findOneByUsername data.username
RocketChat.models.Rooms.removeUsernameById data.rid, data.username
- RocketChat.models.Subscriptions.removeByRoomIdAndUserId data.rid, data.username
+ RocketChat.models.Subscriptions.removeByRoomIdAndUserId data.rid, removedUser._id
- switch room.t
- when 'c'
- RocketChat.authz.removeUsersFromRole(removedUser._id; 'channel-moderator', data.rid)
- when 'p'
- RocketChat.authz.removeUsersFromRole(removedUser._id; 'group-moderator', data.rid)
+ if room.t in [ 'c', 'p' ]
+ RocketChat.authz.removeUsersFromRoles(removedUser._id; 'moderator', data.rid)
- RocketChat.models.Messages.createUserRemovedWithRoomIdAndUser data.rid, removedUser
+ fromUser = RocketChat.models.Users.findOneById fromId
+ RocketChat.models.Messages.createUserRemovedWithRoomIdAndUser data.rid, removedUser,
+ u:
+ _id: fromUser._id
+ username: fromUser.username
return true