mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
PLT-2863 adding remove user from team (#3429)
* PLT-2863 adding remove user from team * PLT-2863 adding the client side UI * Fixing trailing space * Fixing reported issues * Adding documentatino * Switching to final javascript driver
This commit is contained in:
@@ -334,6 +334,43 @@ func TestCliJoinTeam(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCliLeaveTeam(t *testing.T) {
|
||||
if disableCliTests {
|
||||
return
|
||||
}
|
||||
|
||||
th := Setup().InitBasic()
|
||||
|
||||
cmd := exec.Command("bash", "-c", `go run ../mattermost.go -leave_team -team_name="`+th.BasicTeam.Name+`" -email="`+th.BasicUser.Email+`"`)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Log(string(output))
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
profiles := th.BasicClient.Must(th.BasicClient.GetProfiles(th.BasicTeam.Id, "")).Data.(map[string]*model.User)
|
||||
|
||||
found := false
|
||||
|
||||
for _, user := range profiles {
|
||||
if user.Email == th.BasicUser.Email {
|
||||
found = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Fatal("profile still should be in team even if deleted")
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Team().GetTeamsByUserId(th.BasicUser.Id); result.Err != nil {
|
||||
teamMembers := result.Data.([]*model.TeamMember)
|
||||
if len(teamMembers) > 0 {
|
||||
t.Fatal("Shouldn't be in team")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCliResetPassword(t *testing.T) {
|
||||
if disableCliTests {
|
||||
return
|
||||
|
||||
115
api/team.go
115
api/team.go
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
@@ -39,6 +38,7 @@ func InitTeam() {
|
||||
BaseRoutes.NeedTeam.Handle("/invite_members", ApiUserRequired(inviteMembers)).Methods("POST")
|
||||
|
||||
BaseRoutes.NeedTeam.Handle("/add_user_to_team", ApiUserRequired(addUserToTeam)).Methods("POST")
|
||||
BaseRoutes.NeedTeam.Handle("/remove_user_from_team", ApiUserRequired(removeUserFromTeam)).Methods("POST")
|
||||
|
||||
// These should be moved to the global admin console
|
||||
BaseRoutes.NeedTeam.Handle("/import_team", ApiUserRequired(importTeam)).Methods("POST")
|
||||
@@ -266,11 +266,23 @@ func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
|
||||
channelRole = model.CHANNEL_ROLE_ADMIN
|
||||
}
|
||||
|
||||
if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil {
|
||||
if tmr.Err.Id == store.TEAM_MEMBER_EXISTS_ERROR {
|
||||
if etmr := <-Srv.Store.Team().GetMember(team.Id, user.Id); etmr.Err == nil {
|
||||
// Membership alredy exists. Check if deleted and and update, otherwise do nothing
|
||||
rtm := etmr.Data.(model.TeamMember)
|
||||
|
||||
// Do nothing if already added
|
||||
if rtm.DeleteAt == 0 {
|
||||
return nil
|
||||
}
|
||||
return tmr.Err
|
||||
|
||||
if tmr := <-Srv.Store.Team().UpdateMember(tm); tmr.Err != nil {
|
||||
return tmr.Err
|
||||
}
|
||||
} else {
|
||||
// Membership appears to be missing. Lets try to add.
|
||||
if tmr := <-Srv.Store.Team().SaveMember(tm); tmr.Err != nil {
|
||||
return tmr.Err
|
||||
}
|
||||
}
|
||||
|
||||
if uua := <-Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil {
|
||||
@@ -291,6 +303,56 @@ func JoinUserToTeam(team *model.Team, user *model.User) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
func LeaveTeam(team *model.Team, user *model.User) *model.AppError {
|
||||
|
||||
var teamMember model.TeamMember
|
||||
|
||||
if result := <-Srv.Store.Team().GetMember(team.Id, user.Id); result.Err != nil {
|
||||
return model.NewLocAppError("RemoveUserFromTeam", "api.team.remove_user_from_team.missing.app_error", nil, result.Err.Error())
|
||||
} else {
|
||||
teamMember = result.Data.(model.TeamMember)
|
||||
}
|
||||
|
||||
var channelMembers *model.ChannelList
|
||||
|
||||
if result := <-Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil {
|
||||
if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" {
|
||||
channelMembers = &model.ChannelList{make([]*model.Channel, 0), make(map[string]*model.ChannelMember)}
|
||||
} else {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
} else {
|
||||
channelMembers = result.Data.(*model.ChannelList)
|
||||
}
|
||||
|
||||
for _, channel := range channelMembers.Channels {
|
||||
if channel.Type != model.CHANNEL_DIRECT {
|
||||
if result := <-Srv.Store.Channel().RemoveMember(channel.Id, user.Id); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
teamMember.Roles = ""
|
||||
teamMember.DeleteAt = model.GetMillis()
|
||||
|
||||
if result := <-Srv.Store.Team().UpdateMember(&teamMember); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if uua := <-Srv.Store.User().UpdateUpdateAt(user.Id); uua.Err != nil {
|
||||
return uua.Err
|
||||
}
|
||||
|
||||
RemoveAllSessionsForUserId(user.Id)
|
||||
InvalidateCacheForUser(user.Id)
|
||||
|
||||
go Publish(model.NewMessage(team.Id, "", user.Id, model.ACTION_LEAVE_TEAM))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isTeamCreationAllowed(c *Context, email string) bool {
|
||||
|
||||
email = strings.ToLower(email)
|
||||
@@ -483,6 +545,51 @@ func addUserToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(model.MapToJson(params)))
|
||||
}
|
||||
|
||||
func removeUserFromTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
params := model.MapFromJson(r.Body)
|
||||
userId := params["user_id"]
|
||||
|
||||
if len(userId) != 26 {
|
||||
c.SetInvalidParam("removeUserFromTeam", "user_id")
|
||||
return
|
||||
}
|
||||
|
||||
tchan := Srv.Store.Team().Get(c.TeamId)
|
||||
uchan := Srv.Store.User().Get(userId)
|
||||
|
||||
var team *model.Team
|
||||
if result := <-tchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
team = result.Data.(*model.Team)
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if result := <-uchan; result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
user = result.Data.(*model.User)
|
||||
}
|
||||
|
||||
if c.Session.UserId != user.Id {
|
||||
if !c.IsTeamAdmin() {
|
||||
c.Err = model.NewLocAppError("removeUserFromTeam", "api.team.update_team.permissions.app_error", nil, "userId="+c.Session.UserId)
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err := LeaveTeam(team, user)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
w.Write([]byte(model.MapToJson(params)))
|
||||
}
|
||||
|
||||
func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := model.MapFromJson(r.Body)
|
||||
|
||||
@@ -158,7 +158,7 @@ func TestAddUserToTeam(t *testing.T) {
|
||||
}
|
||||
|
||||
user2 := th.CreateUser(th.BasicClient)
|
||||
if result, err := th.BasicClient.AddUserToTeam(user2.Id); err != nil {
|
||||
if result, err := th.BasicClient.AddUserToTeam("", user2.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rm := result.Data.(map[string]string)
|
||||
@@ -168,6 +168,39 @@ func TestAddUserToTeam(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveUserFromTeam(t *testing.T) {
|
||||
th := Setup().InitSystemAdmin().InitBasic()
|
||||
|
||||
if _, err := th.BasicClient.RemoveUserFromTeam(th.SystemAdminTeam.Id, th.SystemAdminUser.Id); err == nil {
|
||||
t.Fatal("should fail not enough permissions")
|
||||
} else {
|
||||
if err.Id != "api.context.permissions.app_error" {
|
||||
t.Fatal("wrong error")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := th.BasicClient.RemoveUserFromTeam("", th.SystemAdminUser.Id); err == nil {
|
||||
t.Fatal("should fail not enough permissions")
|
||||
} else {
|
||||
if err.Id != "api.team.update_team.permissions.app_error" {
|
||||
t.Fatal("wrong error")
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := th.BasicClient.RemoveUserFromTeam("", th.BasicUser.Id); err != nil {
|
||||
t.Fatal("should have removed the user from the team")
|
||||
}
|
||||
|
||||
th.BasicClient.Logout()
|
||||
th.LoginSystemAdmin()
|
||||
|
||||
th.SystemAdminClient.Must(th.SystemAdminClient.AddUserToTeam(th.BasicTeam.Id, th.BasicUser.Id))
|
||||
|
||||
if _, err := th.SystemAdminClient.RemoveUserFromTeam(th.BasicTeam.Id, th.BasicUser.Id); err != nil {
|
||||
t.Fatal("should have removed the user from the team")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUserToTeamFromInvite(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
th.BasicClient.Logout()
|
||||
|
||||
25
i18n/en.json
25
i18n/en.json
@@ -1263,6 +1263,10 @@
|
||||
"id": "api.slackimport.slack_import.zip.app_error",
|
||||
"translation": "Unable to open zip file"
|
||||
},
|
||||
{
|
||||
"id": "api.team.remove_user_from_team.missing.app_error",
|
||||
"translation": "The user does not appear to be part of this team."
|
||||
},
|
||||
{
|
||||
"id": "api.team.create_team.email_disabled.app_error",
|
||||
"translation": "Team sign-up with email is disabled."
|
||||
@@ -4223,6 +4227,27 @@
|
||||
"id": "utils.mail.test.configured.error",
|
||||
"translation": "SMTP server settings do not appear to be configured properly err=%v details=%v"
|
||||
},
|
||||
|
||||
{
|
||||
"id": "utils.mail.test.configured.error",
|
||||
"translation": "SMTP server settings do not appear to be configured properly err=%v details=%v"
|
||||
},
|
||||
{
|
||||
"id": "leave_team_modal.title",
|
||||
"translation": "Leave the team?"
|
||||
},
|
||||
{
|
||||
"id": "leave_team_modal.desc",
|
||||
"translation": "You will be removed from all public channels and private groups. If the team is private you will not be able to rejoin the team. Are you sure?"
|
||||
},
|
||||
{
|
||||
"id": "leave_team_modal.no",
|
||||
"translation": "No"
|
||||
},
|
||||
{
|
||||
"id": "leave_team_modal.yes",
|
||||
"translation": "Yes"
|
||||
},
|
||||
{
|
||||
"id": "web.admin_console.title",
|
||||
"translation": "Admin Console"
|
||||
|
||||
@@ -50,6 +50,7 @@ var flagCmdLeaveChannel bool
|
||||
var flagCmdListChannels bool
|
||||
var flagCmdRestoreChannel bool
|
||||
var flagCmdJoinTeam bool
|
||||
var flagCmdLeaveTeam bool
|
||||
var flagCmdVersion bool
|
||||
var flagCmdRunWebClientTests bool
|
||||
var flagCmdRunJavascriptClientTests bool
|
||||
@@ -286,6 +287,7 @@ func parseCmds() {
|
||||
flag.BoolVar(&flagCmdListChannels, "list_channels", false, "")
|
||||
flag.BoolVar(&flagCmdRestoreChannel, "restore_channel", false, "")
|
||||
flag.BoolVar(&flagCmdJoinTeam, "join_team", false, "")
|
||||
flag.BoolVar(&flagCmdLeaveTeam, "leave_team", false, "")
|
||||
flag.BoolVar(&flagCmdVersion, "version", false, "")
|
||||
flag.BoolVar(&flagCmdRunWebClientTests, "run_web_client_tests", false, "")
|
||||
flag.BoolVar(&flagCmdRunJavascriptClientTests, "run_javascript_client_tests", false, "")
|
||||
@@ -303,6 +305,7 @@ func parseCmds() {
|
||||
flagRunCmds = (flagCmdCreateTeam ||
|
||||
flagCmdCreateUser ||
|
||||
flagCmdInviteUser ||
|
||||
flagCmdLeaveTeam ||
|
||||
flagCmdAssignRole ||
|
||||
flagCmdJoinChannel ||
|
||||
flagCmdLeaveChannel ||
|
||||
@@ -328,6 +331,7 @@ func runCmds() {
|
||||
cmdCreateTeam()
|
||||
cmdCreateUser()
|
||||
cmdInviteUser()
|
||||
cmdLeaveTeam()
|
||||
cmdAssignRole()
|
||||
cmdJoinChannel()
|
||||
cmdLeaveChannel()
|
||||
@@ -1187,6 +1191,47 @@ func cmdJoinTeam() {
|
||||
}
|
||||
}
|
||||
|
||||
func cmdLeaveTeam() {
|
||||
if flagCmdLeaveTeam {
|
||||
if len(flagTeamName) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(flagEmail) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var team *model.Team
|
||||
if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
|
||||
l4g.Error("%v", result.Err)
|
||||
flushLogAndExit(1)
|
||||
} else {
|
||||
team = result.Data.(*model.Team)
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
|
||||
l4g.Error("%v", result.Err)
|
||||
flushLogAndExit(1)
|
||||
} else {
|
||||
user = result.Data.(*model.User)
|
||||
}
|
||||
|
||||
err := api.LeaveTeam(team, user)
|
||||
|
||||
if err != nil {
|
||||
l4g.Error("%v", err)
|
||||
flushLogAndExit(1)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdResetPassword() {
|
||||
if flagCmdResetPassword {
|
||||
if len(flagEmail) == 0 {
|
||||
@@ -1517,7 +1562,12 @@ COMMANDS:
|
||||
Example:
|
||||
platform -invite_user -team_name="name" -email="user@example.com" -site_url="https://mattermost.example.com"
|
||||
|
||||
-join_team Joins a user to the team. It requires the -email and
|
||||
-leave_team Removes a user from a team. It requires the -team_name
|
||||
and -email.
|
||||
Example:
|
||||
platform -remove_user_from_team -team_name="name" -email="user@example.com"
|
||||
|
||||
-join_team Joins a user to the team. It required the -email and
|
||||
-team_name. You may need to logout of your current session
|
||||
for the new team to be applied.
|
||||
Example:
|
||||
|
||||
@@ -345,10 +345,18 @@ func (c *Client) FindTeamByName(name string) (*Result, *AppError) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) AddUserToTeam(userId string) (*Result, *AppError) {
|
||||
// Adds a user directly to the team without sending an invite.
|
||||
// The teamId and userId are required. You must be a valid member of the team and/or
|
||||
// have the correct role to add new users to the team. Returns a map of user_id=userId
|
||||
// if successful, otherwise returns an AppError.
|
||||
func (c *Client) AddUserToTeam(teamId string, userId string) (*Result, *AppError) {
|
||||
if len(teamId) == 0 {
|
||||
teamId = c.GetTeamId()
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = userId
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/add_user_to_team", MapToJson(data)); err != nil {
|
||||
if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v", teamId)+"/add_user_to_team", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
@@ -371,6 +379,26 @@ func (c *Client) AddUserToTeamFromInvite(hash, dataToHash, inviteId string) (*Re
|
||||
}
|
||||
}
|
||||
|
||||
// Removes a user directly from the team.
|
||||
// The teamId and userId are required. You must be a valid member of the team and/or
|
||||
// have the correct role to remove a user from the team. Returns a map of user_id=userId
|
||||
// if successful, otherwise returns an AppError.
|
||||
func (c *Client) RemoveUserFromTeam(teamId string, userId string) (*Result, *AppError) {
|
||||
if len(teamId) == 0 {
|
||||
teamId = c.GetTeamId()
|
||||
}
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = userId
|
||||
if r, err := c.DoApiPost(fmt.Sprintf("/teams/%v", teamId)+"/remove_user_from_team", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) InviteMembers(invites *Invites) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost(c.GetTeamRoute()+"/invite_members", invites.ToJson()); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -17,6 +17,7 @@ const (
|
||||
ACTION_CHANNEL_VIEWED = "channel_viewed"
|
||||
ACTION_DIRECT_ADDED = "direct_added"
|
||||
ACTION_NEW_USER = "new_user"
|
||||
ACTION_LEAVE_TEAM = "leave_team"
|
||||
ACTION_USER_ADDED = "user_added"
|
||||
ACTION_USER_REMOVED = "user_removed"
|
||||
ACTION_PREFERENCE_CHANGED = "preference_changed"
|
||||
|
||||
@@ -14,9 +14,10 @@ const (
|
||||
)
|
||||
|
||||
type TeamMember struct {
|
||||
TeamId string `json:"team_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
TeamId string `json:"team_id"`
|
||||
UserId string `json:"user_id"`
|
||||
Roles string `json:"roles"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
}
|
||||
|
||||
func (o *TeamMember) ToJson() string {
|
||||
|
||||
@@ -75,7 +75,13 @@ func (me SqlSessionStore) Save(session *model.Session) StoreChannel {
|
||||
result.Err = model.NewLocAppError("SqlSessionStore.Save", "store.sql_session.save.app_error", nil, "id="+session.Id+", "+rtcs.Err.Error())
|
||||
return
|
||||
} else {
|
||||
session.TeamMembers = rtcs.Data.([]*model.TeamMember)
|
||||
tempMembers := rtcs.Data.([]*model.TeamMember)
|
||||
session.TeamMembers = make([]*model.TeamMember, 0, len(tempMembers))
|
||||
for _, tm := range tempMembers {
|
||||
if tm.DeleteAt == 0 {
|
||||
session.TeamMembers = append(session.TeamMembers, tm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
@@ -106,7 +112,13 @@ func (me SqlSessionStore) Get(sessionIdOrToken string) StoreChannel {
|
||||
result.Err = model.NewLocAppError("SqlSessionStore.Get", "store.sql_session.get.app_error", nil, "sessionIdOrToken="+sessionIdOrToken+", "+rtcs.Err.Error())
|
||||
return
|
||||
} else {
|
||||
sessions[0].TeamMembers = rtcs.Data.([]*model.TeamMember)
|
||||
tempMembers := rtcs.Data.([]*model.TeamMember)
|
||||
sessions[0].TeamMembers = make([]*model.TeamMember, 0, len(tempMembers))
|
||||
for _, tm := range tempMembers {
|
||||
if tm.DeleteAt == 0 {
|
||||
sessions[0].TeamMembers = append(sessions[0].TeamMembers, tm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +156,13 @@ func (me SqlSessionStore) GetSessions(userId string) StoreChannel {
|
||||
return
|
||||
} else {
|
||||
for _, session := range sessions {
|
||||
session.TeamMembers = rtcs.Data.([]*model.TeamMember)
|
||||
tempMembers := rtcs.Data.([]*model.TeamMember)
|
||||
session.TeamMembers = make([]*model.TeamMember, 0, len(tempMembers))
|
||||
for _, tm := range tempMembers {
|
||||
if tm.DeleteAt == 0 {
|
||||
session.TeamMembers = append(session.TeamMembers, tm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) UpgradeSchemaIfNeeded() {
|
||||
s.CreateColumnIfNotExists("TeamMembers", "DeleteAt", "bigint(20)", "bigint", "0")
|
||||
}
|
||||
|
||||
func (s SqlTeamStore) CreateIndexesIfNotExists() {
|
||||
|
||||
@@ -288,6 +288,13 @@ export function showInviteMemberModal() {
|
||||
});
|
||||
}
|
||||
|
||||
export function showLeaveTeamModal() {
|
||||
AppDispatcher.handleViewAction({
|
||||
type: ActionTypes.TOGGLE_LEAVE_TEAM_MODAL,
|
||||
value: true
|
||||
});
|
||||
}
|
||||
|
||||
export function showRegisterAppModal() {
|
||||
AppDispatcher.handleViewAction({
|
||||
type: ActionTypes.TOGGLE_REGISTER_APP_MODAL,
|
||||
|
||||
@@ -135,6 +135,10 @@ function handleMessage(msg) {
|
||||
handleNewUserEvent();
|
||||
break;
|
||||
|
||||
case SocketEvents.LEAVE_TEAM:
|
||||
handleLeaveTeamEvent(msg);
|
||||
break;
|
||||
|
||||
case SocketEvents.USER_ADDED:
|
||||
handleUserAddedEvent(msg);
|
||||
break;
|
||||
@@ -219,6 +223,19 @@ function handleNewUserEvent() {
|
||||
AsyncClient.getChannelExtraInfo();
|
||||
}
|
||||
|
||||
function handleLeaveTeamEvent(msg) {
|
||||
if (UserStore.getCurrentId() === msg.user_id) {
|
||||
TeamStore.removeTeamMember(msg.team_id);
|
||||
|
||||
// if the are on the team begin removed redirect them to the root
|
||||
if (TeamStore.getCurrentId() === msg.team_id) {
|
||||
browserHistory.push('/');
|
||||
}
|
||||
} else if (TeamStore.getCurrentId() === msg.team_id) {
|
||||
GlobalActions.emitProfilesForDmList();
|
||||
}
|
||||
}
|
||||
|
||||
function handleDirectAddedEvent(msg) {
|
||||
AsyncClient.getChannel(msg.channel_id);
|
||||
AsyncClient.getDirectProfiles();
|
||||
|
||||
@@ -186,6 +186,10 @@ export default class UserList extends React.Component {
|
||||
var memberList = this.state.users.map((user) => {
|
||||
var teamMember = this.getTeamMemberForUser(user.id);
|
||||
|
||||
if (teamMember.delete_at > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<UserItem
|
||||
team={this.state.team}
|
||||
|
||||
@@ -18,6 +18,7 @@ export default class UserItem extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleMakeMember = this.handleMakeMember.bind(this);
|
||||
this.handleRemoveFromTeam = this.handleRemoveFromTeam.bind(this);
|
||||
this.handleMakeActive = this.handleMakeActive.bind(this);
|
||||
this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
|
||||
this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
|
||||
@@ -56,6 +57,19 @@ export default class UserItem extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleRemoveFromTeam() {
|
||||
Client.removeUserFromTeam(
|
||||
this.props.team.id,
|
||||
this.props.user.id,
|
||||
() => {
|
||||
this.props.refreshProfiles();
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
handleMakeActive(e) {
|
||||
e.preventDefault();
|
||||
Client.updateActive(this.props.user.id, true,
|
||||
@@ -222,6 +236,7 @@ export default class UserItem extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
const me = UserStore.getCurrentUser();
|
||||
const email = user.email;
|
||||
let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin';
|
||||
let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin';
|
||||
@@ -299,6 +314,24 @@ export default class UserItem extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let removeFromTeam = null;
|
||||
if (this.props.user.id !== me.id) {
|
||||
removeFromTeam = (
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={this.handleRemoveFromTeam}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='team_members_dropdown.leave_team'
|
||||
defaultMessage='Remove From Team'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
let makeActive = null;
|
||||
if (showMakeActive) {
|
||||
makeActive = (
|
||||
@@ -428,7 +461,6 @@ export default class UserItem extends React.Component {
|
||||
passwordReset = null;
|
||||
}
|
||||
|
||||
const me = UserStore.getCurrentUser();
|
||||
let makeDemoteModal = null;
|
||||
if (this.props.user.id === me.id) {
|
||||
const title = (
|
||||
@@ -511,6 +543,7 @@ export default class UserItem extends React.Component {
|
||||
className='dropdown-menu member-menu'
|
||||
role='menu'
|
||||
>
|
||||
{removeFromTeam}
|
||||
{makeAdmin}
|
||||
{makeMember}
|
||||
{makeActive}
|
||||
|
||||
@@ -39,17 +39,24 @@ class FilteredUserList extends React.Component {
|
||||
this.state = {
|
||||
filter: '',
|
||||
users: this.filterUsers(props.teamMembers, props.users),
|
||||
selected: 'team'
|
||||
selected: 'team',
|
||||
teamMembers: props.teamMembers
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
componentWillReceiveProps(nextProps) {
|
||||
// assume the user list is immutable
|
||||
if (this.props.users !== nextProps.users) {
|
||||
this.setState({
|
||||
users: this.filterUsers(nextProps.teamMembers, nextProps.users)
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.teamMembers !== nextProps.teamMembers) {
|
||||
this.setState({
|
||||
users: this.filterUsers(nextProps.teamMembers, nextProps.users)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -70,6 +77,10 @@ class FilteredUserList extends React.Component {
|
||||
var filteredUsers = users.filter((user) => {
|
||||
for (const index in teamMembers) {
|
||||
if (teamMembers.hasOwnProperty(index) && teamMembers[index].user_id === user.id) {
|
||||
if (teamMembers[index].delete_at > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
115
webapp/components/leave_team_modal.jsx
Normal file
115
webapp/components/leave_team_modal.jsx
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import Constants from 'utils/constants.jsx';
|
||||
const ActionTypes = Constants.ActionTypes;
|
||||
import * as GlobalActions from 'actions/global_actions.jsx';
|
||||
import ModalStore from 'stores/modal_store.jsx';
|
||||
import UserStore from 'stores/user_store.jsx';
|
||||
|
||||
import {intlShape, injectIntl, FormattedMessage} from 'react-intl';
|
||||
|
||||
import {Modal} from 'react-bootstrap';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
class LeaveTeamModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleToggle = this.handleToggle.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleHide = this.handleHide.bind(this);
|
||||
|
||||
this.state = {
|
||||
show: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
ModalStore.addModalListener(ActionTypes.TOGGLE_LEAVE_TEAM_MODAL, this.handleToggle);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
ModalStore.removeModalListener(ActionTypes.TOGGLE_LEAVE_TEAM_MODAL, this.handleToggle);
|
||||
}
|
||||
|
||||
handleToggle(value) {
|
||||
this.setState({
|
||||
show: value
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
GlobalActions.emitLeaveTeam();
|
||||
|
||||
this.setState({
|
||||
show: false
|
||||
});
|
||||
}
|
||||
|
||||
handleHide() {
|
||||
this.setState({
|
||||
show: false
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
var currentUser = UserStore.getCurrentUser();
|
||||
|
||||
if (currentUser != null) {
|
||||
return (
|
||||
<Modal
|
||||
className='modal-confirm'
|
||||
show={this.state.show}
|
||||
onHide={this.handleHide}
|
||||
>
|
||||
<Modal.Header closeButton={false}>
|
||||
<Modal.Title>
|
||||
<FormattedMessage
|
||||
id='leave_team_modal.title'
|
||||
defaultMessage='Leave the team?'
|
||||
/>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<FormattedMessage
|
||||
id='leave_team_modal.desc'
|
||||
defaultMessage='You will be removed from all public channels and private groups. If the team is private you will not be able to rejoin the team. Are you sure?'
|
||||
/>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-default'
|
||||
onClick={this.handleHide}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='leave_team_modal.no'
|
||||
defaultMessage='No'
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
type='button'
|
||||
className='btn btn-danger'
|
||||
onClick={this.handleSubmit}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='leave_team_modal.yes'
|
||||
defaultMessage='Yes'
|
||||
/>
|
||||
</button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
LeaveTeamModal.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(LeaveTeamModal);
|
||||
@@ -236,6 +236,20 @@ export default class NavbarDropdown extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
teams.push(
|
||||
<li key='leaveTeam_li'>
|
||||
<a
|
||||
href='#'
|
||||
onClick={GlobalActions.showLeaveTeamModal}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='navbar_dropdown.leave'
|
||||
defaultMessage='Leave Team'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
||||
if (this.state.teamMembers && this.state.teamMembers.length > 1) {
|
||||
teams.push(
|
||||
<li
|
||||
|
||||
@@ -34,6 +34,7 @@ import RemovedFromChannelModal from 'components/removed_from_channel_modal.jsx';
|
||||
import RegisterAppModal from 'components/register_app_modal.jsx';
|
||||
import ImportThemeModal from 'components/user_settings/import_theme_modal.jsx';
|
||||
import InviteMemberModal from 'components/invite_member_modal.jsx';
|
||||
import LeaveTeamModal from 'components/leave_team_modal.jsx';
|
||||
import SelectTeamModal from 'components/admin_console/select_team_modal.jsx';
|
||||
|
||||
export default class NeedsTeam extends React.Component {
|
||||
@@ -129,6 +130,7 @@ export default class NeedsTeam extends React.Component {
|
||||
<GetPublicLinkModal/>
|
||||
<GetTeamInviteLinkModal/>
|
||||
<InviteMemberModal/>
|
||||
<LeaveTeamModal/>
|
||||
<ImportThemeModal/>
|
||||
<TeamSettingsModal/>
|
||||
<MoreChannelsModal/>
|
||||
|
||||
@@ -19,6 +19,7 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
super(props);
|
||||
|
||||
this.handleMakeMember = this.handleMakeMember.bind(this);
|
||||
this.handleRemoveFromTeam = this.handleRemoveFromTeam.bind(this);
|
||||
this.handleMakeActive = this.handleMakeActive.bind(this);
|
||||
this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
|
||||
this.handleMakeAdmin = this.handleMakeAdmin.bind(this);
|
||||
@@ -52,6 +53,19 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
handleRemoveFromTeam() {
|
||||
Client.removeUserFromTeam(
|
||||
'',
|
||||
this.props.user.id,
|
||||
() => {
|
||||
AsyncClient.getTeamMembers(TeamStore.getCurrentId());
|
||||
AsyncClient.getProfiles();
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
handleMakeActive() {
|
||||
Client.updateActive(this.props.user.id, true,
|
||||
() => {
|
||||
@@ -171,6 +185,7 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
const me = UserStore.getCurrentUser();
|
||||
let showMakeMember = teamMember.roles === 'admin' || user.roles === 'system_admin';
|
||||
let showMakeAdmin = teamMember.roles === '' && user.roles !== 'system_admin';
|
||||
let showMakeActive = false;
|
||||
@@ -225,6 +240,24 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let removeFromTeam = null;
|
||||
if (this.props.user.id !== me.id) {
|
||||
removeFromTeam = (
|
||||
<li role='presentation'>
|
||||
<a
|
||||
role='menuitem'
|
||||
href='#'
|
||||
onClick={this.handleRemoveFromTeam}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='team_members_dropdown.leave_team'
|
||||
defaultMessage='Remove From Team'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
let makeActive = null;
|
||||
if (showMakeActive) {
|
||||
// makeActive = (
|
||||
@@ -260,7 +293,7 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
// </li>
|
||||
// );
|
||||
}
|
||||
const me = UserStore.getCurrentUser();
|
||||
|
||||
let makeDemoteModal = null;
|
||||
if (this.props.user.id === me.id) {
|
||||
const title = (
|
||||
@@ -321,6 +354,7 @@ export default class TeamMembersDropdown extends React.Component {
|
||||
className='dropdown-menu member-menu'
|
||||
role='menu'
|
||||
>
|
||||
{removeFromTeam}
|
||||
{makeAdmin}
|
||||
{makeMember}
|
||||
{makeActive}
|
||||
|
||||
@@ -1202,6 +1202,7 @@
|
||||
"navbar_dropdown.accountSettings": "Account Settings",
|
||||
"navbar_dropdown.console": "System Console",
|
||||
"navbar_dropdown.create": "Create a New Team",
|
||||
"navbar_dropdown.leave": "Leave Team",
|
||||
"navbar_dropdown.emoji": "Custom Emoji",
|
||||
"navbar_dropdown.help": "Help",
|
||||
"navbar_dropdown.integrations": "Integrations",
|
||||
@@ -1414,6 +1415,7 @@
|
||||
"team_members_dropdown.makeAdmin": "Make Team Admin",
|
||||
"team_members_dropdown.makeInactive": "Make Inactive",
|
||||
"team_members_dropdown.makeMember": "Make Member",
|
||||
"team_members_dropdown.leave_team": "Remove From Team",
|
||||
"team_members_dropdown.member": "Member",
|
||||
"team_members_dropdown.systemAdmin": "System Admin",
|
||||
"team_members_dropdown.teamAdmin": "Team Admin",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"keymirror": "0.1.1",
|
||||
"marked": "mattermost/marked#12d2be4cdf54d4ec95fead934e18840b6a2c1a7b",
|
||||
"match-at": "0.1.0",
|
||||
"mattermost": "mattermost/mattermost-javascript#18527e6c4a9aea69aa7845a62d9618b357faa4e7",
|
||||
"mattermost": "mattermost/mattermost-javascript#c72a75ca4ac135e2d476fc048ef7adc450e6739f",
|
||||
"object-assign": "4.1.0",
|
||||
"perfect-scrollbar": "0.6.11",
|
||||
"react": "15.0.2",
|
||||
|
||||
@@ -33,6 +33,7 @@ class ModalStoreClass extends EventEmitter {
|
||||
switch (type) {
|
||||
case ActionTypes.TOGGLE_IMPORT_THEME_MODAL:
|
||||
case ActionTypes.TOGGLE_INVITE_MEMBER_MODAL:
|
||||
case ActionTypes.TOGGLE_LEAVE_TEAM_MODAL:
|
||||
case ActionTypes.TOGGLE_DELETE_POST_MODAL:
|
||||
case ActionTypes.TOGGLE_GET_POST_LINK_MODAL:
|
||||
case ActionTypes.TOGGLE_GET_TEAM_INVITE_LINK_MODAL:
|
||||
|
||||
@@ -139,6 +139,16 @@ class TeamStoreClass extends EventEmitter {
|
||||
this.team_members.push(member);
|
||||
}
|
||||
|
||||
removeTeamMember(teamId) {
|
||||
for (var index in this.team_members) {
|
||||
if (this.team_members.hasOwnProperty(index)) {
|
||||
if (this.team_members[index].team_id === teamId) {
|
||||
Reflect.deleteProperty(this.team_members, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTeamMembers() {
|
||||
return this.team_members;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ export default {
|
||||
|
||||
TOGGLE_IMPORT_THEME_MODAL: null,
|
||||
TOGGLE_INVITE_MEMBER_MODAL: null,
|
||||
TOGGLE_LEAVE_TEAM_MODAL: null,
|
||||
TOGGLE_DELETE_POST_MODAL: null,
|
||||
TOGGLE_GET_POST_LINK_MODAL: null,
|
||||
TOGGLE_GET_TEAM_INVITE_LINK_MODAL: null,
|
||||
@@ -160,6 +161,7 @@ export default {
|
||||
CHANNEL_VIEWED: 'channel_viewed',
|
||||
DIRECT_ADDED: 'direct_added',
|
||||
NEW_USER: 'new_user',
|
||||
LEAVE_TEAM: 'leave_team',
|
||||
USER_ADDED: 'user_added',
|
||||
USER_REMOVED: 'user_removed',
|
||||
TYPING: 'typing',
|
||||
|
||||
Reference in New Issue
Block a user