MM-12234: configurable limit to user autocomplete and search matches (#9499)
* unit test cleanup * allow limiting user search results * clean up test users before starting * model UserSearchOptions to simplify parameters
Этот коммит содержится в:
коммит произвёл
Harrison Healey
родитель
e8c9ccaa7e
Коммит
715097cc76
@@ -184,8 +184,11 @@ func (me *TestHelper) TearDown() {
|
|||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
options := map[string]bool{}
|
options := &model.UserSearchOptions{
|
||||||
options[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
|
AllowEmails: false,
|
||||||
|
AllowFullNames: false,
|
||||||
|
Limit: model.USER_SEARCH_MAX_LIMIT,
|
||||||
|
}
|
||||||
if result := <-me.App.Srv.Store.User().Search("", "fakeuser", options); result.Err != nil {
|
if result := <-me.App.Srv.Store.User().Search("", "fakeuser", options); result.Err != nil {
|
||||||
mlog.Error("Error tearing down test users")
|
mlog.Error("Error tearing down test users")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
61
api4/user.go
61
api4/user.go
@@ -14,7 +14,6 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/app"
|
"github.com/mattermost/mattermost-server/app"
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/store"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (api *API) InitUser() {
|
func (api *API) InitUser() {
|
||||||
@@ -547,23 +546,26 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
searchOptions := map[string]bool{}
|
if props.Limit <= 0 || props.Limit > model.USER_SEARCH_MAX_LIMIT {
|
||||||
searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = props.AllowInactive
|
c.SetInvalidParam("limit")
|
||||||
|
return
|
||||||
if !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
|
||||||
hideFullName := !c.App.Config().PrivacySettings.ShowFullName
|
|
||||||
hideEmail := !c.App.Config().PrivacySettings.ShowEmailAddress
|
|
||||||
|
|
||||||
if hideFullName && hideEmail {
|
|
||||||
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
|
|
||||||
} else if hideFullName {
|
|
||||||
searchOptions[store.USER_SEARCH_OPTION_ALL_NO_FULL_NAME] = true
|
|
||||||
} else if hideEmail {
|
|
||||||
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
profiles, err := c.App.SearchUsers(props, searchOptions, c.IsSystemAdmin())
|
options := &model.UserSearchOptions{
|
||||||
|
IsAdmin: c.IsSystemAdmin(),
|
||||||
|
AllowInactive: props.AllowInactive,
|
||||||
|
Limit: props.Limit,
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
options.AllowEmails = true
|
||||||
|
options.AllowFullNames = true
|
||||||
|
} else {
|
||||||
|
options.AllowEmails = c.App.Config().PrivacySettings.ShowEmailAddress
|
||||||
|
options.AllowFullNames = c.App.Config().PrivacySettings.ShowFullName
|
||||||
|
}
|
||||||
|
|
||||||
|
profiles, err := c.App.SearchUsers(props, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -576,17 +578,26 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
channelId := r.URL.Query().Get("in_channel")
|
channelId := r.URL.Query().Get("in_channel")
|
||||||
teamId := r.URL.Query().Get("in_team")
|
teamId := r.URL.Query().Get("in_team")
|
||||||
name := r.URL.Query().Get("name")
|
name := r.URL.Query().Get("name")
|
||||||
|
limitStr := r.URL.Query().Get("limit")
|
||||||
|
limit, _ := strconv.Atoi(limitStr)
|
||||||
|
if limitStr == "" {
|
||||||
|
limit = model.USER_SEARCH_DEFAULT_LIMIT
|
||||||
|
}
|
||||||
|
|
||||||
autocomplete := new(model.UserAutocomplete)
|
autocomplete := new(model.UserAutocomplete)
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
|
|
||||||
searchOptions := map[string]bool{}
|
options := &model.UserSearchOptions{
|
||||||
|
IsAdmin: c.IsSystemAdmin(),
|
||||||
|
// Never autocomplete on emails.
|
||||||
|
AllowEmails: false,
|
||||||
|
Limit: limit,
|
||||||
|
}
|
||||||
|
|
||||||
hideFullName := !c.App.Config().PrivacySettings.ShowFullName
|
if c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
if hideFullName && !c.App.SessionHasPermissionTo(c.Session, model.PERMISSION_MANAGE_SYSTEM) {
|
options.AllowFullNames = true
|
||||||
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
|
|
||||||
} else {
|
} else {
|
||||||
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
options.AllowFullNames = c.App.Config().PrivacySettings.ShowFullName
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(channelId) > 0 {
|
if len(channelId) > 0 {
|
||||||
@@ -606,8 +617,8 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
if len(channelId) > 0 {
|
if len(channelId) > 0 {
|
||||||
// Applying the provided teamId here is useful for DMs and GMs which don't belong
|
// Applying the provided teamId here is useful for DMs and GMs which don't belong
|
||||||
// to a team. Applying it when the channel does belong to a team makes less sense,
|
// to a team. Applying it when the channel does belong to a team makes less sense,
|
||||||
//t but the permissions are checked above regardless.
|
// but the permissions are checked above regardless.
|
||||||
result, err := c.App.AutocompleteUsersInChannel(teamId, channelId, name, searchOptions, c.IsSystemAdmin())
|
result, err := c.App.AutocompleteUsersInChannel(teamId, channelId, name, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -616,7 +627,7 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
autocomplete.Users = result.InChannel
|
autocomplete.Users = result.InChannel
|
||||||
autocomplete.OutOfChannel = result.OutOfChannel
|
autocomplete.OutOfChannel = result.OutOfChannel
|
||||||
} else if len(teamId) > 0 {
|
} else if len(teamId) > 0 {
|
||||||
result, err := c.App.AutocompleteUsersInTeam(teamId, name, searchOptions, c.IsSystemAdmin())
|
result, err := c.App.AutocompleteUsersInTeam(teamId, name, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -625,7 +636,7 @@ func autocompleteUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
autocomplete.Users = result.InTeam
|
autocomplete.Users = result.InTeam
|
||||||
} else {
|
} else {
|
||||||
// No permission check required
|
// No permission check required
|
||||||
result, err := c.App.SearchUsersInTeam("", name, searchOptions, c.IsSystemAdmin())
|
result, err := c.App.SearchUsersInTeam("", name, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -759,92 +759,92 @@ func TestAutocompleteUsers(t *testing.T) {
|
|||||||
th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowFullName = showFullName })
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.PrivacySettings.ShowFullName = showFullName })
|
||||||
}()
|
}()
|
||||||
|
|
||||||
rusers, resp := Client.AutocompleteUsersInChannel(teamId, channelId, username, "")
|
rusers, resp := Client.AutocompleteUsersInChannel(teamId, channelId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if len(rusers.Users) != 1 {
|
if len(rusers.Users) != 1 {
|
||||||
t.Fatal("should have returned 1 user")
|
t.Fatal("should have returned 1 user")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInChannel(teamId, channelId, "amazonses", "")
|
rusers, resp = Client.AutocompleteUsersInChannel(teamId, channelId, "amazonses", model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
if len(rusers.Users) != 0 {
|
if len(rusers.Users) != 0 {
|
||||||
t.Fatal("should have returned 0 users")
|
t.Fatal("should have returned 0 users")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInChannel(teamId, channelId, "", "")
|
rusers, resp = Client.AutocompleteUsersInChannel(teamId, channelId, "", model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
if len(rusers.Users) < 2 {
|
if len(rusers.Users) < 2 {
|
||||||
t.Fatal("should have many users")
|
t.Fatal("should have many users")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInChannel("", channelId, "", "")
|
rusers, resp = Client.AutocompleteUsersInChannel("", channelId, "", model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
if len(rusers.Users) < 2 {
|
if len(rusers.Users) < 2 {
|
||||||
t.Fatal("should have many users")
|
t.Fatal("should have many users")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInTeam(teamId, username, "")
|
rusers, resp = Client.AutocompleteUsersInTeam(teamId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if len(rusers.Users) != 1 {
|
if len(rusers.Users) != 1 {
|
||||||
t.Fatal("should have returned 1 user")
|
t.Fatal("should have returned 1 user")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsers(username, "")
|
rusers, resp = Client.AutocompleteUsers(username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if len(rusers.Users) != 1 {
|
if len(rusers.Users) != 1 {
|
||||||
t.Fatal("should have returned 1 users")
|
t.Fatal("should have returned 1 users")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsers("", "")
|
rusers, resp = Client.AutocompleteUsers("", model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if len(rusers.Users) < 2 {
|
if len(rusers.Users) < 2 {
|
||||||
t.Fatal("should have returned many users")
|
t.Fatal("should have returned many users")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInTeam(teamId, "amazonses", "")
|
rusers, resp = Client.AutocompleteUsersInTeam(teamId, "amazonses", model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
if len(rusers.Users) != 0 {
|
if len(rusers.Users) != 0 {
|
||||||
t.Fatal("should have returned 0 users")
|
t.Fatal("should have returned 0 users")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInTeam(teamId, "", "")
|
rusers, resp = Client.AutocompleteUsersInTeam(teamId, "", model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
if len(rusers.Users) < 2 {
|
if len(rusers.Users) < 2 {
|
||||||
t.Fatal("should have many users")
|
t.Fatal("should have many users")
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.Logout()
|
Client.Logout()
|
||||||
_, resp = Client.AutocompleteUsersInChannel(teamId, channelId, username, "")
|
_, resp = Client.AutocompleteUsersInChannel(teamId, channelId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
|
|
||||||
_, resp = Client.AutocompleteUsersInTeam(teamId, username, "")
|
_, resp = Client.AutocompleteUsersInTeam(teamId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
|
|
||||||
_, resp = Client.AutocompleteUsers(username, "")
|
_, resp = Client.AutocompleteUsers(username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckUnauthorizedStatus(t, resp)
|
CheckUnauthorizedStatus(t, resp)
|
||||||
|
|
||||||
user := th.CreateUser()
|
user := th.CreateUser()
|
||||||
Client.Login(user.Email, user.Password)
|
Client.Login(user.Email, user.Password)
|
||||||
_, resp = Client.AutocompleteUsersInChannel(teamId, channelId, username, "")
|
_, resp = Client.AutocompleteUsersInChannel(teamId, channelId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckForbiddenStatus(t, resp)
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
_, resp = Client.AutocompleteUsersInTeam(teamId, username, "")
|
_, resp = Client.AutocompleteUsersInTeam(teamId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckForbiddenStatus(t, resp)
|
CheckForbiddenStatus(t, resp)
|
||||||
|
|
||||||
_, resp = Client.AutocompleteUsers(username, "")
|
_, resp = Client.AutocompleteUsers(username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.AutocompleteUsersInChannel(teamId, channelId, username, "")
|
_, resp = th.SystemAdminClient.AutocompleteUsersInChannel(teamId, channelId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.AutocompleteUsersInTeam(teamId, username, "")
|
_, resp = th.SystemAdminClient.AutocompleteUsersInTeam(teamId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
_, resp = th.SystemAdminClient.AutocompleteUsers(username, "")
|
_, resp = th.SystemAdminClient.AutocompleteUsers(username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
// Check against privacy config settings
|
// Check against privacy config settings
|
||||||
@@ -852,21 +852,21 @@ func TestAutocompleteUsers(t *testing.T) {
|
|||||||
|
|
||||||
th.LoginBasic()
|
th.LoginBasic()
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsers(username, "")
|
rusers, resp = Client.AutocompleteUsers(username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if rusers.Users[0].FirstName != "" || rusers.Users[0].LastName != "" {
|
if rusers.Users[0].FirstName != "" || rusers.Users[0].LastName != "" {
|
||||||
t.Fatal("should not show first/last name")
|
t.Fatal("should not show first/last name")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInChannel(teamId, channelId, username, "")
|
rusers, resp = Client.AutocompleteUsersInChannel(teamId, channelId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if rusers.Users[0].FirstName != "" || rusers.Users[0].LastName != "" {
|
if rusers.Users[0].FirstName != "" || rusers.Users[0].LastName != "" {
|
||||||
t.Fatal("should not show first/last name")
|
t.Fatal("should not show first/last name")
|
||||||
}
|
}
|
||||||
|
|
||||||
rusers, resp = Client.AutocompleteUsersInTeam(teamId, username, "")
|
rusers, resp = Client.AutocompleteUsersInTeam(teamId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckNoError(t, resp)
|
CheckNoError(t, resp)
|
||||||
|
|
||||||
if rusers.Users[0].FirstName != "" || rusers.Users[0].LastName != "" {
|
if rusers.Users[0].FirstName != "" || rusers.Users[0].LastName != "" {
|
||||||
@@ -874,7 +874,7 @@ func TestAutocompleteUsers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Run("user must have access to team id, especially when it does not match channel's team id", func(t *testing.T) {
|
t.Run("user must have access to team id, especially when it does not match channel's team id", func(t *testing.T) {
|
||||||
rusers, resp = Client.AutocompleteUsersInChannel("otherTeamId", channelId, username, "")
|
rusers, resp = Client.AutocompleteUsersInChannel("otherTeamId", channelId, username, model.USER_SEARCH_DEFAULT_LIMIT, "")
|
||||||
CheckErrorMessage(t, resp, "api.context.permissions.app_error")
|
CheckErrorMessage(t, resp, "api.context.permissions.app_error")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
58
app/user.go
58
app/user.go
@@ -1469,93 +1469,93 @@ func (a *App) VerifyUserEmail(userId string) *model.AppError {
|
|||||||
return (<-a.Srv.Store.User().VerifyEmail(userId)).Err
|
return (<-a.Srv.Store.User().VerifyEmail(userId)).Err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchUsers(props *model.UserSearch, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) {
|
func (a *App) SearchUsers(props *model.UserSearch, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||||
if props.WithoutTeam {
|
if props.WithoutTeam {
|
||||||
return a.SearchUsersWithoutTeam(props.Term, searchOptions, asAdmin)
|
return a.SearchUsersWithoutTeam(props.Term, options)
|
||||||
} else if props.InChannelId != "" {
|
} else if props.InChannelId != "" {
|
||||||
return a.SearchUsersInChannel(props.InChannelId, props.Term, searchOptions, asAdmin)
|
return a.SearchUsersInChannel(props.InChannelId, props.Term, options)
|
||||||
} else if props.NotInChannelId != "" {
|
} else if props.NotInChannelId != "" {
|
||||||
return a.SearchUsersNotInChannel(props.TeamId, props.NotInChannelId, props.Term, searchOptions, asAdmin)
|
return a.SearchUsersNotInChannel(props.TeamId, props.NotInChannelId, props.Term, options)
|
||||||
} else if props.NotInTeamId != "" {
|
} else if props.NotInTeamId != "" {
|
||||||
return a.SearchUsersNotInTeam(props.NotInTeamId, props.Term, searchOptions, asAdmin)
|
return a.SearchUsersNotInTeam(props.NotInTeamId, props.Term, options)
|
||||||
} else {
|
} else {
|
||||||
return a.SearchUsersInTeam(props.TeamId, props.Term, searchOptions, asAdmin)
|
return a.SearchUsersInTeam(props.TeamId, props.Term, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchUsersInChannel(channelId string, term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) {
|
func (a *App) SearchUsersInChannel(channelId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||||
if result := <-a.Srv.Store.User().SearchInChannel(channelId, term, searchOptions); result.Err != nil {
|
if result := <-a.Srv.Store.User().SearchInChannel(channelId, term, options); result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
} else {
|
} else {
|
||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchUsersNotInChannel(teamId string, channelId string, term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) {
|
func (a *App) SearchUsersNotInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||||
if result := <-a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, searchOptions); result.Err != nil {
|
if result := <-a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, options); result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
} else {
|
} else {
|
||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchUsersInTeam(teamId string, term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) {
|
func (a *App) SearchUsersInTeam(teamId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||||
if result := <-a.Srv.Store.User().Search(teamId, term, searchOptions); result.Err != nil {
|
if result := <-a.Srv.Store.User().Search(teamId, term, options); result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
} else {
|
} else {
|
||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchUsersNotInTeam(notInTeamId string, term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) {
|
func (a *App) SearchUsersNotInTeam(notInTeamId string, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||||
if result := <-a.Srv.Store.User().SearchNotInTeam(notInTeamId, term, searchOptions); result.Err != nil {
|
if result := <-a.Srv.Store.User().SearchNotInTeam(notInTeamId, term, options); result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
} else {
|
} else {
|
||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchUsersWithoutTeam(term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) {
|
func (a *App) SearchUsersWithoutTeam(term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) {
|
||||||
if result := <-a.Srv.Store.User().SearchWithoutTeam(term, searchOptions); result.Err != nil {
|
if result := <-a.Srv.Store.User().SearchWithoutTeam(term, options); result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
} else {
|
} else {
|
||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term string, searchOptions map[string]bool, asAdmin bool) (*model.UserAutocompleteInChannel, *model.AppError) {
|
func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) {
|
||||||
uchan := a.Srv.Store.User().SearchInChannel(channelId, term, searchOptions)
|
uchan := a.Srv.Store.User().SearchInChannel(channelId, term, options)
|
||||||
nuchan := a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, searchOptions)
|
nuchan := a.Srv.Store.User().SearchNotInChannel(teamId, channelId, term, options)
|
||||||
|
|
||||||
autocomplete := &model.UserAutocompleteInChannel{}
|
autocomplete := &model.UserAutocompleteInChannel{}
|
||||||
|
|
||||||
@@ -1565,7 +1565,7 @@ func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term s
|
|||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
autocomplete.InChannel = users
|
autocomplete.InChannel = users
|
||||||
@@ -1577,7 +1577,7 @@ func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term s
|
|||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
autocomplete.OutOfChannel = users
|
autocomplete.OutOfChannel = users
|
||||||
@@ -1586,16 +1586,16 @@ func (a *App) AutocompleteUsersInChannel(teamId string, channelId string, term s
|
|||||||
return autocomplete, nil
|
return autocomplete, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) AutocompleteUsersInTeam(teamId string, term string, searchOptions map[string]bool, asAdmin bool) (*model.UserAutocompleteInTeam, *model.AppError) {
|
func (a *App) AutocompleteUsersInTeam(teamId string, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInTeam, *model.AppError) {
|
||||||
autocomplete := &model.UserAutocompleteInTeam{}
|
autocomplete := &model.UserAutocompleteInTeam{}
|
||||||
|
|
||||||
if result := <-a.Srv.Store.User().Search(teamId, term, searchOptions); result.Err != nil {
|
if result := <-a.Srv.Store.User().Search(teamId, term, options); result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
} else {
|
} else {
|
||||||
users := result.Data.([]*model.User)
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
a.SanitizeProfile(user, asAdmin)
|
a.SanitizeProfile(user, options.IsAdmin)
|
||||||
}
|
}
|
||||||
|
|
||||||
autocomplete.InTeam = users
|
autocomplete.InTeam = users
|
||||||
|
|||||||
@@ -417,6 +417,10 @@ func (c *Client4) DoApiPost(url string, data string) (*http.Response, *AppError)
|
|||||||
return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "")
|
return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client4) doApiPostBytes(url string, data []byte) (*http.Response, *AppError) {
|
||||||
|
return c.doApiRequestBytes(http.MethodPost, c.ApiUrl+url, data, "")
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) {
|
func (c *Client4) DoApiPut(url string, data string) (*http.Response, *AppError) {
|
||||||
return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "")
|
return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "")
|
||||||
}
|
}
|
||||||
@@ -426,7 +430,15 @@ func (c *Client4) DoApiDelete(url string) (*http.Response, *AppError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
|
func (c *Client4) DoApiRequest(method, url, data, etag string) (*http.Response, *AppError) {
|
||||||
rq, _ := http.NewRequest(method, url, strings.NewReader(data))
|
return c.doApiRequestReader(method, url, strings.NewReader(data), etag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) doApiRequestBytes(method, url string, data []byte, etag string) (*http.Response, *AppError) {
|
||||||
|
return c.doApiRequestReader(method, url, bytes.NewReader(data), etag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client4) doApiRequestReader(method, url string, data io.Reader, etag string) (*http.Response, *AppError) {
|
||||||
|
rq, _ := http.NewRequest(method, url, data)
|
||||||
|
|
||||||
if len(etag) > 0 {
|
if len(etag) > 0 {
|
||||||
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
|
rq.Header.Set(HEADER_ETAG_CLIENT, etag)
|
||||||
@@ -691,8 +703,8 @@ func (c *Client4) GetUserByEmail(email, etag string) (*User, *Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AutocompleteUsersInTeam returns the users on a team based on search term.
|
// AutocompleteUsersInTeam returns the users on a team based on search term.
|
||||||
func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, etag string) (*UserAutocomplete, *Response) {
|
func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, limit int, etag string) (*UserAutocomplete, *Response) {
|
||||||
query := fmt.Sprintf("?in_team=%v&name=%v", teamId, username)
|
query := fmt.Sprintf("?in_team=%v&name=%v&limit=%d", teamId, username, limit)
|
||||||
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
|
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -702,8 +714,8 @@ func (c *Client4) AutocompleteUsersInTeam(teamId string, username string, etag s
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AutocompleteUsersInChannel returns the users in a channel based on search term.
|
// AutocompleteUsersInChannel returns the users in a channel based on search term.
|
||||||
func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, etag string) (*UserAutocomplete, *Response) {
|
func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, username string, limit int, etag string) (*UserAutocomplete, *Response) {
|
||||||
query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v", teamId, channelId, username)
|
query := fmt.Sprintf("?in_team=%v&in_channel=%v&name=%v&limit=%d", teamId, channelId, username, limit)
|
||||||
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
|
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -713,8 +725,8 @@ func (c *Client4) AutocompleteUsersInChannel(teamId string, channelId string, us
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AutocompleteUsers returns the users in the system based on search term.
|
// AutocompleteUsers returns the users in the system based on search term.
|
||||||
func (c *Client4) AutocompleteUsers(username string, etag string) (*UserAutocomplete, *Response) {
|
func (c *Client4) AutocompleteUsers(username string, limit int, etag string) (*UserAutocomplete, *Response) {
|
||||||
query := fmt.Sprintf("?name=%v", username)
|
query := fmt.Sprintf("?name=%v&limit=%d", username, limit)
|
||||||
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
|
if r, err := c.DoApiGet(c.GetUsersRoute()+"/autocomplete"+query, etag); err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -875,7 +887,7 @@ func (c *Client4) GetUsersByUsernames(usernames []string) ([]*User, *Response) {
|
|||||||
|
|
||||||
// SearchUsers returns a list of users based on some search criteria.
|
// SearchUsers returns a list of users based on some search criteria.
|
||||||
func (c *Client4) SearchUsers(search *UserSearch) ([]*User, *Response) {
|
func (c *Client4) SearchUsers(search *UserSearch) ([]*User, *Response) {
|
||||||
if r, err := c.DoApiPost(c.GetUsersRoute()+"/search", search.ToJson()); err != nil {
|
if r, err := c.doApiPostBytes(c.GetUsersRoute()+"/search", search.ToJson()); err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
} else {
|
} else {
|
||||||
defer closeBody(r)
|
defer closeBody(r)
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const USER_SEARCH_MAX_LIMIT = 1000
|
||||||
|
const USER_SEARCH_DEFAULT_LIMIT = 100
|
||||||
|
|
||||||
|
// UserSearch captures the parameters provided by a client for initiating a user search.
|
||||||
type UserSearch struct {
|
type UserSearch struct {
|
||||||
Term string `json:"term"`
|
Term string `json:"term"`
|
||||||
TeamId string `json:"team_id"`
|
TeamId string `json:"team_id"`
|
||||||
@@ -16,17 +20,38 @@ type UserSearch struct {
|
|||||||
NotInChannelId string `json:"not_in_channel_id"`
|
NotInChannelId string `json:"not_in_channel_id"`
|
||||||
AllowInactive bool `json:"allow_inactive"`
|
AllowInactive bool `json:"allow_inactive"`
|
||||||
WithoutTeam bool `json:"without_team"`
|
WithoutTeam bool `json:"without_team"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToJson convert a User to a json string
|
// ToJson convert a User to a json string
|
||||||
func (u *UserSearch) ToJson() string {
|
func (u *UserSearch) ToJson() []byte {
|
||||||
b, _ := json.Marshal(u)
|
b, _ := json.Marshal(u)
|
||||||
return string(b)
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserSearchFromJson will decode the input and return a User
|
// UserSearchFromJson will decode the input and return a User
|
||||||
func UserSearchFromJson(data io.Reader) *UserSearch {
|
func UserSearchFromJson(data io.Reader) *UserSearch {
|
||||||
var us *UserSearch
|
var us *UserSearch
|
||||||
json.NewDecoder(data).Decode(&us)
|
json.NewDecoder(data).Decode(&us)
|
||||||
|
|
||||||
|
if us.Limit == 0 {
|
||||||
|
us.Limit = USER_SEARCH_DEFAULT_LIMIT
|
||||||
|
}
|
||||||
|
|
||||||
return us
|
return us
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserSearchOptions captures internal parameters derived from the user's permissions and a
|
||||||
|
// UserSearch request.
|
||||||
|
type UserSearchOptions struct {
|
||||||
|
// IsAdmin tracks whether or not the search is being conducted by an administrator.
|
||||||
|
IsAdmin bool
|
||||||
|
// AllowEmails allows search to examine the emails of users.
|
||||||
|
AllowEmails bool
|
||||||
|
// AllowFullNames allows search to examine the full names of users, vs. just usernames and nicknames.
|
||||||
|
AllowFullNames bool
|
||||||
|
// AllowInactive configures whether or not to return inactive users in the search results.
|
||||||
|
AllowInactive bool
|
||||||
|
// Limit limits the total number of results returned.
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUserSearchJson(t *testing.T) {
|
func TestUserSearchJson(t *testing.T) {
|
||||||
userSearch := UserSearch{Term: NewId(), TeamId: NewId()}
|
userSearch := UserSearch{Term: NewId(), TeamId: NewId()}
|
||||||
json := userSearch.ToJson()
|
json := userSearch.ToJson()
|
||||||
ruserSearch := UserSearchFromJson(strings.NewReader(json))
|
ruserSearch := UserSearchFromJson(bytes.NewReader(json))
|
||||||
|
|
||||||
if userSearch.Term != ruserSearch.Term {
|
if userSearch.Term != ruserSearch.Term {
|
||||||
t.Fatal("Terms do not match")
|
t.Fatal("Terms do not match")
|
||||||
|
|||||||
@@ -970,12 +970,11 @@ func (us SqlUserStore) GetAnyUnreadPostCountForChannel(userId string, channelId
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) Search(teamId string, term string, options map[string]bool) store.StoreChannel {
|
func (us SqlUserStore) Search(teamId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
searchQuery := ""
|
searchQuery := ""
|
||||||
|
|
||||||
if teamId == "" {
|
if teamId == "" {
|
||||||
|
|
||||||
// Id != '' is added because both SEARCH_CLAUSE and INACTIVE_CLAUSE start with an AND
|
// Id != '' is added because both SEARCH_CLAUSE and INACTIVE_CLAUSE start with an AND
|
||||||
searchQuery = `
|
searchQuery = `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -986,8 +985,8 @@ func (us SqlUserStore) Search(teamId string, term string, options map[string]boo
|
|||||||
Id != ''
|
Id != ''
|
||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Username ASC
|
ORDER BY Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit`
|
||||||
} else {
|
} else {
|
||||||
searchQuery = `
|
searchQuery = `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -1000,16 +999,19 @@ func (us SqlUserStore) Search(teamId string, term string, options map[string]boo
|
|||||||
AND TeamMembers.DeleteAt = 0
|
AND TeamMembers.DeleteAt = 0
|
||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Users.Username ASC
|
ORDER BY Users.Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit`
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId})
|
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{
|
||||||
|
"TeamId": teamId,
|
||||||
|
"Limit": options.Limit,
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) SearchWithoutTeam(term string, options map[string]bool) store.StoreChannel {
|
func (us SqlUserStore) SearchWithoutTeam(term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
searchQuery := `
|
searchQuery := `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -1027,14 +1029,16 @@ func (us SqlUserStore) SearchWithoutTeam(term string, options map[string]bool) s
|
|||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Username ASC
|
ORDER BY Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit`
|
||||||
|
|
||||||
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{})
|
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{
|
||||||
|
"Limit": options.Limit,
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options map[string]bool) store.StoreChannel {
|
func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
searchQuery := `
|
searchQuery := `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -1048,14 +1052,17 @@ func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options
|
|||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Users.Username ASC
|
ORDER BY Users.Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit`
|
||||||
|
|
||||||
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"NotInTeamId": notInTeamId})
|
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{
|
||||||
|
"NotInTeamId": notInTeamId,
|
||||||
|
"Limit": options.Limit,
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) store.StoreChannel {
|
func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
searchQuery := ""
|
searchQuery := ""
|
||||||
if teamId == "" {
|
if teamId == "" {
|
||||||
@@ -1071,7 +1078,7 @@ func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term
|
|||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Users.Username ASC
|
ORDER BY Users.Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit`
|
||||||
} else {
|
} else {
|
||||||
searchQuery = `
|
searchQuery = `
|
||||||
SELECT
|
SELECT
|
||||||
@@ -1089,30 +1096,37 @@ func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term
|
|||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Users.Username ASC
|
ORDER BY Users.Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit`
|
||||||
}
|
}
|
||||||
|
|
||||||
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId, "ChannelId": channelId})
|
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{
|
||||||
|
"TeamId": teamId,
|
||||||
|
"ChannelId": channelId,
|
||||||
|
"Limit": options.Limit,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) SearchInChannel(channelId string, term string, options map[string]bool) store.StoreChannel {
|
func (us SqlUserStore) SearchInChannel(channelId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
searchQuery := `
|
searchQuery := `
|
||||||
SELECT
|
SELECT
|
||||||
Users.*
|
Users.*
|
||||||
FROM
|
FROM
|
||||||
Users, ChannelMembers
|
Users, ChannelMembers
|
||||||
WHERE
|
WHERE
|
||||||
ChannelMembers.ChannelId = :ChannelId
|
ChannelMembers.ChannelId = :ChannelId
|
||||||
AND ChannelMembers.UserId = Users.Id
|
AND ChannelMembers.UserId = Users.Id
|
||||||
SEARCH_CLAUSE
|
SEARCH_CLAUSE
|
||||||
INACTIVE_CLAUSE
|
INACTIVE_CLAUSE
|
||||||
ORDER BY Users.Username ASC
|
ORDER BY Users.Username ASC
|
||||||
LIMIT 100`
|
LIMIT :Limit
|
||||||
|
`
|
||||||
|
|
||||||
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"ChannelId": channelId})
|
*result = us.performSearch(searchQuery, term, options, map[string]interface{}{
|
||||||
|
"ChannelId": channelId,
|
||||||
|
"Limit": options.Limit,
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1160,7 +1174,7 @@ func generateSearchQuery(searchQuery string, terms []string, fields []string, pa
|
|||||||
return strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf(" AND %s ", searchClause), 1)
|
return strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf(" AND %s ", searchClause), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) performSearch(searchQuery string, term string, options map[string]bool, parameters map[string]interface{}) store.StoreResult {
|
func (us SqlUserStore) performSearch(searchQuery string, term string, options *model.UserSearchOptions, parameters map[string]interface{}) store.StoreResult {
|
||||||
result := store.StoreResult{}
|
result := store.StoreResult{}
|
||||||
|
|
||||||
// These chars must be removed from the like query.
|
// These chars must be removed from the like query.
|
||||||
@@ -1173,16 +1187,22 @@ func (us SqlUserStore) performSearch(searchQuery string, term string, options ma
|
|||||||
term = strings.Replace(term, c, "*"+c, -1)
|
term = strings.Replace(term, c, "*"+c, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
searchType := USER_SEARCH_TYPE_ALL
|
searchType := USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
|
||||||
if ok := options[store.USER_SEARCH_OPTION_NAMES_ONLY]; ok {
|
if options.AllowEmails {
|
||||||
searchType = USER_SEARCH_TYPE_NAMES
|
if options.AllowFullNames {
|
||||||
} else if ok = options[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME]; ok {
|
searchType = USER_SEARCH_TYPE_ALL
|
||||||
searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
|
} else {
|
||||||
} else if ok = options[store.USER_SEARCH_OPTION_ALL_NO_FULL_NAME]; ok {
|
searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
|
||||||
searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
|
}
|
||||||
|
} else {
|
||||||
|
if options.AllowFullNames {
|
||||||
|
searchType = USER_SEARCH_TYPE_NAMES
|
||||||
|
} else {
|
||||||
|
searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok := options[store.USER_SEARCH_OPTION_ALLOW_INACTIVE]; ok {
|
if ok := options.AllowInactive; ok {
|
||||||
searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "", 1)
|
searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "", 1)
|
||||||
} else {
|
} else {
|
||||||
searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "AND Users.DeleteAt = 0", 1)
|
searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "AND Users.DeleteAt = 0", 1)
|
||||||
|
|||||||
@@ -272,11 +272,11 @@ type UserStore interface {
|
|||||||
GetAnyUnreadPostCountForChannel(userId string, channelId string) StoreChannel
|
GetAnyUnreadPostCountForChannel(userId string, channelId string) StoreChannel
|
||||||
GetRecentlyActiveUsersForTeam(teamId string, offset, limit int) StoreChannel
|
GetRecentlyActiveUsersForTeam(teamId string, offset, limit int) StoreChannel
|
||||||
GetNewUsersForTeam(teamId string, offset, limit int) StoreChannel
|
GetNewUsersForTeam(teamId string, offset, limit int) StoreChannel
|
||||||
Search(teamId string, term string, options map[string]bool) StoreChannel
|
Search(teamId string, term string, options *model.UserSearchOptions) StoreChannel
|
||||||
SearchNotInTeam(notInTeamId string, term string, options map[string]bool) StoreChannel
|
SearchNotInTeam(notInTeamId string, term string, options *model.UserSearchOptions) StoreChannel
|
||||||
SearchInChannel(channelId string, term string, options map[string]bool) StoreChannel
|
SearchInChannel(channelId string, term string, options *model.UserSearchOptions) StoreChannel
|
||||||
SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) StoreChannel
|
SearchNotInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) StoreChannel
|
||||||
SearchWithoutTeam(term string, options map[string]bool) StoreChannel
|
SearchWithoutTeam(term string, options *model.UserSearchOptions) StoreChannel
|
||||||
AnalyticsGetInactiveUsersCount() StoreChannel
|
AnalyticsGetInactiveUsersCount() StoreChannel
|
||||||
AnalyticsGetSystemAdminCount() StoreChannel
|
AnalyticsGetSystemAdminCount() StoreChannel
|
||||||
GetProfilesNotInTeam(teamId string, offset int, limit int) StoreChannel
|
GetProfilesNotInTeam(teamId string, offset int, limit int) StoreChannel
|
||||||
|
|||||||
@@ -314,6 +314,22 @@ func (_m *ChannelStore) GetChannelMembersForExport(userId string, teamId string)
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChannelMembersTimezones provides a mock function with given fields: channelId
|
||||||
|
func (_m *ChannelStore) GetChannelMembersTimezones(channelId string) store.StoreChannel {
|
||||||
|
ret := _m.Called(channelId)
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
||||||
|
r0 = rf(channelId)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// GetChannelUnread provides a mock function with given fields: channelId, userId
|
// GetChannelUnread provides a mock function with given fields: channelId, userId
|
||||||
func (_m *ChannelStore) GetChannelUnread(channelId string, userId string) store.StoreChannel {
|
func (_m *ChannelStore) GetChannelUnread(channelId string, userId string) store.StoreChannel {
|
||||||
ret := _m.Called(channelId, userId)
|
ret := _m.Called(channelId, userId)
|
||||||
@@ -442,21 +458,6 @@ func (_m *ChannelStore) GetMember(channelId string, userId string) store.StoreCh
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_m *ChannelStore) GetChannelMembersTimezones(channelId string) store.StoreChannel {
|
|
||||||
ret := _m.Called(channelId)
|
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
|
||||||
if rf, ok := ret.Get(0).(func(string) store.StoreChannel); ok {
|
|
||||||
r0 = rf(channelId)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(store.StoreChannel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMemberCount provides a mock function with given fields: channelId, allowFromCache
|
// GetMemberCount provides a mock function with given fields: channelId, allowFromCache
|
||||||
func (_m *ChannelStore) GetMemberCount(channelId string, allowFromCache bool) store.StoreChannel {
|
func (_m *ChannelStore) GetMemberCount(channelId string, allowFromCache bool) store.StoreChannel {
|
||||||
ret := _m.Called(channelId, allowFromCache)
|
ret := _m.Called(channelId, allowFromCache)
|
||||||
|
|||||||
@@ -626,11 +626,11 @@ func (_m *UserStore) Save(user *model.User) store.StoreChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search provides a mock function with given fields: teamId, term, options
|
// Search provides a mock function with given fields: teamId, term, options
|
||||||
func (_m *UserStore) Search(teamId string, term string, options map[string]bool) store.StoreChannel {
|
func (_m *UserStore) Search(teamId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
ret := _m.Called(teamId, term, options)
|
ret := _m.Called(teamId, term, options)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, map[string]bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, *model.UserSearchOptions) store.StoreChannel); ok {
|
||||||
r0 = rf(teamId, term, options)
|
r0 = rf(teamId, term, options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
@@ -642,11 +642,11 @@ func (_m *UserStore) Search(teamId string, term string, options map[string]bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SearchInChannel provides a mock function with given fields: channelId, term, options
|
// SearchInChannel provides a mock function with given fields: channelId, term, options
|
||||||
func (_m *UserStore) SearchInChannel(channelId string, term string, options map[string]bool) store.StoreChannel {
|
func (_m *UserStore) SearchInChannel(channelId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
ret := _m.Called(channelId, term, options)
|
ret := _m.Called(channelId, term, options)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, map[string]bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, *model.UserSearchOptions) store.StoreChannel); ok {
|
||||||
r0 = rf(channelId, term, options)
|
r0 = rf(channelId, term, options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
@@ -658,11 +658,11 @@ func (_m *UserStore) SearchInChannel(channelId string, term string, options map[
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SearchNotInChannel provides a mock function with given fields: teamId, channelId, term, options
|
// SearchNotInChannel provides a mock function with given fields: teamId, channelId, term, options
|
||||||
func (_m *UserStore) SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) store.StoreChannel {
|
func (_m *UserStore) SearchNotInChannel(teamId string, channelId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
ret := _m.Called(teamId, channelId, term, options)
|
ret := _m.Called(teamId, channelId, term, options)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, string, map[string]bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, string, *model.UserSearchOptions) store.StoreChannel); ok {
|
||||||
r0 = rf(teamId, channelId, term, options)
|
r0 = rf(teamId, channelId, term, options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
@@ -674,11 +674,11 @@ func (_m *UserStore) SearchNotInChannel(teamId string, channelId string, term st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SearchNotInTeam provides a mock function with given fields: notInTeamId, term, options
|
// SearchNotInTeam provides a mock function with given fields: notInTeamId, term, options
|
||||||
func (_m *UserStore) SearchNotInTeam(notInTeamId string, term string, options map[string]bool) store.StoreChannel {
|
func (_m *UserStore) SearchNotInTeam(notInTeamId string, term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
ret := _m.Called(notInTeamId, term, options)
|
ret := _m.Called(notInTeamId, term, options)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, map[string]bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, *model.UserSearchOptions) store.StoreChannel); ok {
|
||||||
r0 = rf(notInTeamId, term, options)
|
r0 = rf(notInTeamId, term, options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
@@ -690,11 +690,11 @@ func (_m *UserStore) SearchNotInTeam(notInTeamId string, term string, options ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SearchWithoutTeam provides a mock function with given fields: term, options
|
// SearchWithoutTeam provides a mock function with given fields: term, options
|
||||||
func (_m *UserStore) SearchWithoutTeam(term string, options map[string]bool) store.StoreChannel {
|
func (_m *UserStore) SearchWithoutTeam(term string, options *model.UserSearchOptions) store.StoreChannel {
|
||||||
ret := _m.Called(term, options)
|
ret := _m.Called(term, options)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, map[string]bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, *model.UserSearchOptions) store.StoreChannel); ok {
|
||||||
r0 = rf(term, options)
|
r0 = rf(term, options)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
|
|||||||
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Ссылка в новой задаче
Block a user