MM-15422: Adds new parameters for retrieving pages of channels. (#10903)
* MM-15422: Adds new parameters for retrieving pages of channels and searching channels. * MM-15422: Appends excluded channel names with defaults. Adds separate struct for data-tier option parameter.
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
124b371312
Коммит
e8af4872c6
@@ -503,7 +503,12 @@ func getAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channels, err := c.App.GetAllChannels(c.Params.Page, c.Params.PerPage, false)
|
opts := model.ChannelSearchOpts{
|
||||||
|
NotAssociatedToGroup: c.Params.NotAssociatedToGroup,
|
||||||
|
ExcludeDefaultChannels: c.Params.ExcludeDefaultChannels,
|
||||||
|
}
|
||||||
|
|
||||||
|
channels, err := c.App.GetAllChannels(c.Params.Page, c.Params.PerPage, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
@@ -727,9 +732,13 @@ func searchAllChannels(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
includeDeleted := r.URL.Query().Get("include_deleted") == "true"
|
opts := model.ChannelSearchOpts{
|
||||||
|
NotAssociatedToGroup: props.NotAssociatedToGroup,
|
||||||
|
ExcludeDefaultChannels: props.ExcludeDefaultChannels,
|
||||||
|
IncludeDeleted: r.URL.Query().Get("include_deleted") == "true",
|
||||||
|
}
|
||||||
|
|
||||||
channels, err := c.App.SearchAllChannels(props.Term, includeDeleted)
|
channels, err := c.App.SearchAllChannels(props.Term, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -16,23 +16,52 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/utils"
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *App) CreateDefaultChannels(teamId string) ([]*model.Channel, *model.AppError) {
|
// CreateDefaultChannels creates channels in the given team for each channel returned by (*App).DefaultChannelNames.
|
||||||
townSquare := &model.Channel{DisplayName: utils.T("api.channel.create_default_channels.town_square"), Name: "town-square", Type: model.CHANNEL_OPEN, TeamId: teamId}
|
//
|
||||||
|
func (a *App) CreateDefaultChannels(teamID string) ([]*model.Channel, *model.AppError) {
|
||||||
if _, err := a.CreateChannel(townSquare, false); err != nil {
|
displayNames := map[string]string{
|
||||||
|
"town-square": utils.T("api.channel.create_default_channels.town_square"),
|
||||||
|
"off-topic": utils.T("api.channel.create_default_channels.off_topic"),
|
||||||
|
}
|
||||||
|
channels := []*model.Channel{}
|
||||||
|
defaultChannelNames := a.DefaultChannelNames()
|
||||||
|
for _, name := range defaultChannelNames {
|
||||||
|
displayName := utils.TDefault(displayNames[name], name)
|
||||||
|
channel := &model.Channel{DisplayName: displayName, Name: name, Type: model.CHANNEL_OPEN, TeamId: teamID}
|
||||||
|
if _, err := a.CreateChannel(channel, false); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
channels = append(channels, channel)
|
||||||
offTopic := &model.Channel{DisplayName: utils.T("api.channel.create_default_channels.off_topic"), Name: "off-topic", Type: model.CHANNEL_OPEN, TeamId: teamId}
|
|
||||||
|
|
||||||
if _, err := a.CreateChannel(offTopic, false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
channels := []*model.Channel{townSquare, offTopic}
|
|
||||||
return channels, nil
|
return channels, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultChannelNames returns the list of system-wide default channel names.
|
||||||
|
//
|
||||||
|
// By default the list will be (not necessarily in this order):
|
||||||
|
// ['town-square', 'off-topic']
|
||||||
|
// However, if TeamSettings.ExperimentalDefaultChannels contains a list of channels then that list will replace
|
||||||
|
// 'off-topic' and be included in the return results in addition to 'town-square'. For example:
|
||||||
|
// ['town-square', 'game-of-thrones', 'wow']
|
||||||
|
//
|
||||||
|
func (a *App) DefaultChannelNames() []string {
|
||||||
|
names := []string{"town-square"}
|
||||||
|
|
||||||
|
if len(a.Config().TeamSettings.ExperimentalDefaultChannels) == 0 {
|
||||||
|
names = append(names, "off-topic")
|
||||||
|
} else {
|
||||||
|
seenChannels := map[string]bool{}
|
||||||
|
for _, channelName := range a.Config().TeamSettings.ExperimentalDefaultChannels {
|
||||||
|
if !seenChannels[channelName] {
|
||||||
|
names = append(names, channelName)
|
||||||
|
seenChannels[channelName] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError {
|
func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError {
|
||||||
var requestor *model.User
|
var requestor *model.User
|
||||||
if userRequestorId != "" {
|
if userRequestorId != "" {
|
||||||
@@ -43,22 +72,8 @@ func (a *App) JoinDefaultChannels(teamId string, user *model.User, shouldBeAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultChannelList := []string{"town-square"}
|
|
||||||
|
|
||||||
if len(a.Config().TeamSettings.ExperimentalDefaultChannels) == 0 {
|
|
||||||
defaultChannelList = append(defaultChannelList, "off-topic")
|
|
||||||
} else {
|
|
||||||
seenChannels := map[string]bool{}
|
|
||||||
for _, channelName := range a.Config().TeamSettings.ExperimentalDefaultChannels {
|
|
||||||
if !seenChannels[channelName] {
|
|
||||||
defaultChannelList = append(defaultChannelList, channelName)
|
|
||||||
seenChannels[channelName] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var err *model.AppError
|
var err *model.AppError
|
||||||
for _, channelName := range defaultChannelList {
|
for _, channelName := range a.DefaultChannelNames() {
|
||||||
if result := <-a.Srv.Store.Channel().GetByName(teamId, channelName, true); result.Err != nil {
|
if result := <-a.Srv.Store.Channel().GetByName(teamId, channelName, true); result.Err != nil {
|
||||||
err = result.Err
|
err = result.Err
|
||||||
} else {
|
} else {
|
||||||
@@ -1230,8 +1245,16 @@ func (a *App) GetChannelsForUser(teamId string, userId string, includeDeleted bo
|
|||||||
return result.Data.(*model.ChannelList), nil
|
return result.Data.(*model.ChannelList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetAllChannels(page, perPage int, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) {
|
func (a *App) GetAllChannels(page, perPage int, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) {
|
||||||
result := <-a.Srv.Store.Channel().GetAllChannels(page*perPage, perPage, includeDeleted)
|
if opts.ExcludeDefaultChannels {
|
||||||
|
opts.ExcludeChannelNames = a.DefaultChannelNames()
|
||||||
|
}
|
||||||
|
storeOpts := store.ChannelSearchOpts{
|
||||||
|
ExcludeChannelNames: opts.ExcludeChannelNames,
|
||||||
|
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
||||||
|
IncludeDeleted: opts.IncludeDeleted,
|
||||||
|
}
|
||||||
|
result := <-a.Srv.Store.Channel().GetAllChannels(page*perPage, perPage, storeOpts)
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
}
|
}
|
||||||
@@ -1784,8 +1807,17 @@ func (a *App) AutocompleteChannelsForSearch(teamId string, userId string, term s
|
|||||||
return result.Data.(*model.ChannelList), nil
|
return result.Data.(*model.ChannelList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) SearchAllChannels(term string, includeDeleted bool) (*model.ChannelListWithTeamData, *model.AppError) {
|
func (a *App) SearchAllChannels(term string, opts model.ChannelSearchOpts) (*model.ChannelListWithTeamData, *model.AppError) {
|
||||||
result := <-a.Srv.Store.Channel().SearchAllChannels(term, *a.Config().TeamSettings.ExperimentalViewArchivedChannels && includeDeleted)
|
opts.IncludeDeleted = *a.Config().TeamSettings.ExperimentalViewArchivedChannels && opts.IncludeDeleted
|
||||||
|
if opts.ExcludeDefaultChannels {
|
||||||
|
opts.ExcludeChannelNames = a.DefaultChannelNames()
|
||||||
|
}
|
||||||
|
storeOpts := store.ChannelSearchOpts{
|
||||||
|
ExcludeChannelNames: opts.ExcludeChannelNames,
|
||||||
|
NotAssociatedToGroup: opts.NotAssociatedToGroup,
|
||||||
|
IncludeDeleted: opts.IncludeDeleted,
|
||||||
|
}
|
||||||
|
result := <-a.Srv.Store.Channel().SearchAllChannels(term, storeOpts)
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
return nil, result.Err
|
return nil, result.Err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -895,3 +895,20 @@ func TestUpdateChannelMemberRolesChangingGuest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDefaultChannelNames(t *testing.T) {
|
||||||
|
th := Setup(t)
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
actual := th.App.DefaultChannelNames()
|
||||||
|
expect := []string{"town-square", "off-topic"}
|
||||||
|
require.ElementsMatch(t, expect, actual)
|
||||||
|
|
||||||
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||||
|
cfg.TeamSettings.ExperimentalDefaultChannels = []string{"foo", "bar"}
|
||||||
|
})
|
||||||
|
|
||||||
|
actual = th.App.DefaultChannelNames()
|
||||||
|
expect = []string{"town-square", "foo", "bar"}
|
||||||
|
require.ElementsMatch(t, expect, actual)
|
||||||
|
}
|
||||||
|
|||||||
@@ -5274,6 +5274,10 @@
|
|||||||
"id": "store.select_error",
|
"id": "store.select_error",
|
||||||
"translation": "select error"
|
"translation": "select error"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql.build_query.app_error",
|
||||||
|
"translation": "failed to build query"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "store.sql.convert_string_array",
|
"id": "store.sql.convert_string_array",
|
||||||
"translation": "FromDb: Unable to convert StringArray to *string"
|
"translation": "FromDb: Unable to convert StringArray to *string"
|
||||||
|
|||||||
@@ -80,6 +80,20 @@ type DirectChannelForExport struct {
|
|||||||
Members *[]string
|
Members *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelSearchOpts contains options for searching channels.
|
||||||
|
//
|
||||||
|
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.
|
||||||
|
// ExcludeDefaultChannels will exclude the configured default channels (ex 'town-square' and 'off-topic').
|
||||||
|
// IncludeDeleted will include channel records where DeleteAt != 0.
|
||||||
|
// ExcludeChannelNames will exclude channels from the results by name.
|
||||||
|
//
|
||||||
|
type ChannelSearchOpts struct {
|
||||||
|
NotAssociatedToGroup string
|
||||||
|
ExcludeDefaultChannels bool
|
||||||
|
IncludeDeleted bool
|
||||||
|
ExcludeChannelNames []string
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Channel) DeepCopy() *Channel {
|
func (o *Channel) DeepCopy() *Channel {
|
||||||
copy := *o
|
copy := *o
|
||||||
if copy.SchemeId != nil {
|
if copy.SchemeId != nil {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ const CHANNEL_SEARCH_DEFAULT_LIMIT = 50
|
|||||||
|
|
||||||
type ChannelSearch struct {
|
type ChannelSearch struct {
|
||||||
Term string `json:"term"`
|
Term string `json:"term"`
|
||||||
|
ExcludeDefaultChannels bool `json:"exclude_default_channels"`
|
||||||
|
NotAssociatedToGroup string `json:"not_associated_to_group"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToJson convert a Channel to a json string
|
// ToJson convert a Channel to a json string
|
||||||
|
|||||||
@@ -949,17 +949,37 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDelete
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) GetAllChannels(offset int, limit int, includeDeleted bool) store.StoreChannel {
|
func (s SqlChannelStore) GetAllChannels(offset int, limit int, opts store.ChannelSearchOpts) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
deleteFilter := "AND c.DeleteAt = 0"
|
query := s.getQueryBuilder().
|
||||||
if includeDeleted {
|
Select("c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt AS TeamUpdateAt").
|
||||||
deleteFilter = ""
|
From("Channels AS c").
|
||||||
|
Join("Teams ON Teams.Id = c.TeamId").
|
||||||
|
Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}}).
|
||||||
|
OrderBy("c.DisplayName, Teams.DisplayName").
|
||||||
|
Limit(uint64(limit)).
|
||||||
|
Offset(uint64(offset))
|
||||||
|
|
||||||
|
if !opts.IncludeDeleted {
|
||||||
|
query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
|
||||||
}
|
}
|
||||||
|
|
||||||
query := "SELECT c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt as TeamUpdateAt FROM Channels AS c JOIN Teams ON Teams.Id = c.TeamId WHERE (c.Type = 'P' OR c.Type = 'O') " + deleteFilter + " ORDER BY c.DisplayName, Teams.DisplayName LIMIT :Limit OFFSET :Offset"
|
if len(opts.NotAssociatedToGroup) > 0 {
|
||||||
|
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.ExcludeChannelNames) > 0 {
|
||||||
|
query = query.Where(fmt.Sprintf("c.Name NOT IN ('%s')", strings.Join(opts.ExcludeChannelNames, "', '")))
|
||||||
|
}
|
||||||
|
|
||||||
|
queryString, args, err := query.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
data := &model.ChannelListWithTeamData{}
|
data := &model.ChannelListWithTeamData{}
|
||||||
_, err := s.GetReplica().Select(data, query, map[string]interface{}{"Limit": limit, "Offset": offset})
|
_, err = s.GetReplica().Select(data, queryString, args...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql_channel.get_all_channels.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetAllChannels", "store.sql_channel.get_all_channels.get.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
@@ -2120,29 +2140,45 @@ func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SqlChannelStore) SearchAllChannels(term string, includeDeleted bool) store.StoreChannel {
|
func (s SqlChannelStore) SearchAllChannels(term string, opts store.ChannelSearchOpts) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
parameters := map[string]interface{}{}
|
query := s.getQueryBuilder().
|
||||||
deleteFilter := "AND c.DeleteAt = 0"
|
Select("c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt").
|
||||||
if includeDeleted {
|
From("Channels AS c").
|
||||||
deleteFilter = ""
|
Join("Teams AS t ON t.Id = c.TeamId").
|
||||||
|
Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}}).
|
||||||
|
OrderBy("c.DisplayName, t.DisplayName").
|
||||||
|
Limit(uint64(100))
|
||||||
|
|
||||||
|
if !opts.IncludeDeleted {
|
||||||
|
query = query.Where(sq.Eq{"c.DeleteAt": int(0)})
|
||||||
}
|
}
|
||||||
searchQuery := `SELECT c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt FROM Channels AS c JOIN Teams AS t ON t.Id = c.TeamId WHERE (c.Type = 'P' OR c.Type = 'O') ` + deleteFilter + ` SEARCH_CLAUSE ORDER BY c.DisplayName, t.DisplayName LIMIT 100`
|
|
||||||
|
|
||||||
likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
|
likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
if likeTerm == "" {
|
if len(likeTerm) > 0 {
|
||||||
// If the likeTerm is empty after preparing, then don't bother searching.
|
likeClause = strings.ReplaceAll(likeClause, ":LikeTerm", "'"+likeTerm+"'")
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
|
|
||||||
} else {
|
|
||||||
parameters["LikeTerm"] = likeTerm
|
|
||||||
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose")
|
||||||
parameters["FulltextTerm"] = fulltextTerm
|
fulltextClause = strings.ReplaceAll(fulltextClause, ":FulltextTerm", "'"+fulltextTerm+"'")
|
||||||
searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1)
|
query = query.Where("(" + likeClause + " OR " + fulltextClause + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.ExcludeChannelNames) > 0 {
|
||||||
|
query = query.Where(fmt.Sprintf("c.Name NOT IN ('%s')", strings.Join(opts.ExcludeChannelNames, "', '")))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.NotAssociatedToGroup) > 0 {
|
||||||
|
query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
queryString, args, err := query.ToSql()
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlChannelStore.SearchAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var channels model.ChannelListWithTeamData
|
var channels model.ChannelListWithTeamData
|
||||||
|
|
||||||
if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil {
|
if _, err := s.GetReplica().Select(&channels, queryString, args...); err != nil {
|
||||||
result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2501,7 +2537,7 @@ func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string)
|
|||||||
Id
|
Id
|
||||||
LIMIT :Limit`,
|
LIMIT :Limit`,
|
||||||
map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil {
|
map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil {
|
||||||
result.Err = model.NewAppError("SqlTeamStore.GetAllChannelsForExportAfter", "store.sql_channel.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetAllChannelsForExportAfter", "store.sql_channel.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2561,12 +2597,12 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
|
|||||||
|
|
||||||
queryString, args, err := query.ToSql()
|
queryString, args, err := query.ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil {
|
if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil {
|
||||||
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2585,13 +2621,13 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
|
|||||||
|
|
||||||
queryString, args, err = query.ToSql()
|
queryString, args, err = query.ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var channelMembers []*model.ChannelMemberForExport
|
var channelMembers []*model.ChannelMemberForExport
|
||||||
if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil {
|
if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil {
|
||||||
result.Err = model.NewAppError("SqlTeamStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate each channel with its members
|
// Populate each channel with its members
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ type ChannelStore interface {
|
|||||||
GetDeletedByName(team_id string, name string) StoreChannel
|
GetDeletedByName(team_id string, name string) StoreChannel
|
||||||
GetDeleted(team_id string, offset int, limit int) StoreChannel
|
GetDeleted(team_id string, offset int, limit int) StoreChannel
|
||||||
GetChannels(teamId string, userId string, includeDeleted bool) StoreChannel
|
GetChannels(teamId string, userId string, includeDeleted bool) StoreChannel
|
||||||
GetAllChannels(page, perPage int, includeDeleted bool) StoreChannel
|
GetAllChannels(page, perPage int, opts ChannelSearchOpts) StoreChannel
|
||||||
GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
|
GetMoreChannels(teamId string, userId string, offset int, limit int) StoreChannel
|
||||||
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
|
GetPublicChannelsForTeam(teamId string, offset int, limit int) StoreChannel
|
||||||
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
|
GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) StoreChannel
|
||||||
@@ -182,7 +182,7 @@ type ChannelStore interface {
|
|||||||
GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel
|
GetMembersForUserWithPagination(teamId, userId string, page, perPage int) StoreChannel
|
||||||
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
AutocompleteInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
||||||
AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel
|
AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) StoreChannel
|
||||||
SearchAllChannels(term string, includeDeleted bool) StoreChannel
|
SearchAllChannels(term string, opts ChannelSearchOpts) StoreChannel
|
||||||
SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
SearchInTeam(teamId string, term string, includeDeleted bool) StoreChannel
|
||||||
SearchMore(userId string, teamId string, term string) StoreChannel
|
SearchMore(userId string, teamId string, term string) StoreChannel
|
||||||
GetMembersByIds(channelId string, userIds []string) StoreChannel
|
GetMembersByIds(channelId string, userIds []string) StoreChannel
|
||||||
@@ -607,3 +607,15 @@ type LinkMetadataStore interface {
|
|||||||
Save(linkMetadata *model.LinkMetadata) StoreChannel
|
Save(linkMetadata *model.LinkMetadata) StoreChannel
|
||||||
Get(url string, timestamp int64) StoreChannel
|
Get(url string, timestamp int64) StoreChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelSearchOpts contains options for searching channels.
|
||||||
|
//
|
||||||
|
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.
|
||||||
|
// IncludeDeleted will include channel records where DeleteAt != 0.
|
||||||
|
// ExcludeChannelNames will exclude channels from the results by name.
|
||||||
|
//
|
||||||
|
type ChannelSearchOpts struct {
|
||||||
|
NotAssociatedToGroup string
|
||||||
|
IncludeDeleted bool
|
||||||
|
ExcludeChannelNames []string
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ type SqlSupplier interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cleanupChannels(t *testing.T, ss store.Store) {
|
func cleanupChannels(t *testing.T, ss store.Store) {
|
||||||
result := <-ss.Channel().GetAllChannels(0, 100000, true)
|
result := <-ss.Channel().GetAllChannels(0, 100000, store.ChannelSearchOpts{IncludeDeleted: true})
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
t.Fatal("error cleaning all channels")
|
t.Fatalf("error cleaning all channels: %v", result.Err)
|
||||||
}
|
}
|
||||||
list := result.Data.(*model.ChannelListWithTeamData)
|
list := result.Data.(*model.ChannelListWithTeamData)
|
||||||
for _, channel := range *list {
|
for _, channel := range *list {
|
||||||
@@ -1073,6 +1073,15 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
|
|||||||
c1.Type = model.CHANNEL_OPEN
|
c1.Type = model.CHANNEL_OPEN
|
||||||
store.Must(ss.Channel().Save(&c1, -1))
|
store.Must(ss.Channel().Save(&c1, -1))
|
||||||
|
|
||||||
|
group := &model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: model.NewId(),
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
}
|
||||||
|
store.Must(ss.Group().Create(group))
|
||||||
|
store.Must(ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, c1.Id, true)))
|
||||||
|
|
||||||
c2 := model.Channel{}
|
c2 := model.Channel{}
|
||||||
c2.TeamId = t1.Id
|
c2.TeamId = t1.Id
|
||||||
c2.DisplayName = "Channel2" + model.NewId()
|
c2.DisplayName = "Channel2" + model.NewId()
|
||||||
@@ -1103,7 +1112,7 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
|
|||||||
c5.Type = model.CHANNEL_GROUP
|
c5.Type = model.CHANNEL_GROUP
|
||||||
store.Must(ss.Channel().Save(&c5, -1))
|
store.Must(ss.Channel().Save(&c5, -1))
|
||||||
|
|
||||||
cresult := <-ss.Channel().GetAllChannels(0, 10, false)
|
cresult := <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{})
|
||||||
list := cresult.Data.(*model.ChannelListWithTeamData)
|
list := cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
assert.Len(t, *list, 2)
|
assert.Len(t, *list, 2)
|
||||||
assert.Equal(t, (*list)[0].Id, c1.Id)
|
assert.Equal(t, (*list)[0].Id, c1.Id)
|
||||||
@@ -1111,7 +1120,7 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
|
|||||||
assert.Equal(t, (*list)[1].Id, c3.Id)
|
assert.Equal(t, (*list)[1].Id, c3.Id)
|
||||||
assert.Equal(t, (*list)[1].TeamDisplayName, "Name2")
|
assert.Equal(t, (*list)[1].TeamDisplayName, "Name2")
|
||||||
|
|
||||||
cresult = <-ss.Channel().GetAllChannels(0, 10, true)
|
cresult = <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{IncludeDeleted: true})
|
||||||
list = cresult.Data.(*model.ChannelListWithTeamData)
|
list = cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
assert.Len(t, *list, 3)
|
assert.Len(t, *list, 3)
|
||||||
assert.Equal(t, (*list)[0].Id, c1.Id)
|
assert.Equal(t, (*list)[0].Id, c1.Id)
|
||||||
@@ -1119,12 +1128,22 @@ func testChannelStoreGetAllChannels(t *testing.T, ss store.Store, s SqlSupplier)
|
|||||||
assert.Equal(t, (*list)[1].Id, c2.Id)
|
assert.Equal(t, (*list)[1].Id, c2.Id)
|
||||||
assert.Equal(t, (*list)[2].Id, c3.Id)
|
assert.Equal(t, (*list)[2].Id, c3.Id)
|
||||||
|
|
||||||
cresult = <-ss.Channel().GetAllChannels(0, 1, true)
|
cresult = <-ss.Channel().GetAllChannels(0, 1, store.ChannelSearchOpts{IncludeDeleted: true})
|
||||||
list = cresult.Data.(*model.ChannelListWithTeamData)
|
list = cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
assert.Len(t, *list, 1)
|
assert.Len(t, *list, 1)
|
||||||
assert.Equal(t, (*list)[0].Id, c1.Id)
|
assert.Equal(t, (*list)[0].Id, c1.Id)
|
||||||
assert.Equal(t, (*list)[0].TeamDisplayName, "Name")
|
assert.Equal(t, (*list)[0].TeamDisplayName, "Name")
|
||||||
|
|
||||||
|
// Not associated to group
|
||||||
|
cresult = <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{NotAssociatedToGroup: group.Id})
|
||||||
|
list = cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
|
assert.Len(t, *list, 1)
|
||||||
|
|
||||||
|
// Exclude channel names
|
||||||
|
cresult = <-ss.Channel().GetAllChannels(0, 10, store.ChannelSearchOpts{ExcludeChannelNames: []string{c1.Name}})
|
||||||
|
list = cresult.Data.(*model.ChannelListWithTeamData)
|
||||||
|
assert.Len(t, *list, 1)
|
||||||
|
|
||||||
// Manually truncate Channels table until testlib can handle cleanups
|
// Manually truncate Channels table until testlib can handle cleanups
|
||||||
s.GetMaster().Exec("TRUNCATE Channels")
|
s.GetMaster().Exec("TRUNCATE Channels")
|
||||||
}
|
}
|
||||||
@@ -2325,6 +2344,15 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
}
|
}
|
||||||
store.Must(ss.Channel().Save(&o7, -1))
|
store.Must(ss.Channel().Save(&o7, -1))
|
||||||
|
|
||||||
|
group := &model.Group{
|
||||||
|
Name: model.NewId(),
|
||||||
|
DisplayName: model.NewId(),
|
||||||
|
Source: model.GroupSourceLdap,
|
||||||
|
RemoteId: model.NewId(),
|
||||||
|
}
|
||||||
|
store.Must(ss.Group().Create(group))
|
||||||
|
store.Must(ss.Group().CreateGroupSyncable(model.NewGroupChannel(group.Id, o7.Id, true)))
|
||||||
|
|
||||||
o8 := model.Channel{
|
o8 := model.Channel{
|
||||||
TeamId: t1.Id,
|
TeamId: t1.Id,
|
||||||
DisplayName: "Off-Limit",
|
DisplayName: "Off-Limit",
|
||||||
@@ -2381,28 +2409,31 @@ func testChannelStoreSearchAllChannels(t *testing.T, ss store.Store) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Description string
|
Description string
|
||||||
Term string
|
Term string
|
||||||
IncludeDeleted bool
|
Opts store.ChannelSearchOpts
|
||||||
ExpectedResults *model.ChannelList
|
ExpectedResults *model.ChannelList
|
||||||
}{
|
}{
|
||||||
{"ChannelA", "ChannelA", false, &model.ChannelList{&o1, &o2, &o3}},
|
{"ChannelA", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3}},
|
||||||
{"ChannelA, include deleted", "ChannelA", true, &model.ChannelList{&o1, &o2, &o3, &o13}},
|
{"ChannelA, include deleted", "ChannelA", store.ChannelSearchOpts{IncludeDeleted: true}, &model.ChannelList{&o1, &o2, &o3, &o13}},
|
||||||
{"empty string", "", false, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o12, &o11, &o8, &o7, &o6, &o10, &o9}},
|
{"empty string", "", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o1, &o2, &o3, &o4, &o5, &o12, &o11, &o8, &o7, &o6, &o10, &o9}},
|
||||||
{"no matches", "blargh", false, &model.ChannelList{}},
|
{"no matches", "blargh", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{}},
|
||||||
{"prefix", "off-", false, &model.ChannelList{&o8, &o7, &o6}},
|
{"prefix", "off-", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o8, &o7, &o6}},
|
||||||
{"full match with dash", "off-topic", false, &model.ChannelList{&o6}},
|
{"full match with dash", "off-topic", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o6}},
|
||||||
{"town square", "town square", false, &model.ChannelList{&o9}},
|
{"town square", "town square", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}},
|
||||||
{"the in name", "the", false, &model.ChannelList{&o10}},
|
{"the in name", "the", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o10}},
|
||||||
{"Mobile", "Mobile", false, &model.ChannelList{&o11}},
|
{"Mobile", "Mobile", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o11}},
|
||||||
{"search purpose", "now searchable", false, &model.ChannelList{&o12}},
|
{"search purpose", "now searchable", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o12}},
|
||||||
{"pipe ignored", "town square |", false, &model.ChannelList{&o9}},
|
{"pipe ignored", "town square |", store.ChannelSearchOpts{IncludeDeleted: false}, &model.ChannelList{&o9}},
|
||||||
|
{"exclude defaults search 'off'", "off-", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"off-topic"}}, &model.ChannelList{&o8, &o7}},
|
||||||
|
{"exclude defaults search 'town'", "town", store.ChannelSearchOpts{IncludeDeleted: false, ExcludeChannelNames: []string{"town-square"}}, &model.ChannelList{}},
|
||||||
|
{"exclude by group association", "off", store.ChannelSearchOpts{IncludeDeleted: false, NotAssociatedToGroup: group.Id}, &model.ChannelList{&o8, &o6}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.Description, func(t *testing.T) {
|
t.Run(testCase.Description, func(t *testing.T) {
|
||||||
result := <-ss.Channel().SearchAllChannels(testCase.Term, testCase.IncludeDeleted)
|
result := <-ss.Channel().SearchAllChannels(testCase.Term, testCase.Opts)
|
||||||
require.Nil(t, result.Err)
|
require.Nil(t, result.Err)
|
||||||
channels := result.Data.(*model.ChannelListWithTeamData)
|
channels := result.Data.(*model.ChannelListWithTeamData)
|
||||||
require.Equal(t, len(*channels), len(*testCase.ExpectedResults))
|
require.Equal(t, len(*testCase.ExpectedResults), len(*channels))
|
||||||
for i, expected := range *testCase.ExpectedResults {
|
for i, expected := range *testCase.ExpectedResults {
|
||||||
require.Equal(t, (*channels)[i].Id, expected.Id)
|
require.Equal(t, (*channels)[i].Id, expected.Id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,13 +212,13 @@ func (_m *ChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId stri
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllChannels provides a mock function with given fields: page, perPage, includeDeleted
|
// GetAllChannels provides a mock function with given fields: page, perPage, opts
|
||||||
func (_m *ChannelStore) GetAllChannels(page int, perPage int, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) GetAllChannels(page int, perPage int, opts store.ChannelSearchOpts) store.StoreChannel {
|
||||||
ret := _m.Called(page, perPage, includeDeleted)
|
ret := _m.Called(page, perPage, opts)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(int, int, bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(int, int, store.ChannelSearchOpts) store.StoreChannel); ok {
|
||||||
r0 = rf(page, perPage, includeDeleted)
|
r0 = rf(page, perPage, opts)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(store.StoreChannel)
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
@@ -1003,13 +1003,13 @@ func (_m *ChannelStore) SaveMember(member *model.ChannelMember) store.StoreChann
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchAllChannels provides a mock function with given fields: term, includeDeleted
|
// SearchAllChannels provides a mock function with given fields: term, opts
|
||||||
func (_m *ChannelStore) SearchAllChannels(term string, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) SearchAllChannels(term string, opts store.ChannelSearchOpts) store.StoreChannel {
|
||||||
ret := _m.Called(term, includeDeleted)
|
ret := _m.Called(term, opts)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, store.ChannelSearchOpts) store.StoreChannel); ok {
|
||||||
r0 = rf(term, includeDeleted)
|
r0 = rf(term, opts)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).(store.StoreChannel)
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ type Params struct {
|
|||||||
NotAssociatedToChannel string
|
NotAssociatedToChannel string
|
||||||
Paginate *bool
|
Paginate *bool
|
||||||
IncludeMemberCount bool
|
IncludeMemberCount bool
|
||||||
|
NotAssociatedToGroup string
|
||||||
|
ExcludeDefaultChannels bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParamsFromRequest(r *http.Request) *Params {
|
func ParamsFromRequest(r *http.Request) *Params {
|
||||||
@@ -260,5 +262,11 @@ func ParamsFromRequest(r *http.Request) *Params {
|
|||||||
params.IncludeMemberCount = val
|
params.IncludeMemberCount = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params.NotAssociatedToGroup = query.Get("not_associated_to_group")
|
||||||
|
|
||||||
|
if val, err := strconv.ParseBool(query.Get("exclude_default_channels")); err == nil {
|
||||||
|
params.ExcludeDefaultChannels = val
|
||||||
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user