* MM-11065: Allow to search and get archived channels from the API

* Fixing more tests

* Add some unit tests

* Add includeDeleted parameter to session permissions check function

* More test fixing

* Adding archive channels list in channels search

* Add restriction for archived channel edition

* Reverting permissions checks modification

* Changed the query parameter to include_deleted

* Enable search archive channels as true by default

* Adding tests for verify search on deleted channels

* Allowing to override archive channels during the imports

* Fixed test

* Search in archive channels from the API must be explicitly requested

* Removing includeDeleted parameter from GetChannelByName and GetChannelByNameForTeam

* Back to ViewArchivedChannels config

* Fixing tests

* Reverting GetChannelByName parameter

* Add include deleted parameter on GetChannel functions in plugins api

* Fixing tests
This commit is contained in:
Martin Kraft
2018-07-30 15:06:08 -04:00
committed by GitHub
parent 65cd447a61
commit 5872bf9c2f
33 changed files with 399 additions and 185 deletions

View File

@@ -44,6 +44,7 @@ type TestHelper struct {
BasicTeam *model.Team
BasicChannel *model.Channel
BasicPrivateChannel *model.Channel
BasicDeletedChannel *model.Channel
BasicChannel2 *model.Channel
BasicPost *model.Post
@@ -250,6 +251,7 @@ func (me *TestHelper) InitBasic() *TestHelper {
me.BasicTeam = me.CreateTeam()
me.BasicChannel = me.CreatePublicChannel()
me.BasicPrivateChannel = me.CreatePrivateChannel()
me.BasicDeletedChannel = me.CreatePublicChannel()
me.BasicChannel2 = me.CreatePublicChannel()
me.BasicPost = me.CreatePost()
me.BasicUser = me.CreateUser()
@@ -262,7 +264,10 @@ func (me *TestHelper) InitBasic() *TestHelper {
me.App.AddUserToChannel(me.BasicUser2, me.BasicChannel2)
me.App.AddUserToChannel(me.BasicUser, me.BasicPrivateChannel)
me.App.AddUserToChannel(me.BasicUser2, me.BasicPrivateChannel)
me.App.AddUserToChannel(me.BasicUser, me.BasicDeletedChannel)
me.App.AddUserToChannel(me.BasicUser2, me.BasicDeletedChannel)
me.App.UpdateUserRoles(me.BasicUser.Id, model.SYSTEM_USER_ROLE_ID, false)
me.Client.DeleteChannel(me.BasicDeletedChannel.Id)
me.LoginBasic()
return me

View File

@@ -589,7 +589,7 @@ func getChannelsForTeamForUser(c *Context, w http.ResponseWriter, r *http.Reques
return
}
channels, err := c.App.GetChannelsForUser(c.Params.TeamId, c.Params.UserId)
channels, err := c.App.GetChannelsForUser(c.Params.TeamId, c.Params.UserId, false)
if err != nil {
c.Err = err
return
@@ -709,7 +709,9 @@ func getChannelByName(c *Context, w http.ResponseWriter, r *http.Request) {
var channel *model.Channel
var err *model.AppError
if channel, err = c.App.GetChannelByName(c.Params.ChannelName, c.Params.TeamId); err != nil {
includeDeleted := r.URL.Query().Get("include_deleted") == "true"
if channel, err = c.App.GetChannelByName(c.Params.ChannelName, c.Params.TeamId, includeDeleted); err != nil {
c.Err = err
return
}
@@ -744,7 +746,9 @@ func getChannelByNameForTeamName(c *Context, w http.ResponseWriter, r *http.Requ
var channel *model.Channel
var err *model.AppError
if channel, err = c.App.GetChannelByNameForTeamName(c.Params.ChannelName, c.Params.TeamName); err != nil {
includeDeleted := r.URL.Query().Get("include_deleted") == "true"
if channel, err = c.App.GetChannelByNameForTeamName(c.Params.ChannelName, c.Params.TeamName, includeDeleted); err != nil {
c.Err = err
return
}

View File

@@ -867,7 +867,7 @@ func TestDeleteChannel(t *testing.T) {
CheckNoError(t, resp)
// default channel cannot be deleted.
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, team.Id)
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, team.Id, false)
pass, resp = Client.DeleteChannel(defaultChannel.Id)
CheckBadRequestStatus(t, resp)
@@ -998,7 +998,7 @@ func TestConvertChannelToPrivate(t *testing.T) {
defer th.TearDown()
Client := th.Client
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, th.BasicTeam.Id)
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, th.BasicTeam.Id, false)
_, resp := Client.ConvertChannelToPrivate(defaultChannel.Id)
CheckForbiddenStatus(t, resp)
@@ -1115,6 +1115,16 @@ func TestGetChannelByName(t *testing.T) {
_, resp = Client.GetChannelByName(strings.ToUpper(th.BasicPrivateChannel.Name), th.BasicTeam.Id, "")
CheckNoError(t, resp)
_, resp = Client.GetChannelByName(th.BasicDeletedChannel.Name, th.BasicTeam.Id, "")
CheckNotFoundStatus(t, resp)
channel, resp = Client.GetChannelByNameIncludeDeleted(th.BasicDeletedChannel.Name, th.BasicTeam.Id, "")
CheckNoError(t, resp)
if channel.Name != th.BasicDeletedChannel.Name {
t.Fatal("names did not match")
}
Client.RemoveUserFromChannel(th.BasicChannel.Id, th.BasicUser.Id)
_, resp = Client.GetChannelByName(th.BasicChannel.Name, th.BasicTeam.Id, "")
CheckNoError(t, resp)
@@ -1157,6 +1167,16 @@ func TestGetChannelByNameForTeamName(t *testing.T) {
_, resp = Client.GetChannelByNameForTeamName(th.BasicChannel.Name, th.BasicTeam.Name, "")
CheckNoError(t, resp)
_, resp = Client.GetChannelByNameForTeamName(th.BasicDeletedChannel.Name, th.BasicTeam.Name, "")
CheckNotFoundStatus(t, resp)
channel, resp = Client.GetChannelByNameForTeamNameIncludeDeleted(th.BasicDeletedChannel.Name, th.BasicTeam.Name, "")
CheckNoError(t, resp)
if channel.Name != th.BasicDeletedChannel.Name {
t.Fatal("names did not match")
}
_, resp = Client.GetChannelByNameForTeamName(th.BasicChannel.Name, model.NewRandomString(15), "")
CheckNotFoundStatus(t, resp)
@@ -1330,8 +1350,8 @@ func TestGetChannelMembersForUser(t *testing.T) {
members, resp := Client.GetChannelMembersForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
CheckNoError(t, resp)
if len(*members) != 5 {
t.Fatal("should have 5 members on team")
if len(*members) != 6 {
t.Fatal("should have 6 members on team")
}
_, resp = Client.GetChannelMembersForUser("", th.BasicTeam.Id, "")

View File

@@ -330,6 +330,8 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
includeDeletedChannels := r.URL.Query().Get("include_deleted_channels") == "true"
props := model.StringInterfaceFromJson(r.Body)
terms, ok := props["terms"].(string)
if !ok || len(terms) == 0 {
@@ -341,7 +343,7 @@ func searchPosts(c *Context, w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
results, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch)
results, err := c.App.SearchPostsInTeam(terms, c.Session.UserId, c.Params.TeamId, isOrSearch, includeDeletedChannels)
elapsedTime := float64(time.Since(startTime)) / float64(time.Second)
metrics := c.App.Metrics

View File

@@ -1288,6 +1288,10 @@ func TestSearchPosts(t *testing.T) {
message = "hashtag for post4"
_ = th.CreateMessagePost(message)
archivedChannel := th.CreatePublicChannel()
_ = th.CreateMessagePostWithClient(th.Client, archivedChannel, "#hashtag for post3")
th.Client.DeleteChannel(archivedChannel.Id)
posts, resp := Client.SearchPosts(th.BasicTeam.Id, "search", false)
CheckNoError(t, resp)
if len(posts.Order) != 3 {
@@ -1306,6 +1310,12 @@ func TestSearchPosts(t *testing.T) {
t.Fatal("wrong search")
}
posts, resp = Client.SearchPostsIncludeDeletedChannels(th.BasicTeam.Id, "#hashtag", false)
CheckNoError(t, resp)
if len(posts.Order) != 2 {
t.Fatal("wrong search")
}
if posts, resp = Client.SearchPosts(th.BasicTeam.Id, "*", false); len(posts.Order) != 0 {
t.Fatal("searching for just * shouldn't return any results")
}
@@ -1328,7 +1338,6 @@ func TestSearchPosts(t *testing.T) {
Client.Logout()
_, resp = Client.SearchPosts(th.BasicTeam.Id, "#sgtitlereview", false)
CheckUnauthorizedStatus(t, resp)
}
func TestSearchHashtagPosts(t *testing.T) {

View File

@@ -176,7 +176,7 @@ func TestSaveReaction(t *testing.T) {
t.Run("unable-to-react-in-read-only-town-square", func(t *testing.T) {
th.LoginBasic()
channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id)
channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, true)
assert.Nil(t, err)
post := th.CreatePostWithClient(th.Client, channel)
@@ -482,7 +482,7 @@ func TestDeleteReaction(t *testing.T) {
t.Run("unable-to-delete-reactions-in-read-only-town-square", func(t *testing.T) {
th.LoginBasic()
channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id)
channel, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, true)
assert.Nil(t, err)
post := th.CreatePostWithClient(th.Client, channel)

View File

@@ -37,7 +37,7 @@ func (a *App) SessionHasPermissionToChannel(session model.Session, channelId str
return false
}
cmc := a.Srv.Store.Channel().GetAllChannelMembersForUser(session.UserId, true)
cmc := a.Srv.Store.Channel().GetAllChannelMembersForUser(session.UserId, true, true)
var channelRoles []string
if cmcresult := <-cmc; cmcresult.Err == nil {

View File

@@ -393,7 +393,7 @@ func (a *App) GetGroupChannel(userIds []string) (*model.Channel, *model.AppError
return nil, model.NewAppError("GetGroupChannel", "api.channel.create_group.bad_user.app_error", nil, "user_ids="+model.ArrayToJson(userIds), http.StatusBadRequest)
}
channel, err := a.GetChannelByName(model.GetGroupNameFromUserIds(userIds), "")
channel, err := a.GetChannelByName(model.GetGroupNameFromUserIds(userIds), "", true)
if err != nil {
return nil, err
}
@@ -1011,16 +1011,26 @@ func (a *App) GetChannel(channelId string) (*model.Channel, *model.AppError) {
}
}
func (a *App) GetChannelByName(channelName, teamId string) (*model.Channel, *model.AppError) {
if result := <-a.Srv.Store.Channel().GetByName(teamId, channelName, true); result.Err != nil && result.Err.Id == "store.sql_channel.get_by_name.missing.app_error" {
func (a *App) GetChannelByName(channelName, teamId string, includeDeleted bool) (*model.Channel, *model.AppError) {
var result store.StoreResult
if includeDeleted {
result = <-a.Srv.Store.Channel().GetByNameIncludeDeleted(teamId, channelName, false)
} else {
result = <-a.Srv.Store.Channel().GetByName(teamId, channelName, false)
}
if result.Err != nil && result.Err.Id == "store.sql_channel.get_by_name.missing.app_error" {
result.Err.StatusCode = http.StatusNotFound
return nil, result.Err
} else if result.Err != nil {
}
if result.Err != nil {
result.Err.StatusCode = http.StatusBadRequest
return nil, result.Err
} else {
return result.Data.(*model.Channel), nil
}
return result.Data.(*model.Channel), nil
}
func (a *App) GetChannelsByNames(channelNames []string, teamId string) ([]*model.Channel, *model.AppError) {
@@ -1035,7 +1045,7 @@ func (a *App) GetChannelsByNames(channelNames []string, teamId string) ([]*model
}
}
func (a *App) GetChannelByNameForTeamName(channelName, teamName string) (*model.Channel, *model.AppError) {
func (a *App) GetChannelByNameForTeamName(channelName, teamName string, includeDeleted bool) (*model.Channel, *model.AppError) {
var team *model.Team
if result := <-a.Srv.Store.Team().GetByName(teamName); result.Err != nil {
@@ -1045,19 +1055,28 @@ func (a *App) GetChannelByNameForTeamName(channelName, teamName string) (*model.
team = result.Data.(*model.Team)
}
if result := <-a.Srv.Store.Channel().GetByName(team.Id, channelName, true); result.Err != nil && result.Err.Id == "store.sql_channel.get_by_name.missing.app_error" {
var result store.StoreResult
if includeDeleted {
result = <-a.Srv.Store.Channel().GetByNameIncludeDeleted(team.Id, channelName, false)
} else {
result = <-a.Srv.Store.Channel().GetByName(team.Id, channelName, false)
}
if result.Err != nil && result.Err.Id == "store.sql_channel.get_by_name.missing.app_error" {
result.Err.StatusCode = http.StatusNotFound
return nil, result.Err
} else if result.Err != nil {
}
if result.Err != nil {
result.Err.StatusCode = http.StatusBadRequest
return nil, result.Err
} else {
return result.Data.(*model.Channel), nil
}
return result.Data.(*model.Channel), nil
}
func (a *App) GetChannelsForUser(teamId string, userId string) (*model.ChannelList, *model.AppError) {
if result := <-a.Srv.Store.Channel().GetChannels(teamId, userId); result.Err != nil {
func (a *App) GetChannelsForUser(teamId string, userId string, includeDeleted bool) (*model.ChannelList, *model.AppError) {
if result := <-a.Srv.Store.Channel().GetChannels(teamId, userId, includeDeleted); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.ChannelList), nil
@@ -1494,7 +1513,9 @@ func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *mod
}
func (a *App) AutocompleteChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
if result := <-a.Srv.Store.Channel().AutocompleteInTeam(teamId, term); result.Err != nil {
includeDeleted := *a.Config().TeamSettings.ViewArchivedChannels
if result := <-a.Srv.Store.Channel().AutocompleteInTeam(teamId, term, includeDeleted); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.ChannelList), nil
@@ -1502,7 +1523,9 @@ func (a *App) AutocompleteChannels(teamId string, term string) (*model.ChannelLi
}
func (a *App) SearchChannels(teamId string, term string) (*model.ChannelList, *model.AppError) {
if result := <-a.Srv.Store.Channel().SearchInTeam(teamId, term); result.Err != nil {
includeDeleted := *a.Config().TeamSettings.ViewArchivedChannels
if result := <-a.Srv.Store.Channel().SearchInTeam(teamId, term, includeDeleted); result.Err != nil {
return nil, result.Err
} else {
return result.Data.(*model.ChannelList), nil

View File

@@ -175,7 +175,7 @@ func TestJoinDefaultChannelsExperimentalDefaultChannels(t *testing.T) {
th.App.JoinDefaultChannels(th.BasicTeam.Id, user, false, "")
for _, channelName := range defaultChannelList {
channel, err := th.App.GetChannelByName(channelName, th.BasicTeam.Id)
channel, err := th.App.GetChannelByName(channelName, th.BasicTeam.Id, false)
if err != nil {
t.Errorf("Expected nil, got %s", err)

View File

@@ -59,7 +59,7 @@ func (me *InviteProvider) DoCommand(a *App, args *model.CommandArgs, message str
if len(splitMessage) > 1 && splitMessage[1] != "" {
targetChannelName := strings.TrimPrefix(strings.TrimSpace(splitMessage[1]), "~")
if channelToJoin, err = a.GetChannelByName(targetChannelName, args.TeamId); err != nil {
if channelToJoin, err = a.GetChannelByName(targetChannelName, args.TeamId, false); err != nil {
return &model.CommandResponse{Text: args.T("api.command_invite.channel.error", map[string]interface{}{"Channel": targetChannelName}), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
} else {

View File

@@ -1116,7 +1116,7 @@ func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *mod
a.UpdateTeamMemberSchemeRoles(team.Id, user.Id, isSchemeUser, isSchemeAdmin)
}
if defaultChannel, err := a.GetChannelByName(model.DEFAULT_CHANNEL, team.Id); err != nil {
if defaultChannel, err := a.GetChannelByName(model.DEFAULT_CHANNEL, team.Id, true); err != nil {
return err
} else if _, err = a.addUserToChannel(user, defaultChannel, member); err != nil {
return err
@@ -1139,7 +1139,7 @@ func (a *App) ImportUserChannels(user *model.User, team *model.Team, teamMember
// Loop through all channels.
for _, cdata := range *data {
channel, err := a.GetChannelByName(*cdata.Name, team.Id)
channel, err := a.GetChannelByName(*cdata.Name, team.Id, true)
if err != nil {
return err
}

View File

@@ -1898,7 +1898,7 @@ func TestImportImportChannel(t *testing.T) {
th.CheckChannelsCount(t, channelCount+1)
// Get the Channel and check all the fields are correct.
if channel, err := th.App.GetChannelByName(*data.Name, team.Id); err != nil {
if channel, err := th.App.GetChannelByName(*data.Name, team.Id, false); err != nil {
t.Fatalf("Failed to get channel from database.")
} else {
assert.Equal(t, *data.Name, channel.Name)
@@ -1923,7 +1923,7 @@ func TestImportImportChannel(t *testing.T) {
th.CheckChannelsCount(t, channelCount)
// Get the Channel and check all the fields are correct.
if channel, err := th.App.GetChannelByName(*data.Name, team.Id); err != nil {
if channel, err := th.App.GetChannelByName(*data.Name, team.Id, false); err != nil {
t.Fatalf("Failed to get channel from database.")
} else {
assert.Equal(t, *data.Name, channel.Name)
@@ -2157,7 +2157,7 @@ func TestImportImportUser(t *testing.T) {
DisplayName: ptrStr("Display Name"),
Type: ptrStr("O"),
}, false)
channel, err := th.App.GetChannelByName(channelName, team.Id)
channel, err := th.App.GetChannelByName(channelName, team.Id, false)
if err != nil {
t.Fatalf("Failed to get channel from database.")
}
@@ -2671,7 +2671,7 @@ func TestImportImportUser(t *testing.T) {
if err := th.App.ImportChannel(channelData, false); err != nil {
t.Fatalf("Import should have succeeded.")
}
channel, err = th.App.GetChannelByName(*channelData.Name, team.Id)
channel, err = th.App.GetChannelByName(*channelData.Name, team.Id, false)
if err != nil {
t.Fatalf("Failed to get channel from database: %v", err.Error())
}
@@ -2755,7 +2755,7 @@ func TestImportImportPost(t *testing.T) {
DisplayName: ptrStr("Display Name"),
Type: ptrStr("O"),
}, false)
channel, err := th.App.GetChannelByName(channelName, team.Id)
channel, err := th.App.GetChannelByName(channelName, team.Id, false)
if err != nil {
t.Fatalf("Failed to get channel from database.")
}
@@ -3959,7 +3959,7 @@ func TestImportPostAndRepliesWithAttachments(t *testing.T) {
DisplayName: ptrStr("Display Name"),
Type: ptrStr("O"),
}, false)
_, err = th.App.GetChannelByName(channelName, team.Id)
_, err = th.App.GetChannelByName(channelName, team.Id, false)
if err != nil {
t.Fatalf("Failed to get channel from database.")
}

View File

@@ -195,12 +195,12 @@ func (api *PluginAPI) GetChannel(channelId string) (*model.Channel, *model.AppEr
return api.app.GetChannel(channelId)
}
func (api *PluginAPI) GetChannelByName(teamId, name string) (*model.Channel, *model.AppError) {
return api.app.GetChannelByName(name, teamId)
func (api *PluginAPI) GetChannelByName(teamId, name string, includeDeleted bool) (*model.Channel, *model.AppError) {
return api.app.GetChannelByName(name, teamId, includeDeleted)
}
func (api *PluginAPI) GetChannelByNameForTeamName(teamName, channelName string) (*model.Channel, *model.AppError) {
return api.app.GetChannelByNameForTeamName(channelName, teamName)
func (api *PluginAPI) GetChannelByNameForTeamName(teamName, channelName string, includeDeleted bool) (*model.Channel, *model.AppError) {
return api.app.GetChannelByNameForTeamName(channelName, teamName, includeDeleted)
}
func (api *PluginAPI) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError) {

View File

@@ -638,8 +638,9 @@ func (a *App) DeletePostFiles(post *model.Post) {
}
}
func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOrSearch bool) (*model.PostSearchResults, *model.AppError) {
func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOrSearch bool, includeDeletedChannels bool) (*model.PostSearchResults, *model.AppError) {
paramsList := model.ParseSearchParams(terms)
includeDeleted := includeDeletedChannels && *a.Config().TeamSettings.ViewArchivedChannels
esInterface := a.Elasticsearch
if license := a.License(); esInterface != nil && *a.Config().ElasticsearchSettings.EnableSearching && license != nil && *license.Features.Elasticsearch {
@@ -651,7 +652,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
if params.Terms != "*" {
// Convert channel names to channel IDs
for idx, channelName := range params.InChannels {
if channel, err := a.GetChannelByName(channelName, teamId); err != nil {
if channel, err := a.GetChannelByName(channelName, teamId, includeDeleted); err != nil {
mlog.Error(fmt.Sprint(err))
} else {
params.InChannels[idx] = channel.Id
@@ -677,7 +678,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
}
// We only allow the user to search in channels they are a member of.
userChannels, err := a.GetChannelsForUser(teamId, userId)
userChannels, err := a.GetChannelsForUser(teamId, userId, includeDeleted)
if err != nil {
mlog.Error(fmt.Sprint(err))
return nil, err
@@ -710,6 +711,7 @@ func (a *App) SearchPostsInTeam(terms string, userId string, teamId string, isOr
channels := []store.StoreChannel{}
for _, params := range paramsList {
params.IncludeDeletedChannels = includeDeleted
params.OrTerms = isOrSearch
// don't allow users to search for everything
if params.Terms != "*" {

View File

@@ -715,7 +715,7 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
var channelList *model.ChannelList
if result := <-a.Srv.Store.Channel().GetChannels(team.Id, user.Id); result.Err != nil {
if result := <-a.Srv.Store.Channel().GetChannels(team.Id, user.Id, true); result.Err != nil {
if result.Err.Id == "store.sql_channel.get_channels.not_found.app_error" {
channelList = &model.ChannelList{}
} else {

View File

@@ -901,7 +901,7 @@ func (a *App) UpdateActive(user *model.User, active bool) (*model.User, *model.A
}
for _, team := range teamsForUser {
channelsForUser, err := a.GetChannelsForUser(team.Id, user.Id)
channelsForUser, err := a.GetChannelsForUser(team.Id, user.Id, false)
if err != nil {
return nil, err
}

View File

@@ -334,7 +334,7 @@ func (webCon *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool {
}
if webCon.AllChannelMembers == nil {
if result := <-webCon.App.Srv.Store.Channel().GetAllChannelMembersForUser(webCon.UserId, true); result.Err != nil {
if result := <-webCon.App.Srv.Store.Channel().GetAllChannelMembersForUser(webCon.UserId, true, false); result.Err != nil {
mlog.Error("webhub.shouldSendEvent: " + result.Err.Error())
return false
} else {

View File

@@ -101,6 +101,7 @@
"MaxNotificationsPerChannel": 1000,
"EnableConfirmNotificationsToChannel": true,
"TeammateNameDisplay": "username",
"ViewArchivedChannels": true,
"ExperimentalEnableAutomaticReplies": false,
"ExperimentalHideTownSquareinLHS": false,
"ExperimentalTownSquareIsReadOnly": false,

View File

@@ -154,7 +154,7 @@ func manualTest(c *web.Context, w http.ResponseWriter, r *http.Request) {
func getChannelID(a *app.App, channelname string, teamid string, userid string) (id string, err bool) {
// Grab all the channels
result := <-a.Srv.Store.Channel().GetChannels(teamid, userid)
result := <-a.Srv.Store.Channel().GetChannels(teamid, userid, false)
if result.Err != nil {
mlog.Debug("Unable to get channels")
return "", false

View File

@@ -1791,6 +1791,15 @@ func (c *Client4) GetChannelByName(channelName, teamId string, etag string) (*Ch
}
}
func (c *Client4) GetChannelByNameIncludeDeleted(channelName, teamId string, etag string) (*Channel, *Response) {
if r, err := c.DoApiGet(c.GetChannelByNameRoute(channelName, teamId)+"?include_deleted=true", etag); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
return ChannelFromJson(r.Body), BuildResponse(r)
}
}
// GetChannelByNameForTeamName returns a channel based on the provided channel name and team name strings.
func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag string) (*Channel, *Response) {
if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName), etag); err != nil {
@@ -1801,6 +1810,15 @@ func (c *Client4) GetChannelByNameForTeamName(channelName, teamName string, etag
}
}
func (c *Client4) GetChannelByNameForTeamNameIncludeDeleted(channelName, teamName string, etag string) (*Channel, *Response) {
if r, err := c.DoApiGet(c.GetChannelByNameForTeamNameRoute(channelName, teamName)+"?include_deleted=true", etag); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
return ChannelFromJson(r.Body), BuildResponse(r)
}
}
// GetChannelMembers gets a page of channel members.
func (c *Client4) GetChannelMembers(channelId string, page, perPage int, etag string) (*ChannelMembers, *Response) {
query := fmt.Sprintf("?page=%v&per_page=%v", page, perPage)
@@ -2127,6 +2145,17 @@ func (c *Client4) SearchPosts(teamId string, terms string, isOrSearch bool) (*Po
}
}
// SearchPosts returns any posts with matching terms string including deleted channels.
func (c *Client4) SearchPostsIncludeDeletedChannels(teamId string, terms string, isOrSearch bool) (*PostList, *Response) {
requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch}
if r, err := c.DoApiPost(c.GetTeamRoute(teamId)+"/posts/search?include_deleted_channels=true", StringInterfaceToJson(requestBody)); err != nil {
return nil, BuildErrorResponse(r, err)
} else {
defer closeBody(r)
return PostListFromJson(r.Body), BuildResponse(r)
}
}
// SearchPosts returns any posts with matching terms string, including .
func (c *Client4) SearchPostsWithMatches(teamId string, terms string, isOrSearch bool) (*PostSearchResults, *Response) {
requestBody := map[string]interface{}{"terms": terms, "is_or_search": isOrSearch}

View File

@@ -1125,6 +1125,7 @@ type TeamSettings struct {
MaxNotificationsPerChannel *int64
EnableConfirmNotificationsToChannel *bool
TeammateNameDisplay *string
ViewArchivedChannels *bool
ExperimentalEnableAutomaticReplies *bool
ExperimentalHideTownSquareinLHS *bool
ExperimentalTownSquareIsReadOnly *bool
@@ -1254,6 +1255,9 @@ func (s *TeamSettings) SetDefaults() {
s.EnableUserCreation = NewBool(true)
}
if s.ViewArchivedChannels == nil {
s.ViewArchivedChannels = NewBool(true)
}
}
type ClientRequirements struct {

View File

@@ -12,11 +12,12 @@ var searchTermPuncStart = regexp.MustCompile(`^[^\pL\d\s#"]+`)
var searchTermPuncEnd = regexp.MustCompile(`[^\pL\d\s*"]+$`)
type SearchParams struct {
Terms string
IsHashtag bool
InChannels []string
FromUsers []string
OrTerms bool
Terms string
IsHashtag bool
InChannels []string
FromUsers []string
OrTerms bool
IncludeDeletedChannels bool
}
var searchFlags = [...]string{"from", "channel", "in"}

View File

@@ -108,10 +108,10 @@ type API interface {
GetChannel(channelId string) (*model.Channel, *model.AppError)
// GetChannelByName gets a channel by its name, given a team id.
GetChannelByName(teamId, name string) (*model.Channel, *model.AppError)
GetChannelByName(teamId, name string, includeDeleted bool) (*model.Channel, *model.AppError)
// GetChannelByNameForTeamName gets a channel by its name, given a team name.
GetChannelByNameForTeamName(teamName, channelName string) (*model.Channel, *model.AppError)
GetChannelByNameForTeamName(teamName, channelName string, includeDeleted bool) (*model.Channel, *model.AppError)
// GetDirectChannel gets a direct message channel.
GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.AppError)

View File

@@ -1349,6 +1349,7 @@ func (s *apiRPCServer) GetChannel(args *Z_GetChannelArgs, returns *Z_GetChannelR
type Z_GetChannelByNameArgs struct {
A string
B string
C bool
}
type Z_GetChannelByNameReturns struct {
@@ -1356,8 +1357,8 @@ type Z_GetChannelByNameReturns struct {
B *model.AppError
}
func (g *apiRPCClient) GetChannelByName(teamId, name string) (*model.Channel, *model.AppError) {
_args := &Z_GetChannelByNameArgs{teamId, name}
func (g *apiRPCClient) GetChannelByName(teamId, name string, includeDeleted bool) (*model.Channel, *model.AppError) {
_args := &Z_GetChannelByNameArgs{teamId, name, includeDeleted}
_returns := &Z_GetChannelByNameReturns{}
if err := g.client.Call("Plugin.GetChannelByName", _args, _returns); err != nil {
log.Printf("RPC call to GetChannelByName API failed: %s", err.Error())
@@ -1367,9 +1368,9 @@ func (g *apiRPCClient) GetChannelByName(teamId, name string) (*model.Channel, *m
func (s *apiRPCServer) GetChannelByName(args *Z_GetChannelByNameArgs, returns *Z_GetChannelByNameReturns) error {
if hook, ok := s.impl.(interface {
GetChannelByName(teamId, name string) (*model.Channel, *model.AppError)
GetChannelByName(teamId, name string, includeDeleted bool) (*model.Channel, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetChannelByName(args.A, args.B)
returns.A, returns.B = hook.GetChannelByName(args.A, args.B, args.C)
} else {
return fmt.Errorf("API GetChannelByName called but not implemented.")
}
@@ -1379,6 +1380,7 @@ func (s *apiRPCServer) GetChannelByName(args *Z_GetChannelByNameArgs, returns *Z
type Z_GetChannelByNameForTeamNameArgs struct {
A string
B string
C bool
}
type Z_GetChannelByNameForTeamNameReturns struct {
@@ -1386,8 +1388,8 @@ type Z_GetChannelByNameForTeamNameReturns struct {
B *model.AppError
}
func (g *apiRPCClient) GetChannelByNameForTeamName(teamName, channelName string) (*model.Channel, *model.AppError) {
_args := &Z_GetChannelByNameForTeamNameArgs{teamName, channelName}
func (g *apiRPCClient) GetChannelByNameForTeamName(teamName, channelName string, includeDeleted bool) (*model.Channel, *model.AppError) {
_args := &Z_GetChannelByNameForTeamNameArgs{teamName, channelName, includeDeleted}
_returns := &Z_GetChannelByNameForTeamNameReturns{}
if err := g.client.Call("Plugin.GetChannelByNameForTeamName", _args, _returns); err != nil {
log.Printf("RPC call to GetChannelByNameForTeamName API failed: %s", err.Error())
@@ -1397,9 +1399,9 @@ func (g *apiRPCClient) GetChannelByNameForTeamName(teamName, channelName string)
func (s *apiRPCServer) GetChannelByNameForTeamName(args *Z_GetChannelByNameForTeamNameArgs, returns *Z_GetChannelByNameForTeamNameReturns) error {
if hook, ok := s.impl.(interface {
GetChannelByNameForTeamName(teamName, channelName string) (*model.Channel, *model.AppError)
GetChannelByNameForTeamName(teamName, channelName string, includeDeleted bool) (*model.Channel, *model.AppError)
}); ok {
returns.A, returns.B = hook.GetChannelByNameForTeamName(args.A, args.B)
returns.A, returns.B = hook.GetChannelByNameForTeamName(args.A, args.B, args.C)
} else {
return fmt.Errorf("API GetChannelByNameForTeamName called but not implemented.")
}

View File

@@ -30,7 +30,7 @@ func (p *HelpPlugin) OnConfigurationChange() error {
return nil
}
channel, err := p.API.GetChannelByName(p.ChannelName, team.Id)
channel, err := p.API.GetChannelByName(p.ChannelName, team.Id, false)
if err != nil {
p.API.LogError("failed to find channel", "channel_name", p.ChannelName)
return nil

View File

@@ -308,13 +308,13 @@ func (_m *API) GetChannel(channelId string) (*model.Channel, *model.AppError) {
return r0, r1
}
// GetChannelByName provides a mock function with given fields: teamId, name
func (_m *API) GetChannelByName(teamId string, name string) (*model.Channel, *model.AppError) {
ret := _m.Called(teamId, name)
// GetChannelByName provides a mock function with given fields: teamId, name, includeDeleted
func (_m *API) GetChannelByName(teamId string, name string, includeDeleted bool) (*model.Channel, *model.AppError) {
ret := _m.Called(teamId, name, includeDeleted)
var r0 *model.Channel
if rf, ok := ret.Get(0).(func(string, string) *model.Channel); ok {
r0 = rf(teamId, name)
if rf, ok := ret.Get(0).(func(string, string, bool) *model.Channel); ok {
r0 = rf(teamId, name, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Channel)
@@ -322,8 +322,8 @@ func (_m *API) GetChannelByName(teamId string, name string) (*model.Channel, *mo
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok {
r1 = rf(teamId, name)
if rf, ok := ret.Get(1).(func(string, string, bool) *model.AppError); ok {
r1 = rf(teamId, name, includeDeleted)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
@@ -333,13 +333,13 @@ func (_m *API) GetChannelByName(teamId string, name string) (*model.Channel, *mo
return r0, r1
}
// GetChannelByNameForTeamName provides a mock function with given fields: teamName, channelName
func (_m *API) GetChannelByNameForTeamName(teamName string, channelName string) (*model.Channel, *model.AppError) {
ret := _m.Called(teamName, channelName)
// GetChannelByNameForTeamName provides a mock function with given fields: teamName, channelName, includeDeleted
func (_m *API) GetChannelByNameForTeamName(teamName string, channelName string, includeDeleted bool) (*model.Channel, *model.AppError) {
ret := _m.Called(teamName, channelName, includeDeleted)
var r0 *model.Channel
if rf, ok := ret.Get(0).(func(string, string) *model.Channel); ok {
r0 = rf(teamName, channelName)
if rf, ok := ret.Get(0).(func(string, string, bool) *model.Channel); ok {
r0 = rf(teamName, channelName, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Channel)
@@ -347,8 +347,8 @@ func (_m *API) GetChannelByNameForTeamName(teamName string, channelName string)
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok {
r1 = rf(teamName, channelName)
if rf, ok := ret.Get(1).(func(string, string, bool) *model.AppError); ok {
r1 = rf(teamName, channelName, includeDeleted)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)

View File

@@ -303,6 +303,11 @@ func (s SqlChannelStore) CreateIndexesIfNotExists() {
func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if channel.DeleteAt != 0 {
result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.archived_channel.app_error", nil, "", http.StatusBadRequest)
return
}
if channel.Type == model.CHANNEL_DIRECT {
result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.direct_channel.app_error", nil, "", http.StatusBadRequest)
return
@@ -352,6 +357,11 @@ func (s SqlChannelStore) CreateDirectChannel(userId string, otherUserId string)
func (s SqlChannelStore) SaveDirectChannel(directchannel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if directchannel.DeleteAt != 0 {
result.Err = model.NewAppError("SqlChannelStore.Save", "store.sql_channel.save.archived_channel.app_error", nil, "", http.StatusBadRequest)
return
}
if directchannel.Type != model.CHANNEL_DIRECT {
result.Err = model.NewAppError("SqlChannelStore.SaveDirectChannel", "store.sql_channel.save_direct_channel.not_direct.app_error", nil, "", http.StatusBadRequest)
return
@@ -629,10 +639,14 @@ func (s SqlChannelStore) PermanentDeleteMembersByChannel(channelId string) store
})
}
func (s SqlChannelStore) GetChannels(teamId string, userId string) store.StoreChannel {
func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDeleted bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := "SELECT Channels.* FROM Channels, ChannelMembers WHERE Id = ChannelId AND UserId = :UserId AND DeleteAt = 0 AND (TeamId = :TeamId OR TeamId = '') ORDER BY DisplayName"
if includeDeleted {
query = "SELECT Channels.* FROM Channels, ChannelMembers WHERE Id = ChannelId AND UserId = :UserId AND (TeamId = :TeamId OR TeamId = '') ORDER BY DisplayName"
}
data := &model.ChannelList{}
_, err := s.GetReplica().Select(data, "SELECT Channels.* FROM Channels, ChannelMembers WHERE Id = ChannelId AND UserId = :UserId AND DeleteAt = 0 AND (TeamId = :TeamId OR TeamId = '') ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId})
_, err := s.GetReplica().Select(data, query, map[string]interface{}{"TeamId": teamId, "UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetChannels", "store.sql_channel.get_channels.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
@@ -1100,7 +1114,7 @@ func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string
s.metrics.IncrementMemCacheMissCounter("All Channel Members for User")
}
result := <-s.GetAllChannelMembersForUser(userId, true)
result := <-s.GetAllChannelMembersForUser(userId, true, false)
if result.Err != nil {
mlog.Error("SqlChannelStore.IsUserInChannelUseCache: " + result.Err.Error())
return false
@@ -1147,7 +1161,7 @@ func (s SqlChannelStore) GetMemberForPost(postId string, userId string) store.St
})
}
func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool) store.StoreChannel {
func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if allowFromCache {
if cacheItem, ok := allChannelMembersForUserCache.Get(userId); ok {
@@ -1163,6 +1177,11 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac
s.metrics.IncrementMemCacheMissCounter("All Channel Members for User")
}
var deletedClause string
if !includeDeleted {
deletedClause = "Channels.DeleteAt = 0 AND"
}
var data allChannelMembers
_, err := s.GetReplica().Select(&data, `
SELECT
@@ -1182,8 +1201,8 @@ func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCac
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
WHERE
Channels.DeleteAt = 0
AND ChannelMembers.UserId = :UserId`, map[string]interface{}{"UserId": userId})
`+deletedClause+`
ChannelMembers.UserId = :UserId`, map[string]interface{}{"UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.GetAllChannelMembersForUser", "store.sql_channel.get_channels.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError)
@@ -1515,8 +1534,13 @@ func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) store.S
})
}
func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string) store.StoreChannel {
func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
deleteFilter := "AND DeleteAt = 0"
if includeDeleted {
deleteFilter = ""
}
queryFormat := `
SELECT
*
@@ -1525,7 +1549,7 @@ func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string) store.St
WHERE
TeamId = :TeamId
AND Type = 'O'
AND DeleteAt = 0
` + deleteFilter + `
%v
LIMIT 50`
@@ -1555,8 +1579,12 @@ func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string) store.St
})
}
func (s SqlChannelStore) SearchInTeam(teamId string, term string) store.StoreChannel {
func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
deleteFilter := "AND DeleteAt = 0"
if includeDeleted {
deleteFilter = ""
}
searchQuery := `
SELECT
*
@@ -1565,7 +1593,7 @@ func (s SqlChannelStore) SearchInTeam(teamId string, term string) store.StoreCha
WHERE
TeamId = :TeamId
AND Type = 'O'
AND DeleteAt = 0
` + deleteFilter + `
SEARCH_CLAUSE
ORDER BY DisplayName
LIMIT 100`

View File

@@ -803,6 +803,11 @@ func (s *SqlPostStore) Search(teamId string, userId string, params *model.Search
var posts []*model.Post
deletedQueryPart := "AND DeleteAt = 0"
if params.IncludeDeletedChannels {
deletedQueryPart = ""
}
searchQuery := `
SELECT
*
@@ -822,7 +827,7 @@ func (s *SqlPostStore) Search(teamId string, userId string, params *model.Search
Id = ChannelId
AND (TeamId = :TeamId OR TeamId = '')
AND UserId = :UserId
AND DeleteAt = 0
` + deletedQueryPart + `
CHANNEL_FILTER)
SEARCH_CLAUSE
ORDER BY CreateAt DESC

View File

@@ -131,7 +131,7 @@ type ChannelStore interface {
GetByNameIncludeDeleted(team_id string, name string, allowFromCache bool) StoreChannel
GetDeletedByName(team_id string, name string) StoreChannel
GetDeleted(team_id string, offset int, limit int) StoreChannel
GetChannels(teamId string, userId string) StoreChannel
GetChannels(teamId string, userId string, includeDeleted bool) StoreChannel
GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
@@ -143,7 +143,7 @@ type ChannelStore interface {
UpdateMember(member *model.ChannelMember) StoreChannel
GetMembers(channelId string, offset, limit int) StoreChannel
GetMember(channelId string, userId string) StoreChannel
GetAllChannelMembersForUser(userId string, allowFromCache bool) StoreChannel
GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) StoreChannel
InvalidateAllChannelMembersForUser(userId string)
IsUserInChannelUseCache(userId string, channelId string) bool
GetAllChannelMembersNotifyPropsForChannel(channelId string, allowFromCache bool) StoreChannel
@@ -160,8 +160,8 @@ type ChannelStore interface {
IncrementMentionCount(channelId string, userId string) StoreChannel
AnalyticsTypeCount(teamId string, channelType string) StoreChannel
GetMembersForUser(teamId string, userId string) StoreChannel
AutocompleteInTeam(teamId string, term string) StoreChannel
SearchInTeam(teamId string, term string) StoreChannel
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
SearchMore(userId string, teamId string, term string) StoreChannel
GetMembersByIds(channelId string, userIds []string) StoreChannel
AnalyticsDeletedTypeCount(teamId string, channelType string) StoreChannel

View File

@@ -493,7 +493,7 @@ func testChannelStoreDelete(t *testing.T, ss store.Store) {
t.Fatal(r.Err)
}
cresult := <-ss.Channel().GetChannels(o1.TeamId, m1.UserId)
cresult := <-ss.Channel().GetChannels(o1.TeamId, m1.UserId, false)
list := cresult.Data.(*model.ChannelList)
if len(*list) != 1 {
@@ -509,7 +509,7 @@ func testChannelStoreDelete(t *testing.T, ss store.Store) {
<-ss.Channel().PermanentDelete(o2.Id)
cresult = <-ss.Channel().GetChannels(o1.TeamId, m1.UserId)
cresult = <-ss.Channel().GetChannels(o1.TeamId, m1.UserId, false)
t.Log(cresult.Err)
if cresult.Err.Id != "store.sql_channel.get_channels.not_found.app_error" {
t.Fatal("no channels should be found")
@@ -555,7 +555,7 @@ func testChannelStoreGetByName(t *testing.T, ss store.Store) {
store.Must(ss.Channel().Delete(r1.Data.(*model.Channel).Id, model.GetMillis()))
if err := (<-ss.Channel().GetByName(o1.TeamId, "", false)).Err; err == nil {
if err := (<-ss.Channel().GetByName(o1.TeamId, r1.Data.(*model.Channel).Name, false)).Err; err == nil {
t.Fatal("Deleted channel should not be returned by GetByName()")
}
}
@@ -617,8 +617,11 @@ func testChannelStoreGetDeletedByName(t *testing.T, ss store.Store) {
o1.DisplayName = "Name"
o1.Name = "zz" + model.NewId() + "b"
o1.Type = model.CHANNEL_OPEN
o1.DeleteAt = model.GetMillis()
store.Must(ss.Channel().Save(&o1, -1))
now := model.GetMillis()
store.Must(ss.Channel().Delete(o1.Id, now))
o1.DeleteAt = now
o1.UpdateAt = now
if r1 := <-ss.Channel().GetDeletedByName(o1.TeamId, o1.Name); r1.Err != nil {
t.Fatal(r1.Err)
@@ -639,8 +642,8 @@ func testChannelStoreGetDeleted(t *testing.T, ss store.Store) {
o1.DisplayName = "Channel1"
o1.Name = "zz" + model.NewId() + "b"
o1.Type = model.CHANNEL_OPEN
o1.DeleteAt = model.GetMillis()
store.Must(ss.Channel().Save(&o1, -1))
store.Must(ss.Channel().Delete(o1.Id, model.GetMillis()))
cresult := <-ss.Channel().GetDeleted(o1.TeamId, 0, 100)
if cresult.Err != nil {
@@ -678,8 +681,8 @@ func testChannelStoreGetDeleted(t *testing.T, ss store.Store) {
o3.DisplayName = "Channel3"
o3.Name = "zz" + model.NewId() + "b"
o3.Type = model.CHANNEL_OPEN
o3.DeleteAt = model.GetMillis()
store.Must(ss.Channel().Save(&o3, -1))
store.Must(ss.Channel().SetDeleteAt(o3.Id, model.GetMillis(), model.GetMillis()))
cresult = <-ss.Channel().GetDeleted(o1.TeamId, 0, 100)
if cresult.Err != nil {
@@ -890,26 +893,26 @@ func testChannelStoreGetChannels(t *testing.T, ss store.Store) {
m3.NotifyProps = model.GetDefaultChannelNotifyProps()
store.Must(ss.Channel().SaveMember(&m3))
cresult := <-ss.Channel().GetChannels(o1.TeamId, m1.UserId)
cresult := <-ss.Channel().GetChannels(o1.TeamId, m1.UserId, false)
list := cresult.Data.(*model.ChannelList)
if (*list)[0].Id != o1.Id {
t.Fatal("missing channel")
}
acresult := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, false)
acresult := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, false, false)
ids := acresult.Data.(map[string]string)
if _, ok := ids[o1.Id]; !ok {
t.Fatal("missing channel")
}
acresult2 := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, true)
acresult2 := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, true, false)
ids2 := acresult2.Data.(map[string]string)
if _, ok := ids2[o1.Id]; !ok {
t.Fatal("missing channel")
}
acresult3 := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, true)
acresult3 := <-ss.Channel().GetAllChannelMembersForUser(m1.UserId, true, false)
ids3 := acresult3.Data.(map[string]string)
if _, ok := ids3[o1.Id]; !ok {
t.Fatal("missing channel")
@@ -1913,12 +1916,12 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
o12.Type = model.CHANNEL_OPEN
store.Must(ss.Channel().Save(&o12, -1))
for name, search := range map[string]func(teamId string, term string) store.StoreChannel{
for name, search := range map[string]func(teamId string, term string, includeDeleted bool) store.StoreChannel{
"AutocompleteInTeam": ss.Channel().AutocompleteInTeam,
"SearchInTeam": ss.Channel().SearchInTeam,
} {
t.Run(name, func(t *testing.T) {
if result := <-search(o1.TeamId, "ChannelA"); result.Err != nil {
if result := <-search(o1.TeamId, "ChannelA", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -1927,7 +1930,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, ""); result.Err != nil {
if result := <-search(o1.TeamId, "", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -1936,7 +1939,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "blargh"); result.Err != nil {
if result := <-search(o1.TeamId, "blargh", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -1945,7 +1948,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "off-"); result.Err != nil {
if result := <-search(o1.TeamId, "off-", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -1962,7 +1965,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "off-topic"); result.Err != nil {
if result := <-search(o1.TeamId, "off-topic", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -1975,7 +1978,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "town square"); result.Err != nil {
if result := <-search(o1.TeamId, "town square", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -1988,7 +1991,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "the"); result.Err != nil {
if result := <-search(o1.TeamId, "the", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -2002,7 +2005,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "Mobile"); result.Err != nil {
if result := <-search(o1.TeamId, "Mobile", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -2016,7 +2019,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "now searchable"); result.Err != nil {
if result := <-search(o1.TeamId, "now searchable", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)
@@ -2029,7 +2032,7 @@ func testChannelStoreSearchInTeam(t *testing.T, ss store.Store) {
}
}
if result := <-search(o1.TeamId, "town square |"); result.Err != nil {
if result := <-search(o1.TeamId, "town square |", false); result.Err != nil {
t.Fatal(result.Err)
} else {
channels := result.Data.(*model.ChannelList)

View File

@@ -46,12 +46,12 @@ func (_m *ChannelStore) AnalyticsTypeCount(teamId string, channelType string) st
}
// AutocompleteInTeam provides a mock function with given fields: teamId, term
func (_m *ChannelStore) AutocompleteInTeam(teamId string, term string) store.StoreChannel {
func (_m *ChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
ret := _m.Called(teamId, term)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
r0 = rf(teamId, term)
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
r0 = rf(teamId, term, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
@@ -147,12 +147,12 @@ func (_m *ChannelStore) GetAll(teamId string) store.StoreChannel {
}
// GetAllChannelMembersForUser provides a mock function with given fields: userId, allowFromCache
func (_m *ChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool) store.StoreChannel {
func (_m *ChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) store.StoreChannel {
ret := _m.Called(userId, allowFromCache)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, bool) store.StoreChannel); ok {
r0 = rf(userId, allowFromCache)
if rf, ok := ret.Get(0).(func(string, bool, bool) store.StoreChannel); ok {
r0 = rf(userId, allowFromCache, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
@@ -259,12 +259,12 @@ func (_m *ChannelStore) GetChannelUnread(channelId string, userId string) store.
}
// GetChannels provides a mock function with given fields: teamId, userId
func (_m *ChannelStore) GetChannels(teamId string, userId string) store.StoreChannel {
func (_m *ChannelStore) GetChannels(teamId string, userId string, includeDeleted bool) store.StoreChannel {
ret := _m.Called(teamId, userId)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
r0 = rf(teamId, userId)
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
r0 = rf(teamId, userId, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
@@ -776,12 +776,12 @@ func (_m *ChannelStore) SaveMember(member *model.ChannelMember) store.StoreChann
}
// SearchInTeam provides a mock function with given fields: teamId, term
func (_m *ChannelStore) SearchInTeam(teamId string, term string) store.StoreChannel {
func (_m *ChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
ret := _m.Called(teamId, term)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, string) store.StoreChannel); ok {
r0 = rf(teamId, term)
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
r0 = rf(teamId, term, includeDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)

View File

@@ -13,6 +13,7 @@ import (
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPostStore(t *testing.T, ss store.Store) {
@@ -839,6 +840,20 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
c2.Type = model.CHANNEL_OPEN
c2 = (<-ss.Channel().Save(c2, -1)).Data.(*model.Channel)
c3 := &model.Channel{}
c3.TeamId = teamId
c3.DisplayName = "Channel1"
c3.Name = "zz" + model.NewId() + "b"
c3.Type = model.CHANNEL_OPEN
c3 = (<-ss.Channel().Save(c3, -1)).Data.(*model.Channel)
<-ss.Channel().Delete(c3.Id, model.GetMillis())
m3 := model.ChannelMember{}
m3.ChannelId = c3.Id
m3.UserId = userId
m3.NotifyProps = model.GetDefaultChannelNotifyProps()
store.Must(ss.Channel().SaveMember(&m3))
o1 := &model.Post{}
o1.ChannelId = c1.Id
o1.UserId = model.NewId()
@@ -877,69 +892,129 @@ func testPostStoreSearch(t *testing.T, ss store.Store) {
o5.Hashtags = "#secret #howdy"
o5 = (<-ss.Post().Save(o5)).Data.(*model.Post)
r1 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "corey", IsHashtag: false})).Data.(*model.PostList)
if len(r1.Order) != 1 || r1.Order[0] != o1.Id {
t.Fatal("returned wrong search result")
}
o6 := &model.Post{}
o6.ChannelId = c3.Id
o6.UserId = model.NewId()
o6.Hashtags = "#hashtag"
o6 = (<-ss.Post().Save(o6)).Data.(*model.Post)
r3 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "new", IsHashtag: false})).Data.(*model.PostList)
if len(r3.Order) != 2 || (r3.Order[0] != o1.Id && r3.Order[1] != o1.Id) {
t.Fatal("returned wrong search result")
}
o7 := &model.Post{}
o7.ChannelId = c3.Id
o7.UserId = model.NewId()
o7.Message = "New Jersey is where John is from corey new york"
o7 = (<-ss.Post().Save(o7)).Data.(*model.Post)
r4 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "john", IsHashtag: false})).Data.(*model.PostList)
if len(r4.Order) != 1 || r4.Order[0] != o2.Id {
t.Fatal("returned wrong search result")
}
o8 := &model.Post{}
o8.ChannelId = c3.Id
o8.UserId = model.NewId()
o8.Message = "Deleted"
o8 = (<-ss.Post().Save(o8)).Data.(*model.Post)
r5 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "matter*", IsHashtag: false})).Data.(*model.PostList)
if len(r5.Order) != 1 || r5.Order[0] != o1.Id {
t.Fatal("returned wrong search result")
tt := []struct {
name string
searchParams *model.SearchParams
extectedResultsCount int
expectedMessageResultIds []string
}{
{
"normal-search-1",
&model.SearchParams{Terms: "corey"},
1,
[]string{o1.Id},
},
{
"normal-search-2",
&model.SearchParams{Terms: "new"},
2,
[]string{o1.Id, o2.Id},
},
{
"normal-search-3",
&model.SearchParams{Terms: "john"},
1,
[]string{o2.Id},
},
{
"wildcard-search",
&model.SearchParams{Terms: "matter*"},
1,
[]string{o1.Id},
},
{
"hashtag-search",
&model.SearchParams{Terms: "#hashtag", IsHashtag: true},
1,
[]string{o4.Id},
},
{
"hashtag-search-2",
&model.SearchParams{Terms: "#secret", IsHashtag: true},
1,
[]string{o5.Id},
},
{
"no-match-mention",
&model.SearchParams{Terms: "@thisshouldmatchnothing", IsHashtag: true},
0,
[]string{},
},
{
"no-results-search",
&model.SearchParams{Terms: "mattermost jersey"},
0,
[]string{},
},
{
"multiple-words-search",
&model.SearchParams{Terms: "corey new york"},
1,
[]string{o1.Id},
},
{
"multiple-wildcard-search",
&model.SearchParams{Terms: "matter* jer*"},
0,
[]string{},
},
{
"search-with-work-next-to-a-symbol",
&model.SearchParams{Terms: "message blargh"},
1,
[]string{o4.Id},
},
{
"search-with-or",
&model.SearchParams{Terms: "Jersey corey", OrTerms: true},
2,
[]string{o1.Id, o2.Id},
},
{
"search-with-or-and-deleted",
&model.SearchParams{Terms: "Jersey corey", OrTerms: true, IncludeDeletedChannels: true},
3,
[]string{o1.Id, o2.Id, o7.Id},
},
{
"search-hashtag-deleted",
&model.SearchParams{Terms: "#hashtag", IsHashtag: true, IncludeDeletedChannels: true},
2,
[]string{o4.Id, o6.Id},
},
{
"search-deleted-only",
&model.SearchParams{Terms: "Deleted", IncludeDeletedChannels: true},
1,
[]string{o8.Id},
},
}
r6 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "#hashtag", IsHashtag: true})).Data.(*model.PostList)
if len(r6.Order) != 1 || r6.Order[0] != o4.Id {
t.Fatal("returned wrong search result")
}
r7 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "#secret", IsHashtag: true})).Data.(*model.PostList)
if len(r7.Order) != 1 || r7.Order[0] != o5.Id {
t.Fatal("returned wrong search result")
}
r8 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "@thisshouldmatchnothing", IsHashtag: true})).Data.(*model.PostList)
if len(r8.Order) != 0 {
t.Fatal("returned wrong search result")
}
r9 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "mattermost jersey", IsHashtag: false})).Data.(*model.PostList)
if len(r9.Order) != 0 {
t.Fatal("returned wrong search result")
}
r9a := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "corey new york", IsHashtag: false})).Data.(*model.PostList)
if len(r9a.Order) != 1 {
t.Fatal("returned wrong search result")
}
r10 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "matter* jer*", IsHashtag: false})).Data.(*model.PostList)
if len(r10.Order) != 0 {
t.Fatal("returned wrong search result")
}
r11 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "message blargh", IsHashtag: false})).Data.(*model.PostList)
if len(r11.Order) != 1 {
t.Fatal("returned wrong search result")
}
r12 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "blargh>", IsHashtag: false})).Data.(*model.PostList)
if len(r12.Order) != 1 {
t.Fatal("returned wrong search result")
}
r13 := (<-ss.Post().Search(teamId, userId, &model.SearchParams{Terms: "Jersey corey", IsHashtag: false, OrTerms: true})).Data.(*model.PostList)
if len(r13.Order) != 2 {
t.Fatal("returned wrong search result")
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
result := (<-ss.Post().Search(teamId, userId, tc.searchParams)).Data.(*model.PostList)
require.Len(t, result.Order, tc.extectedResultsCount)
for _, expectedMessageResultId := range tc.expectedMessageResultIds {
assert.Contains(t, result.Order, expectedMessageResultId)
}
})
}
}

View File

@@ -512,6 +512,7 @@ func GenerateClientConfig(c *model.Config, diagnosticId string, license *model.L
props["EnableXToLeaveChannelsFromLHS"] = strconv.FormatBool(*c.TeamSettings.EnableXToLeaveChannelsFromLHS)
props["TeammateNameDisplay"] = *c.TeamSettings.TeammateNameDisplay
props["ExperimentalPrimaryTeam"] = *c.TeamSettings.ExperimentalPrimaryTeam
props["ViewArchivedChannels"] = strconv.FormatBool(*c.TeamSettings.ViewArchivedChannels)
props["EnableOAuthServiceProvider"] = strconv.FormatBool(c.ServiceSettings.EnableOAuthServiceProvider)
props["GoogleDeveloperKey"] = c.ServiceSettings.GoogleDeveloperKey