summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/user.go37
-rw-r--r--api/user_test.go58
-rw-r--r--model/client.go6
-rw-r--r--model/user_search.go39
-rw-r--r--model/user_search_test.go19
-rw-r--r--store/sql_user_store.go79
-rw-r--r--store/sql_user_store_test.go90
-rw-r--r--store/store.go6
-rw-r--r--webapp/components/admin_console/team_users.jsx7
-rw-r--r--webapp/components/channel_invite_modal.jsx2
-rw-r--r--webapp/components/channel_members_modal.jsx2
-rw-r--r--webapp/components/more_direct_channels.jsx6
-rw-r--r--webapp/stores/user_store.jsx6
-rw-r--r--webapp/utils/constants.jsx5
14 files changed, 279 insertions, 83 deletions
diff --git a/api/user.go b/api/user.go
index 37f8d6818..2c00dd4c8 100644
--- a/api/user.go
+++ b/api/user.go
@@ -2592,33 +2592,36 @@ func sanitizeProfile(c *Context, user *model.User) *model.User {
}
func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) {
- props := model.MapFromJson(r.Body)
+ props := model.UserSearchFromJson(r.Body)
+ if props == nil {
+ c.SetInvalidParam("searchUsers", "")
+ return
+ }
- term := props["term"]
- if len(term) == 0 {
+ if len(props.Term) == 0 {
c.SetInvalidParam("searchUsers", "term")
return
}
- teamId := props["team_id"]
- inChannelId := props["in_channel"]
- notInChannelId := props["not_in_channel"]
-
- if inChannelId != "" && !HasPermissionToChannelContext(c, inChannelId, model.PERMISSION_READ_CHANNEL) {
+ if props.InChannelId != "" && !HasPermissionToChannelContext(c, props.InChannelId, model.PERMISSION_READ_CHANNEL) {
return
}
- if notInChannelId != "" && !HasPermissionToChannelContext(c, notInChannelId, model.PERMISSION_READ_CHANNEL) {
+ if props.NotInChannelId != "" && !HasPermissionToChannelContext(c, props.NotInChannelId, model.PERMISSION_READ_CHANNEL) {
return
}
+ searchOptions := map[string]bool{}
+ searchOptions[store.USER_SEARCH_OPTION_USERNAME_ONLY] = true
+ searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = props.AllowInactive
+
var uchan store.StoreChannel
- if inChannelId != "" {
- uchan = Srv.Store.User().SearchInChannel(inChannelId, term, store.USER_SEARCH_TYPE_USERNAME)
- } else if notInChannelId != "" {
- uchan = Srv.Store.User().SearchNotInChannel(teamId, notInChannelId, term, store.USER_SEARCH_TYPE_USERNAME)
+ if props.InChannelId != "" {
+ uchan = Srv.Store.User().SearchInChannel(props.InChannelId, props.Term, searchOptions)
+ } else if props.NotInChannelId != "" {
+ uchan = Srv.Store.User().SearchNotInChannel(props.TeamId, props.NotInChannelId, props.Term, searchOptions)
} else {
- uchan = Srv.Store.User().Search(teamId, term, store.USER_SEARCH_TYPE_USERNAME)
+ uchan = Srv.Store.User().Search(props.TeamId, props.Term, searchOptions)
}
if result := <-uchan; result.Err != nil {
@@ -2674,8 +2677,8 @@ func autocompleteUsersInChannel(c *Context, w http.ResponseWriter, r *http.Reque
return
}
- uchan := Srv.Store.User().SearchInChannel(channelId, term, store.USER_SEARCH_TYPE_ALL)
- nuchan := Srv.Store.User().SearchNotInChannel(teamId, channelId, term, store.USER_SEARCH_TYPE_ALL)
+ uchan := Srv.Store.User().SearchInChannel(channelId, term, map[string]bool{})
+ nuchan := Srv.Store.User().SearchNotInChannel(teamId, channelId, term, map[string]bool{})
autocomplete := &model.UserAutocompleteInChannel{}
@@ -2720,7 +2723,7 @@ func autocompleteUsersInTeam(c *Context, w http.ResponseWriter, r *http.Request)
}
}
- uchan := Srv.Store.User().Search(teamId, term, store.USER_SEARCH_TYPE_ALL)
+ uchan := Srv.Store.User().Search(teamId, term, map[string]bool{})
autocomplete := &model.UserAutocompleteInTeam{}
diff --git a/api/user_test.go b/api/user_test.go
index 2c6238c54..75e246ab3 100644
--- a/api/user_test.go
+++ b/api/user_test.go
@@ -2034,10 +2034,14 @@ func TestGetProfilesNotInChannel(t *testing.T) {
}
func TestSearchUsers(t *testing.T) {
- th := Setup().InitBasic()
+ th := Setup().InitBasic().InitSystemAdmin()
Client := th.BasicClient
- if result, err := Client.SearchUsers(th.BasicUser.Username, "", map[string]string{}); err != nil {
+ inactiveUser := th.CreateUser(Client)
+ LinkUserToTeam(inactiveUser, th.BasicTeam)
+ th.SystemAdminClient.Must(th.SystemAdminClient.UpdateActive(inactiveUser.Id, false))
+
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username}); err != nil {
t.Fatal(err)
} else {
users := result.Data.([]*model.User)
@@ -2054,7 +2058,41 @@ func TestSearchUsers(t *testing.T) {
}
}
- if result, err := Client.SearchUsers(th.BasicUser.Username, "", map[string]string{"in_channel": th.BasicChannel.Id}); err != nil {
+ if result, err := Client.SearchUsers(model.UserSearch{Term: inactiveUser.Username, TeamId: th.BasicTeam.Id}); err != nil {
+ t.Fatal(err)
+ } else {
+ users := result.Data.([]*model.User)
+
+ found := false
+ for _, user := range users {
+ if user.Id == inactiveUser.Id {
+ found = true
+ }
+ }
+
+ if found {
+ t.Fatal("should not have found inactive user")
+ }
+ }
+
+ if result, err := Client.SearchUsers(model.UserSearch{Term: inactiveUser.Username, TeamId: th.BasicTeam.Id, AllowInactive: true}); err != nil {
+ t.Fatal(err)
+ } else {
+ users := result.Data.([]*model.User)
+
+ found := false
+ for _, user := range users {
+ if user.Id == inactiveUser.Id {
+ found = true
+ }
+ }
+
+ if !found {
+ t.Fatal("should have found inactive user")
+ }
+ }
+
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username, InChannelId: th.BasicChannel.Id}); err != nil {
t.Fatal(err)
} else {
users := result.Data.([]*model.User)
@@ -2075,7 +2113,7 @@ func TestSearchUsers(t *testing.T) {
}
}
- if result, err := Client.SearchUsers(th.BasicUser2.Username, "", map[string]string{"not_in_channel": th.BasicChannel.Id}); err != nil {
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser2.Username, NotInChannelId: th.BasicChannel.Id}); err != nil {
t.Fatal(err)
} else {
users := result.Data.([]*model.User)
@@ -2102,7 +2140,7 @@ func TestSearchUsers(t *testing.T) {
}
}
- if result, err := Client.SearchUsers(th.BasicUser2.Username, th.BasicTeam.Id, map[string]string{"not_in_channel": th.BasicChannel.Id}); err != nil {
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser2.Username, TeamId: th.BasicTeam.Id, NotInChannelId: th.BasicChannel.Id}); err != nil {
t.Fatal(err)
} else {
users := result.Data.([]*model.User)
@@ -2129,7 +2167,7 @@ func TestSearchUsers(t *testing.T) {
}
}
- if result, err := Client.SearchUsers(th.BasicUser.Username, "junk", map[string]string{"not_in_channel": th.BasicChannel.Id}); err != nil {
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username, TeamId: "junk", NotInChannelId: th.BasicChannel.Id}); err != nil {
t.Fatal(err)
} else {
users := result.Data.([]*model.User)
@@ -2141,7 +2179,7 @@ func TestSearchUsers(t *testing.T) {
th.LoginBasic2()
- if result, err := Client.SearchUsers(th.BasicUser.Username, "", map[string]string{}); err != nil {
+ if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username}); err != nil {
t.Fatal(err)
} else {
users := result.Data.([]*model.User)
@@ -2158,15 +2196,15 @@ func TestSearchUsers(t *testing.T) {
}
}
- if _, err := Client.SearchUsers("", "", map[string]string{}); err == nil {
+ if _, err := Client.SearchUsers(model.UserSearch{}); err == nil {
t.Fatal("should have errored - blank term")
}
- if _, err := Client.SearchUsers(th.BasicUser.Username, "", map[string]string{"in_channel": th.BasicChannel.Id}); err == nil {
+ if _, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username, InChannelId: th.BasicChannel.Id}); err == nil {
t.Fatal("should not have access")
}
- if _, err := Client.SearchUsers(th.BasicUser.Username, "", map[string]string{"not_in_channel": th.BasicChannel.Id}); err == nil {
+ if _, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username, NotInChannelId: th.BasicChannel.Id}); err == nil {
t.Fatal("should not have access")
}
}
diff --git a/model/client.go b/model/client.go
index e9d6c512c..02c6ac9b2 100644
--- a/model/client.go
+++ b/model/client.go
@@ -572,10 +572,8 @@ func (c *Client) GetProfilesByIds(userIds []string) (*Result, *AppError) {
// SearchUsers returns a list of users that have a username matching or similar to the search term. Must
// be authenticated.
-func (c *Client) SearchUsers(term string, teamId string, options map[string]string) (*Result, *AppError) {
- options["term"] = term
- options["team_id"] = teamId
- if r, err := c.DoApiPost("/users/search", MapToJson(options)); err != nil {
+func (c *Client) SearchUsers(params UserSearch) (*Result, *AppError) {
+ if r, err := c.DoApiPost("/users/search", params.ToJson()); err != nil {
return nil, err
} else {
defer closeBody(r)
diff --git a/model/user_search.go b/model/user_search.go
new file mode 100644
index 000000000..4bbd2bd78
--- /dev/null
+++ b/model/user_search.go
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "encoding/json"
+ "io"
+)
+
+type UserSearch struct {
+ Term string `json:"term"`
+ TeamId string `json:"team_id"`
+ InChannelId string `json:"in_channel_id"`
+ NotInChannelId string `json:"not_in_channel_id"`
+ AllowInactive bool `json:"allow_inactive"`
+}
+
+// ToJson convert a User to a json string
+func (u *UserSearch) ToJson() string {
+ b, err := json.Marshal(u)
+ if err != nil {
+ return ""
+ } else {
+ return string(b)
+ }
+}
+
+// UserSearchFromJson will decode the input and return a User
+func UserSearchFromJson(data io.Reader) *UserSearch {
+ decoder := json.NewDecoder(data)
+ var us UserSearch
+ err := decoder.Decode(&us)
+ if err == nil {
+ return &us
+ } else {
+ return nil
+ }
+}
diff --git a/model/user_search_test.go b/model/user_search_test.go
new file mode 100644
index 000000000..b2543ffdb
--- /dev/null
+++ b/model/user_search_test.go
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package model
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestUserSearchJson(t *testing.T) {
+ userSearch := UserSearch{Term: NewId(), TeamId: NewId()}
+ json := userSearch.ToJson()
+ ruserSearch := UserSearchFromJson(strings.NewReader(json))
+
+ if userSearch.Term != ruserSearch.Term {
+ t.Fatal("Terms do not match")
+ }
+}
diff --git a/store/sql_user_store.go b/store/sql_user_store.go
index 4f5e11d00..17fdcbc85 100644
--- a/store/sql_user_store.go
+++ b/store/sql_user_store.go
@@ -15,12 +15,14 @@ import (
)
const (
- MISSING_ACCOUNT_ERROR = "store.sql_user.missing_account.const"
- MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error"
- PROFILES_IN_CHANNEL_CACHE_SIZE = 5000
- PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
- USER_SEARCH_TYPE_ALL = "Username, FirstName, LastName, Nickname"
- USER_SEARCH_TYPE_USERNAME = "Username"
+ MISSING_ACCOUNT_ERROR = "store.sql_user.missing_account.const"
+ MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error"
+ PROFILES_IN_CHANNEL_CACHE_SIZE = 5000
+ PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
+ USER_SEARCH_OPTION_USERNAME_ONLY = "username_only"
+ USER_SEARCH_OPTION_ALLOW_INACTIVE = "allow_inactive"
+ USER_SEARCH_TYPE_ALL = "Username, FirstName, LastName, Nickname"
+ USER_SEARCH_TYPE_USERNAME = "Username"
)
type SqlUserStore struct {
@@ -1085,20 +1087,24 @@ func (us SqlUserStore) GetUnreadCountForChannel(userId string, channelId string)
return storeChannel
}
-func (us SqlUserStore) Search(teamId string, term string, searchType string) StoreChannel {
+func (us SqlUserStore) Search(teamId string, term string, options map[string]bool) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
searchQuery := ""
+
if teamId == "" {
+
+ // Id != '' is added because both SEARCH_CLAUSE and INACTIVE_CLAUSE start with an AND
searchQuery = `
SELECT
*
FROM
Users
WHERE
- DeleteAt = 0
+ Id != ''
SEARCH_CLAUSE
+ INACTIVE_CLAUSE
ORDER BY Username ASC
LIMIT 50`
} else {
@@ -1110,14 +1116,14 @@ func (us SqlUserStore) Search(teamId string, term string, searchType string) Sto
WHERE
TeamMembers.TeamId = :TeamId
AND Users.Id = TeamMembers.UserId
- AND Users.DeleteAt = 0
AND TeamMembers.DeleteAt = 0
SEARCH_CLAUSE
+ INACTIVE_CLAUSE
ORDER BY Users.Username ASC
LIMIT 100`
}
- storeChannel <- us.performSearch(searchQuery, term, searchType, map[string]interface{}{"TeamId": teamId})
+ storeChannel <- us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId})
close(storeChannel)
}()
@@ -1125,7 +1131,7 @@ func (us SqlUserStore) Search(teamId string, term string, searchType string) Sto
return storeChannel
}
-func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, searchType string) StoreChannel {
+func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
@@ -1133,34 +1139,38 @@ func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term
if teamId == "" {
searchQuery = `
SELECT
- u.*
- FROM Users u
+ Users.*
+ FROM Users
LEFT JOIN ChannelMembers cm
- ON cm.UserId = u.Id
+ ON cm.UserId = Users.Id
AND cm.ChannelId = :ChannelId
- WHERE cm.UserId IS NULL
- SEARCH_CLAUSE
- ORDER BY u.Username ASC
+ WHERE
+ cm.UserId IS NULL
+ SEARCH_CLAUSE
+ INACTIVE_CLAUSE
+ ORDER BY Users.Username ASC
LIMIT 100`
} else {
searchQuery = `
SELECT
- u.*
- FROM Users u
+ Users.*
+ FROM Users
INNER JOIN TeamMembers tm
- ON tm.UserId = u.Id
+ ON tm.UserId = Users.Id
AND tm.TeamId = :TeamId
AND tm.DeleteAt = 0
LEFT JOIN ChannelMembers cm
- ON cm.UserId = u.Id
+ ON cm.UserId = Users.Id
AND cm.ChannelId = :ChannelId
- WHERE cm.UserId IS NULL
- SEARCH_CLAUSE
- ORDER BY u.Username ASC
+ WHERE
+ cm.UserId IS NULL
+ SEARCH_CLAUSE
+ INACTIVE_CLAUSE
+ ORDER BY Users.Username ASC
LIMIT 100`
}
- storeChannel <- us.performSearch(searchQuery, term, searchType, map[string]interface{}{"TeamId": teamId, "ChannelId": channelId})
+ storeChannel <- us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId, "ChannelId": channelId})
close(storeChannel)
}()
@@ -1168,7 +1178,7 @@ func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term
return storeChannel
}
-func (us SqlUserStore) SearchInChannel(channelId string, term string, searchType string) StoreChannel {
+func (us SqlUserStore) SearchInChannel(channelId string, term string, options map[string]bool) StoreChannel {
storeChannel := make(StoreChannel, 1)
go func() {
@@ -1180,12 +1190,12 @@ func (us SqlUserStore) SearchInChannel(channelId string, term string, searchType
WHERE
ChannelMembers.ChannelId = :ChannelId
AND ChannelMembers.UserId = Users.Id
- AND Users.DeleteAt = 0
SEARCH_CLAUSE
+ INACTIVE_CLAUSE
ORDER BY Users.Username ASC
LIMIT 100`
- storeChannel <- us.performSearch(searchQuery, term, searchType, map[string]interface{}{"ChannelId": channelId})
+ storeChannel <- us.performSearch(searchQuery, term, options, map[string]interface{}{"ChannelId": channelId})
close(storeChannel)
}()
@@ -1193,7 +1203,7 @@ func (us SqlUserStore) SearchInChannel(channelId string, term string, searchType
return storeChannel
}
-func (us SqlUserStore) performSearch(searchQuery string, term string, searchType string, parameters map[string]interface{}) StoreResult {
+func (us SqlUserStore) performSearch(searchQuery string, term string, options map[string]bool, parameters map[string]interface{}) StoreResult {
result := StoreResult{}
// these chars have special meaning and can be treated as spaces
@@ -1201,6 +1211,17 @@ func (us SqlUserStore) performSearch(searchQuery string, term string, searchType
term = strings.Replace(term, c, " ", -1)
}
+ searchType := USER_SEARCH_TYPE_ALL
+ if ok := options[USER_SEARCH_OPTION_USERNAME_ONLY]; ok {
+ searchType = USER_SEARCH_TYPE_USERNAME
+ }
+
+ if ok := options[USER_SEARCH_OPTION_ALLOW_INACTIVE]; ok {
+ searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "", 1)
+ } else {
+ searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "AND Users.DeleteAt = 0", 1)
+ }
+
if term == "" {
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
} else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go
index 23c124cb7..bc7cc69c5 100644
--- a/store/sql_user_store_test.go
+++ b/store/sql_user_store_test.go
@@ -942,11 +942,75 @@ func TestUserStoreSearch(t *testing.T) {
u2.Email = model.NewId()
Must(store.User().Save(u2))
+ u3 := &model.User{}
+ u3.Username = "jimbo" + model.NewId()
+ u3.Email = model.NewId()
+ u3.DeleteAt = 1
+ Must(store.User().Save(u3))
+
tid := model.NewId()
Must(store.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u1.Id}))
Must(store.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u2.Id}))
+ Must(store.Team().SaveMember(&model.TeamMember{TeamId: tid, UserId: u3.Id}))
+
+ searchOptions := map[string]bool{}
+ searchOptions[USER_SEARCH_OPTION_USERNAME_ONLY] = true
+
+ if r1 := <-store.User().Search(tid, "jimb", searchOptions); r1.Err != nil {
+ t.Fatal(r1.Err)
+ } else {
+ profiles := r1.Data.([]*model.User)
+ found1 := false
+ found2 := false
+ for _, profile := range profiles {
+ if profile.Id == u1.Id {
+ found1 = true
+ }
+
+ if profile.Id == u3.Id {
+ found2 = true
+ }
+ }
+
+ if !found1 {
+ t.Fatal("should have found user")
+ }
+
+ if found2 {
+ t.Fatal("should not have found inactive user")
+ }
+ }
- if r1 := <-store.User().Search(tid, "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ searchOptions[USER_SEARCH_OPTION_ALLOW_INACTIVE] = true
+
+ if r1 := <-store.User().Search(tid, "jimb", searchOptions); r1.Err != nil {
+ t.Fatal(r1.Err)
+ } else {
+ profiles := r1.Data.([]*model.User)
+ found1 := false
+ found2 := false
+ for _, profile := range profiles {
+ if profile.Id == u1.Id {
+ found1 = true
+ }
+
+ if profile.Id == u3.Id {
+ found2 = true
+ }
+ }
+
+ if !found1 {
+ t.Fatal("should have found user")
+ }
+
+ if !found2 {
+ t.Fatal("should have found inactive user")
+ }
+ }
+
+ searchOptions[USER_SEARCH_OPTION_ALLOW_INACTIVE] = false
+
+ if r1 := <-store.User().Search(tid, "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -963,7 +1027,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().Search("", "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().Search("", "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -980,7 +1044,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().Search("", "jim-bobb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().Search("", "jim-bobb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -998,7 +1062,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().Search(tid, "", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().Search(tid, "", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
}
@@ -1009,7 +1073,7 @@ func TestUserStoreSearch(t *testing.T) {
c1.Type = model.CHANNEL_OPEN
c1 = *Must(store.Channel().Save(&c1)).(*model.Channel)
- if r1 := <-store.User().SearchNotInChannel(tid, c1.Id, "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().SearchNotInChannel(tid, c1.Id, "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1026,7 +1090,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().SearchNotInChannel("", c1.Id, "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().SearchNotInChannel("", c1.Id, "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1043,7 +1107,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().SearchNotInChannel("junk", c1.Id, "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().SearchNotInChannel("junk", c1.Id, "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1060,7 +1124,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().SearchInChannel(c1.Id, "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().SearchInChannel(c1.Id, "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1079,7 +1143,7 @@ func TestUserStoreSearch(t *testing.T) {
Must(store.Channel().SaveMember(&model.ChannelMember{ChannelId: c1.Id, UserId: u1.Id, NotifyProps: model.GetDefaultChannelNotifyProps()}))
- if r1 := <-store.User().SearchInChannel(c1.Id, "jimb", USER_SEARCH_TYPE_USERNAME); r1.Err != nil {
+ if r1 := <-store.User().SearchInChannel(c1.Id, "jimb", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1096,7 +1160,9 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().Search(tid, "Tim", USER_SEARCH_TYPE_ALL); r1.Err != nil {
+ searchOptions = map[string]bool{}
+
+ if r1 := <-store.User().Search(tid, "Tim", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1113,7 +1179,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().Search(tid, "Bill", USER_SEARCH_TYPE_ALL); r1.Err != nil {
+ if r1 := <-store.User().Search(tid, "Bill", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
@@ -1130,7 +1196,7 @@ func TestUserStoreSearch(t *testing.T) {
}
}
- if r1 := <-store.User().Search(tid, "Rob", USER_SEARCH_TYPE_ALL); r1.Err != nil {
+ if r1 := <-store.User().Search(tid, "Rob", searchOptions); r1.Err != nil {
t.Fatal(r1.Err)
} else {
profiles := r1.Data.([]*model.User)
diff --git a/store/store.go b/store/store.go
index 6cf216699..85a1ad398 100644
--- a/store/store.go
+++ b/store/store.go
@@ -164,9 +164,9 @@ type UserStore interface {
GetUnreadCount(userId string) StoreChannel
GetUnreadCountForChannel(userId string, channelId string) StoreChannel
GetRecentlyActiveUsersForTeam(teamId string) StoreChannel
- Search(teamId string, term string, searchType string) StoreChannel
- SearchInChannel(channelId string, term string, searchType string) StoreChannel
- SearchNotInChannel(teamId string, channelId string, term string, searchType string) StoreChannel
+ Search(teamId string, term string, options map[string]bool) StoreChannel
+ SearchInChannel(channelId string, term string, options map[string]bool) StoreChannel
+ SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) StoreChannel
}
type SessionStore interface {
diff --git a/webapp/components/admin_console/team_users.jsx b/webapp/components/admin_console/team_users.jsx
index 1d0886737..3efb242ed 100644
--- a/webapp/components/admin_console/team_users.jsx
+++ b/webapp/components/admin_console/team_users.jsx
@@ -13,7 +13,7 @@ import UserStore from 'stores/user_store.jsx';
import {searchUsers, loadProfilesAndTeamMembers, loadTeamMembersForProfilesList} from 'actions/user_actions.jsx';
import {getTeamStats, getUser} from 'utils/async_client.jsx';
-import Constants from 'utils/constants.jsx';
+import {Constants, UserSearchOptions} from 'utils/constants.jsx';
import * as Utils from 'utils/utils.jsx';
import React from 'react';
@@ -145,10 +145,13 @@ export default class UserList extends React.Component {
return;
}
+ const options = {};
+ options[UserSearchOptions.ALLOW_INACTIVE] = true;
+
searchUsers(
term,
this.props.params.team,
- {},
+ options,
(users) => {
this.setState({loading: true, search: true, users});
loadTeamMembersForProfilesList(users, this.props.params.team, this.loadComplete);
diff --git a/webapp/components/channel_invite_modal.jsx b/webapp/components/channel_invite_modal.jsx
index 14e02e04b..7f6ca4d32 100644
--- a/webapp/components/channel_invite_modal.jsx
+++ b/webapp/components/channel_invite_modal.jsx
@@ -105,7 +105,7 @@ export default class ChannelInviteModal extends React.Component {
searchUsers(
term,
TeamStore.getCurrentId(),
- {not_in_channel: this.props.channel.id},
+ {not_in_channel_id: this.props.channel.id},
(users) => {
this.setState({search: true, users});
}
diff --git a/webapp/components/channel_members_modal.jsx b/webapp/components/channel_members_modal.jsx
index 286a2243a..76ce535ad 100644
--- a/webapp/components/channel_members_modal.jsx
+++ b/webapp/components/channel_members_modal.jsx
@@ -117,7 +117,7 @@ export default class ChannelMembersModal extends React.Component {
searchUsers(
term,
TeamStore.getCurrentId(),
- {in_channel: this.props.channel.id},
+ {in_channel_id: this.props.channel.id},
(users) => {
this.setState({search: true, users});
}
diff --git a/webapp/components/more_direct_channels.jsx b/webapp/components/more_direct_channels.jsx
index 52e546cd2..7e57261b6 100644
--- a/webapp/components/more_direct_channels.jsx
+++ b/webapp/components/more_direct_channels.jsx
@@ -37,7 +37,7 @@ export default class MoreDirectChannels extends React.Component {
this.loadComplete = this.loadComplete.bind(this);
this.state = {
- users: UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true),
+ users: UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true, true),
loadingDMChannel: -1,
listType: 'team',
loading: false,
@@ -111,7 +111,7 @@ export default class MoreDirectChannels extends React.Component {
if (this.state.listType === 'any') {
users = UserStore.getProfileList();
} else {
- users = UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true);
+ users = UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true, true);
}
this.setState({
@@ -125,7 +125,7 @@ export default class MoreDirectChannels extends React.Component {
if (listType === 'any') {
users = UserStore.getProfileList();
} else {
- users = UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true);
+ users = UserStore.getProfileListInTeam(TeamStore.getCurrentId(), true, true);
}
this.setState({
diff --git a/webapp/stores/user_store.jsx b/webapp/stores/user_store.jsx
index 9ac8540f9..bb3415a7d 100644
--- a/webapp/stores/user_store.jsx
+++ b/webapp/stores/user_store.jsx
@@ -312,7 +312,7 @@ class UserStoreClass extends EventEmitter {
this.saveProfiles(profiles);
}
- getProfileListInTeam(teamId = TeamStore.getCurrentId(), skipCurrent) {
+ getProfileListInTeam(teamId = TeamStore.getCurrentId(), skipCurrent = false, skipInactive = false) {
const userIds = this.profiles_in_team[teamId] || [];
const profiles = [];
const currentId = this.getCurrentId();
@@ -324,6 +324,10 @@ class UserStoreClass extends EventEmitter {
continue;
}
+ if (skipInactive && profile.delete_at > 0) {
+ continue;
+ }
+
if (profile) {
profiles.push(profile);
}
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index beaca3921..352401142 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -181,6 +181,10 @@ export const UserStatuses = {
ONLINE: 'online'
};
+export const UserSearchOptions = {
+ ALLOW_INACTIVE: 'allow_inactive'
+};
+
export const SocketEvents = {
POSTED: 'posted',
POST_EDITED: 'post_edited',
@@ -214,6 +218,7 @@ export const Constants = {
ActionTypes,
WebrtcActionTypes,
UserStatuses,
+ UserSearchOptions,
TutorialSteps,
PayloadSources: keyMirror({