* parent 48256721c4 (#14358)

author Eli Yukelzon <reflog@gmail.com> 1585814774 +0300
committer Eli Yukelzon <reflog@gmail.com> 1589111022 +0300

Sidebar caregories implemented

Apply suggestions from code review

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>

Update store/sqlstore/channel_store.go

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>

Update store/sqlstore/channel_store.go

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>

code review suggestions

status messages

edge case

bugs...

timeout reverse

* MM-25126 Add a separate default sorting method for categories (#14575)

* MM-25158 Add user to initial sidebar categories when joining team (#14570)

* MM-25281  Place new categories in the correct position (#14609)

* MM-25277  Return channels that aren't in a category as part of the Channels/Direct Messages categories (#14601)

* MM-25276  Remove categories when leaving a team (#14600)

* Remove categories when leaving a team

* layers

* corrected cleanup function

* lint

* .

* corrected errors in postgres

* .

* MM-25280  Ensure that the "update category order" API call only contains real category IDs and isn't missing any IDs (#14626)

* Ensure that the "update category order" API call only contains real category IDs and isn't missing any IDs

* tests

* correct status code

* MM-25278  Change "update category" API to return 400 when changing unsupported fields (#14599)

* MM-25279  Change category migration to only populate channels in Favorites (#14627)

* MM-25157 Add API to delete custom categories  (#14574)

* MM-25157 Add API to delete custom categories

* get categories fix

* maxorder fix

* Use correct websocket event when deleting category

* Fix tests and remove debug code

* Actually use the right websocket event this time

* test cleanup

* Update test for new category order

Co-authored-by: Eli Yukelzon <reflog@gmail.com>

* MM-24914 Various fixes for sidebar channel handling (#14756)

* Fix checking for channel membership when reordering channels

* Remove unique constraint on SidebarCategories

* Set column sizes for SidebarCategories and SidebarChannels tables

* Allow changing the sorting method for non-DM categories

* Fix nil pointers in error handling

* Fix orphaned channels from other team being returned in Channels category

* Fix non-orphaned channels being duplicated in the Channels category

* Remove unique constraint on SidebarChannels

* Fix category/name of favorites preferences

* Fix testSidebarChannelsMigration

* Rename err to nErr and appErr to err

* Fix channel order returned by GetSidebarCategories on MySQL

* Fix adding/removing favorites preferences

* Remove leftover TODO

* Change SidebarCategoryType enums to use full names (#14786)

* Change SidebarCategoryType enums to use full names

* Fix Channels constant

* Remove leftover debug code

* MM-24914 Fix updateCategory endpoint returning the wrong type (#14795)

* MM-24914 Make some changes to UpdateSidebarCategories (#14806)

* Fix orphaned DMs not always being returned

* MM-24914 Make some changes to UpdateSidebarCategories

* Run updateSidebarCategoryOrderT in a transaction

* Fix deleting SidebarChannels based on order of arguments to UpdateSidebarCategories

* bump for api testing

* bump for api testing

* Change CreateInitialSidebarCategories to return a plain error

* Change MigrateSidebarCategories to return a plain error

* Remove usage of UpdateColumns when updating sidebar categories (#14843)

* Remove usage of UpdateColumns when changing category order

* Add a random test case

* Remove usage of UpdateColumns when updating sidebar categories (#14843)

* Remove usage of UpdateColumns when changing category order

* Add a random test case

* Remove usage of UpdateColumns when updating sidebar categories (#14843)

* Remove usage of UpdateColumns when changing category order

* Add a random test case

* MM-26343 Make CreateInitialSidebarCategories idempotent (#14870)

* Fix bad merge

* Fix another bad merge

* Fix unintentionally removed i18n string

Co-authored-by: Eli Yukelzon <reflog@gmail.com>
Этот коммит содержится в:
Harrison Healey
2020-07-06 18:20:35 -04:00
коммит произвёл GitHub
родитель 83a80a2422
Коммит de6a57cdc3
31 изменённых файлов: 3855 добавлений и 4 удалений

Просмотреть файл

@@ -46,6 +46,7 @@ type Routes struct {
ChannelMember *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members/{user_id:[A-Za-z0-9]+}' ChannelMember *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/members/{user_id:[A-Za-z0-9]+}'
ChannelMembersForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/teams/{team_id:[A-Za-z0-9]+}/channels/members' ChannelMembersForUser *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/teams/{team_id:[A-Za-z0-9]+}/channels/members'
ChannelModerations *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/moderations' ChannelModerations *mux.Router // 'api/v4/channels/{channel_id:[A-Za-z0-9]+}/moderations'
ChannelCategories *mux.Router // 'api/v4/users/{user_id:[A-Za-z0-9]+}/teams/{team_id:[A-Za-z0-9]+}/channels/categories'
Posts *mux.Router // 'api/v4/posts' Posts *mux.Router // 'api/v4/posts'
Post *mux.Router // 'api/v4/posts/{post_id:[A-Za-z0-9]+}' Post *mux.Router // 'api/v4/posts/{post_id:[A-Za-z0-9]+}'
@@ -160,6 +161,7 @@ func Init(configservice configservice.ConfigService, globalOptionsFunc app.AppOp
api.BaseRoutes.ChannelMember = api.BaseRoutes.ChannelMembers.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter() api.BaseRoutes.ChannelMember = api.BaseRoutes.ChannelMembers.PathPrefix("/{user_id:[A-Za-z0-9]+}").Subrouter()
api.BaseRoutes.ChannelMembersForUser = api.BaseRoutes.User.PathPrefix("/teams/{team_id:[A-Za-z0-9]+}/channels/members").Subrouter() api.BaseRoutes.ChannelMembersForUser = api.BaseRoutes.User.PathPrefix("/teams/{team_id:[A-Za-z0-9]+}/channels/members").Subrouter()
api.BaseRoutes.ChannelModerations = api.BaseRoutes.Channel.PathPrefix("/moderations").Subrouter() api.BaseRoutes.ChannelModerations = api.BaseRoutes.Channel.PathPrefix("/moderations").Subrouter()
api.BaseRoutes.ChannelCategories = api.BaseRoutes.User.PathPrefix("/teams/{team_id:[A-Za-z0-9]+}/channels/categories").Subrouter()
api.BaseRoutes.Posts = api.BaseRoutes.ApiRoot.PathPrefix("/posts").Subrouter() api.BaseRoutes.Posts = api.BaseRoutes.ApiRoot.PathPrefix("/posts").Subrouter()
api.BaseRoutes.Post = api.BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter() api.BaseRoutes.Post = api.BaseRoutes.Posts.PathPrefix("/{post_id:[A-Za-z0-9]+}").Subrouter()

Просмотреть файл

@@ -35,6 +35,15 @@ func (api *API) InitChannel() {
api.BaseRoutes.ChannelsForTeam.Handle("/search_autocomplete", api.ApiSessionRequired(autocompleteChannelsForTeamForSearch)).Methods("GET") api.BaseRoutes.ChannelsForTeam.Handle("/search_autocomplete", api.ApiSessionRequired(autocompleteChannelsForTeamForSearch)).Methods("GET")
api.BaseRoutes.User.Handle("/teams/{team_id:[A-Za-z0-9]+}/channels", api.ApiSessionRequired(getChannelsForTeamForUser)).Methods("GET") api.BaseRoutes.User.Handle("/teams/{team_id:[A-Za-z0-9]+}/channels", api.ApiSessionRequired(getChannelsForTeamForUser)).Methods("GET")
api.BaseRoutes.ChannelCategories.Handle("", api.ApiSessionRequired(getCategoriesForTeamForUser)).Methods("GET")
api.BaseRoutes.ChannelCategories.Handle("", api.ApiSessionRequired(createCategoryForTeamForUser)).Methods("POST")
api.BaseRoutes.ChannelCategories.Handle("", api.ApiSessionRequired(updateCategoriesForTeamForUser)).Methods("PUT")
api.BaseRoutes.ChannelCategories.Handle("/order", api.ApiSessionRequired(getCategoryOrderForTeamForUser)).Methods("GET")
api.BaseRoutes.ChannelCategories.Handle("/order", api.ApiSessionRequired(updateCategoryOrderForTeamForUser)).Methods("PUT")
api.BaseRoutes.ChannelCategories.Handle("/{category_id:[A-Za-z0-9]+}", api.ApiSessionRequired(getCategoryForTeamForUser)).Methods("GET")
api.BaseRoutes.ChannelCategories.Handle("/{category_id:[A-Za-z0-9]+}", api.ApiSessionRequired(updateCategoryForTeamForUser)).Methods("PUT")
api.BaseRoutes.ChannelCategories.Handle("/{category_id:[A-Za-z0-9]+}", api.ApiSessionRequired(deleteCategoryForTeamForUser)).Methods("DELETE")
api.BaseRoutes.Channel.Handle("", api.ApiSessionRequired(getChannel)).Methods("GET") api.BaseRoutes.Channel.Handle("", api.ApiSessionRequired(getChannel)).Methods("GET")
api.BaseRoutes.Channel.Handle("", api.ApiSessionRequired(updateChannel)).Methods("PUT") api.BaseRoutes.Channel.Handle("", api.ApiSessionRequired(updateChannel)).Methods("PUT")
api.BaseRoutes.Channel.Handle("/patch", api.ApiSessionRequired(patchChannel)).Methods("PUT") api.BaseRoutes.Channel.Handle("/patch", api.ApiSessionRequired(patchChannel)).Methods("PUT")
@@ -1848,3 +1857,254 @@ func moveChannel(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(channel.ToJson())) w.Write([]byte(channel.ToJson()))
} }
func getCategoriesForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
categories, err := c.App.GetSidebarCategories(c.Params.UserId, c.Params.TeamId)
if err != nil {
c.Err = err
return
}
w.Write(categories.ToJson())
}
func createCategoryForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
auditRec := c.MakeAuditRecord("createCategoryForTeamForUser", audit.Fail)
defer c.LogAuditRec(auditRec)
categoryCreateRequest, err := model.SidebarCategoryFromJson(r.Body)
if err != nil || c.Params.UserId != categoryCreateRequest.UserId || c.Params.TeamId != categoryCreateRequest.TeamId {
c.SetInvalidParam("category")
return
}
if appErr := validateUserChannels("createCategoryForTeamForUser", c, c.Params.TeamId, c.Params.UserId, categoryCreateRequest.Channels); appErr != nil {
c.Err = appErr
return
}
category, appErr := c.App.CreateSidebarCategory(c.Params.UserId, c.Params.TeamId, categoryCreateRequest)
if appErr != nil {
c.Err = appErr
return
}
auditRec.Success()
w.Write(category.ToJson())
}
func getCategoryOrderForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
order, err := c.App.GetSidebarCategoryOrder(c.Params.UserId, c.Params.TeamId)
if err != nil {
c.Err = err
return
}
w.Write([]byte(model.ArrayToJson(order)))
}
func updateCategoryOrderForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
auditRec := c.MakeAuditRecord("updateCategoryOrderForTeamForUser", audit.Fail)
defer c.LogAuditRec(auditRec)
categoryOrder := model.ArrayFromJson(r.Body)
for _, categoryId := range categoryOrder {
if !c.App.SessionHasPermissionToCategory(*c.App.Session(), c.Params.UserId, c.Params.TeamId, categoryId) {
c.SetInvalidParam("category")
return
}
}
err := c.App.UpdateSidebarCategoryOrder(c.Params.UserId, c.Params.TeamId, categoryOrder)
if err != nil {
c.Err = err
return
}
auditRec.Success()
w.Write([]byte(model.ArrayToJson(categoryOrder)))
}
func getCategoryForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId().RequireCategoryId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToCategory(*c.App.Session(), c.Params.UserId, c.Params.TeamId, c.Params.CategoryId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
categories, err := c.App.GetSidebarCategory(c.Params.CategoryId)
if err != nil {
c.Err = err
return
}
w.Write(categories.ToJson())
}
func updateCategoriesForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToUser(*c.App.Session(), c.Params.UserId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
auditRec := c.MakeAuditRecord("updateCategoriesForTeamForUser", audit.Fail)
defer c.LogAuditRec(auditRec)
categoriesUpdateRequest, err := model.SidebarCategoriesFromJson(r.Body)
if err != nil {
c.SetInvalidParam("category")
return
}
var channelsToCheck []string
for _, category := range categoriesUpdateRequest {
if !c.App.SessionHasPermissionToCategory(*c.App.Session(), c.Params.UserId, c.Params.TeamId, category.Id) {
c.SetInvalidParam("category")
return
}
channelsToCheck = append(channelsToCheck, category.Channels...)
}
if appErr := validateUserChannels("updateCategoriesForTeamForUser", c, c.Params.TeamId, c.Params.UserId, channelsToCheck); appErr != nil {
c.Err = appErr
return
}
categories, appErr := c.App.UpdateSidebarCategories(c.Params.UserId, c.Params.TeamId, categoriesUpdateRequest)
if appErr != nil {
c.Err = appErr
return
}
auditRec.Success()
w.Write(model.SidebarCategoriesWithChannelsToJson(categories))
}
func validateUserChannels(operationName string, c *Context, teamId, userId string, channelIDs []string) *model.AppError {
channels, err := c.App.GetChannelsForUser(teamId, userId, false)
if err != nil {
return model.NewAppError("Api4."+operationName, "api.invalid_channel", nil, err.Error(), http.StatusBadRequest)
}
for _, channelId := range channelIDs {
found := false
for _, channel := range *channels {
if channel.Id == channelId {
found = true
break
}
}
if !found {
return model.NewAppError("Api4."+operationName, "api.invalid_channel", nil, "", http.StatusBadRequest)
}
}
return nil
}
func updateCategoryForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId().RequireCategoryId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToCategory(*c.App.Session(), c.Params.UserId, c.Params.TeamId, c.Params.CategoryId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
auditRec := c.MakeAuditRecord("updateCategoryForTeamForUser", audit.Fail)
defer c.LogAuditRec(auditRec)
categoryUpdateRequest, err := model.SidebarCategoryFromJson(r.Body)
if err != nil || categoryUpdateRequest.TeamId != c.Params.TeamId || categoryUpdateRequest.UserId != c.Params.UserId {
c.SetInvalidParam("category")
return
}
if appErr := validateUserChannels("updateCategoryForTeamForUser", c, c.Params.TeamId, c.Params.UserId, categoryUpdateRequest.Channels); appErr != nil {
c.Err = appErr
return
}
categoryUpdateRequest.Id = c.Params.CategoryId
categories, appErr := c.App.UpdateSidebarCategories(c.Params.UserId, c.Params.TeamId, []*model.SidebarCategoryWithChannels{categoryUpdateRequest})
if appErr != nil {
c.Err = appErr
return
}
auditRec.Success()
w.Write(categories[0].ToJson())
}
func deleteCategoryForTeamForUser(c *Context, w http.ResponseWriter, r *http.Request) {
c.RequireUserId().RequireTeamId().RequireCategoryId()
if c.Err != nil {
return
}
if !c.App.SessionHasPermissionToCategory(*c.App.Session(), c.Params.UserId, c.Params.TeamId, c.Params.CategoryId) {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
return
}
auditRec := c.MakeAuditRecord("deleteCategoryForTeamForUser", audit.Fail)
defer c.LogAuditRec(auditRec)
appErr := c.App.DeleteSidebarCategory(c.Params.UserId, c.Params.TeamId, c.Params.CategoryId)
if appErr != nil {
c.Err = appErr
return
}
auditRec.Success()
ReturnStatusOK(w)
}

Просмотреть файл

@@ -3888,5 +3888,106 @@ func TestMoveChannel(t *testing.T) {
require.NotNil(t, resp.Error) require.NotNil(t, resp.Error)
CheckErrorMessage(t, resp, "app.channel.move_channel.members_do_not_match.error") CheckErrorMessage(t, resp, "app.channel.move_channel.members_do_not_match.error")
}) })
}
func TestUpdateCategoryForTeamForUser(t *testing.T) {
t.Run("should update the channel order of the Channels category", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
categories, resp := th.Client.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
require.Nil(t, resp.Error)
require.Len(t, categories.Categories, 3)
require.Len(t, categories.Order, 3)
channelsCategory := categories.Categories[1]
require.Equal(t, model.SidebarCategoryChannels, channelsCategory.Type)
require.Len(t, channelsCategory.Channels, 5) // Town Square, Off Topic, and the 3 channels created by InitBasic
// Should return the correct values from the API
updatedCategory := &model.SidebarCategoryWithChannels{
SidebarCategory: channelsCategory.SidebarCategory,
Channels: []string{channelsCategory.Channels[1], channelsCategory.Channels[0], channelsCategory.Channels[4], channelsCategory.Channels[3], channelsCategory.Channels[2]},
}
t.Log("UserId=" + th.BasicUser.Id)
t.Log("TeamId=" + th.BasicTeam.Id)
t.Log("category=" + channelsCategory.Id)
received, resp := th.Client.UpdateSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, channelsCategory.Id, updatedCategory)
assert.Nil(t, resp.Error)
assert.Equal(t, channelsCategory.Id, received.Id)
assert.Equal(t, updatedCategory.Channels, received.Channels)
// And when requesting the category later
received, resp = th.Client.GetSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, channelsCategory.Id, "")
assert.Nil(t, resp.Error)
assert.Equal(t, channelsCategory.Id, received.Id)
assert.Equal(t, updatedCategory.Channels, received.Channels)
})
t.Run("should update the sort order of the DM category", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
categories, resp := th.Client.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
require.Nil(t, resp.Error)
require.Len(t, categories.Categories, 3)
require.Len(t, categories.Order, 3)
dmsCategory := categories.Categories[2]
require.Equal(t, model.SidebarCategoryDirectMessages, dmsCategory.Type)
require.Equal(t, model.SidebarCategorySortRecent, dmsCategory.Sorting)
// Should return the correct values from the API
updatedCategory := &model.SidebarCategoryWithChannels{
SidebarCategory: dmsCategory.SidebarCategory,
Channels: dmsCategory.Channels,
}
updatedCategory.Sorting = model.SidebarCategorySortAlphabetical
received, resp := th.Client.UpdateSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, dmsCategory.Id, updatedCategory)
assert.Nil(t, resp.Error)
assert.Equal(t, dmsCategory.Id, received.Id)
assert.Equal(t, model.SidebarCategorySortAlphabetical, received.Sorting)
// And when requesting the category later
received, resp = th.Client.GetSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, dmsCategory.Id, "")
assert.Nil(t, resp.Error)
assert.Equal(t, dmsCategory.Id, received.Id)
assert.Equal(t, model.SidebarCategorySortAlphabetical, received.Sorting)
})
t.Run("should update the display name of a custom category", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
customCategory, resp := th.Client.CreateSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
UserId: th.BasicUser.Id,
TeamId: th.BasicTeam.Id,
DisplayName: "custom123",
},
})
require.Nil(t, resp.Error)
require.Equal(t, "custom123", customCategory.DisplayName)
// Should return the correct values from the API
updatedCategory := &model.SidebarCategoryWithChannels{
SidebarCategory: customCategory.SidebarCategory,
Channels: customCategory.Channels,
}
updatedCategory.DisplayName = "abcCustom"
received, resp := th.Client.UpdateSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, customCategory.Id, updatedCategory)
assert.Nil(t, resp.Error)
assert.Equal(t, customCategory.Id, received.Id)
assert.Equal(t, updatedCategory.DisplayName, received.DisplayName)
// And when requesting the category later
received, resp = th.Client.GetSidebarCategoryForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, customCategory.Id, "")
assert.Nil(t, resp.Error)
assert.Equal(t, customCategory.Id, received.Id)
assert.Equal(t, updatedCategory.DisplayName, received.DisplayName)
})
} }

Просмотреть файл

@@ -415,6 +415,7 @@ type AppIface interface {
CreateRole(role *model.Role) (*model.Role, *model.AppError) CreateRole(role *model.Role) (*model.Role, *model.AppError)
CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) CreateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError)
CreateSession(session *model.Session) (*model.Session, *model.AppError) CreateSession(session *model.Session) (*model.Session, *model.AppError)
CreateSidebarCategory(userId, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError)
CreateTeam(team *model.Team) (*model.Team, *model.AppError) CreateTeam(team *model.Team) (*model.Team, *model.AppError)
CreateTeamWithUser(team *model.Team, userId string) (*model.Team, *model.AppError) CreateTeamWithUser(team *model.Team, userId string) (*model.Team, *model.AppError)
CreateTermsOfService(text, userId string) (*model.TermsOfService, *model.AppError) CreateTermsOfService(text, userId string) (*model.TermsOfService, *model.AppError)
@@ -449,6 +450,7 @@ type AppIface interface {
DeletePreferences(userId string, preferences model.Preferences) *model.AppError DeletePreferences(userId string, preferences model.Preferences) *model.AppError
DeleteReactionForPost(reaction *model.Reaction) *model.AppError DeleteReactionForPost(reaction *model.Reaction) *model.AppError
DeleteScheme(schemeId string) (*model.Scheme, *model.AppError) DeleteScheme(schemeId string) (*model.Scheme, *model.AppError)
DeleteSidebarCategory(userId, teamId, categoryId string) *model.AppError
DeleteToken(token *model.Token) *model.AppError DeleteToken(token *model.Token) *model.AppError
DiagnosticId() string DiagnosticId() string
DisableAutoResponder(userId string, asAdmin bool) *model.AppError DisableAutoResponder(userId string, asAdmin bool) *model.AppError
@@ -633,6 +635,9 @@ type AppIface interface {
GetSession(token string) (*model.Session, *model.AppError) GetSession(token string) (*model.Session, *model.AppError)
GetSessionById(sessionId string) (*model.Session, *model.AppError) GetSessionById(sessionId string) (*model.Session, *model.AppError)
GetSessions(userId string) ([]*model.Session, *model.AppError) GetSessions(userId string) ([]*model.Session, *model.AppError)
GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError)
GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError)
GetSidebarCategoryOrder(userId, teamId string) ([]string, *model.AppError)
GetSinglePost(postId string) (*model.Post, *model.AppError) GetSinglePost(postId string) (*model.Post, *model.AppError)
GetSiteURL() string GetSiteURL() string
GetStatus(userId string) (*model.Status, *model.AppError) GetStatus(userId string) (*model.Status, *model.AppError)
@@ -861,6 +866,7 @@ type AppIface interface {
Session() *model.Session Session() *model.Session
SessionCacheLength() int SessionCacheLength() int
SessionHasPermissionTo(session model.Session, permission *model.Permission) bool SessionHasPermissionTo(session model.Session, permission *model.Permission) bool
SessionHasPermissionToCategory(session model.Session, userId, teamId, categoryId string) bool
SessionHasPermissionToChannel(session model.Session, channelId string, permission *model.Permission) bool SessionHasPermissionToChannel(session model.Session, channelId string, permission *model.Permission) bool
SessionHasPermissionToChannelByPost(session model.Session, postId string, permission *model.Permission) bool SessionHasPermissionToChannelByPost(session model.Session, postId string, permission *model.Permission) bool
SessionHasPermissionToTeam(session model.Session, teamId string, permission *model.Permission) bool SessionHasPermissionToTeam(session model.Session, teamId string, permission *model.Permission) bool
@@ -953,6 +959,8 @@ type AppIface interface {
UpdateRole(role *model.Role) (*model.Role, *model.AppError) UpdateRole(role *model.Role) (*model.Role, *model.AppError)
UpdateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError) UpdateScheme(scheme *model.Scheme) (*model.Scheme, *model.AppError)
UpdateSessionsIsGuest(userId string, isGuest bool) UpdateSessionsIsGuest(userId string, isGuest bool)
UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError)
UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []string) *model.AppError
UpdateTeam(team *model.Team) (*model.Team, *model.AppError) UpdateTeam(team *model.Team) (*model.Team, *model.AppError)
UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError)
UpdateTeamMemberSchemeRoles(teamId string, userId string, isSchemeGuest bool, isSchemeUser bool, isSchemeAdmin bool) (*model.TeamMember, *model.AppError) UpdateTeamMemberSchemeRoles(teamId string, userId string, isSchemeGuest bool, isSchemeUser bool, isSchemeAdmin bool) (*model.TeamMember, *model.AppError)

Просмотреть файл

@@ -89,6 +89,14 @@ func (a *App) SessionHasPermissionToChannelByPost(session model.Session, postId
return a.SessionHasPermissionTo(session, permission) return a.SessionHasPermissionTo(session, permission)
} }
func (a *App) SessionHasPermissionToCategory(session model.Session, userId, teamId, categoryId string) bool {
if a.SessionHasPermissionTo(session, model.PERMISSION_EDIT_OTHER_USERS) {
return true
}
category, err := a.GetSidebarCategory(categoryId)
return err == nil && category != nil && category.UserId == session.UserId && category.UserId == userId && category.TeamId == teamId
}
func (a *App) SessionHasPermissionToUser(session model.Session, userId string) bool { func (a *App) SessionHasPermissionToUser(session model.Session, userId string) bool {
if userId == "" { if userId == "" {
return false return false

Просмотреть файл

@@ -2383,6 +2383,10 @@ func (a *App) MoveChannel(team *model.Team, channel *model.Channel, user *model.
return err return err
} }
if appErr := a.Srv().Store.Channel().UpdateSidebarChannelCategoryOnMove(channel, team.Id); appErr != nil {
return appErr
}
channel.TeamId = team.Id channel.TeamId = team.Id
if _, err := a.Srv().Store.Channel().Update(channel); err != nil { if _, err := a.Srv().Store.Channel().Update(channel); err != nil {
var appErr *model.AppError var appErr *model.AppError
@@ -2598,3 +2602,70 @@ func (a *App) ClearChannelMembersCache(channelID string) {
page++ page++
} }
} }
func (a *App) createInitialSidebarCategories(user *model.User, team *model.Team) *model.AppError {
nErr := a.Srv().Store.Channel().CreateInitialSidebarCategories(user, team.Id)
if nErr != nil {
return model.NewAppError("createInitialSidebarCategories", "app.channel.create_initial_sidebar_categories.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
}
return nil
}
func (a *App) GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
return a.Srv().Store.Channel().GetSidebarCategories(userId, teamId)
}
func (a *App) GetSidebarCategoryOrder(userId, teamId string) ([]string, *model.AppError) {
return a.Srv().Store.Channel().GetSidebarCategoryOrder(userId, teamId)
}
func (a *App) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
return a.Srv().Store.Channel().GetSidebarCategory(categoryId)
}
func (a *App) CreateSidebarCategory(userId, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
category, err := a.Srv().Store.Channel().CreateSidebarCategory(userId, teamId, newCategory)
if err != nil {
return nil, err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_SIDEBAR_CATEGORY_CREATED, teamId, "", userId, nil)
message.Add("category_id", category.Id)
a.Publish(message)
return category, nil
}
func (a *App) UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []string) *model.AppError {
err := a.Srv().Store.Channel().UpdateSidebarCategoryOrder(userId, teamId, categoryOrder)
if err != nil {
return err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_SIDEBAR_CATEGORY_ORDER_UPDATED, teamId, "", userId, nil)
message.Add("order", categoryOrder)
a.Publish(message)
return nil
}
func (a *App) UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
result, err := a.Srv().Store.Channel().UpdateSidebarCategories(userId, teamId, categories)
if err != nil {
return nil, err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_SIDEBAR_CATEGORY_UPDATED, teamId, "", userId, nil)
a.Publish(message)
return result, nil
}
func (a *App) DeleteSidebarCategory(userId, teamId, categoryId string) *model.AppError {
err := a.Srv().Store.Channel().DeleteSidebarCategory(categoryId)
if err != nil {
return err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_SIDEBAR_CATEGORY_DELETED, teamId, "", userId, nil)
message.Add("category_id", categoryId)
a.Publish(message)
return nil
}

Просмотреть файл

@@ -1795,3 +1795,66 @@ func TestMarkChannelsAsViewedPanic(t *testing.T) {
_, err := th.App.MarkChannelsAsViewed([]string{"channelID"}, "userID", th.App.Session().Id) _, err := th.App.MarkChannelsAsViewed([]string{"channelID"}, "userID", th.App.Session().Id)
require.Nil(t, err) require.Nil(t, err)
} }
func TestSidebarCategory(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
basicChannel2 := th.CreateChannel(th.BasicTeam)
defer th.App.PermanentDeleteChannel(basicChannel2)
user := th.CreateUser()
defer th.App.Srv().Store.User().PermanentDelete(user.Id)
th.LinkUserToTeam(user, th.BasicTeam)
th.AddUserToChannel(user, basicChannel2)
var createdCategory *model.SidebarCategoryWithChannels
t.Run("CreateSidebarCategory", func(t *testing.T) {
catData := model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
DisplayName: "TEST",
},
Channels: []string{th.BasicChannel.Id, basicChannel2.Id, basicChannel2.Id},
}
_, err := th.App.CreateSidebarCategory(user.Id, th.BasicTeam.Id, &catData)
require.NotNil(t, err, "Should return error due to duplicate IDs")
catData.Channels = []string{th.BasicChannel.Id, basicChannel2.Id}
cat, err := th.App.CreateSidebarCategory(user.Id, th.BasicTeam.Id, &catData)
require.Nil(t, err, "Expected no error")
require.NotNil(t, cat, "Expected category object, got nil")
createdCategory = cat
})
t.Run("UpdateSidebarCategories", func(t *testing.T) {
require.NotNil(t, createdCategory)
createdCategory.Channels = []string{th.BasicChannel.Id}
updatedCat, err := th.App.UpdateSidebarCategories(user.Id, th.BasicTeam.Id, []*model.SidebarCategoryWithChannels{createdCategory})
require.Nil(t, err, "Expected no error")
require.NotNil(t, updatedCat, "Expected category object, got nil")
require.Len(t, updatedCat, 1)
require.Len(t, updatedCat[0].Channels, 1)
require.Equal(t, updatedCat[0].Channels[0], th.BasicChannel.Id)
})
t.Run("UpdateSidebarCategoryOrder", func(t *testing.T) {
err := th.App.UpdateSidebarCategoryOrder(user.Id, th.BasicTeam.Id, []string{th.BasicChannel.Id, basicChannel2.Id})
require.NotNil(t, err, "Should return error due to invalid order")
actualOrder, err := th.App.GetSidebarCategoryOrder(user.Id, th.BasicTeam.Id)
require.Nil(t, err, "Should fetch order successfully")
actualOrder[2], actualOrder[3] = actualOrder[3], actualOrder[2]
err = th.App.UpdateSidebarCategoryOrder(user.Id, th.BasicTeam.Id, actualOrder)
require.Nil(t, err, "Should update order successfully")
actualOrder[2] = "asd"
err = th.App.UpdateSidebarCategoryOrder(user.Id, th.BasicTeam.Id, actualOrder)
require.NotNil(t, err, "Should return error due to invalid id")
})
t.Run("GetSidebarCategoryOrder", func(t *testing.T) {
catOrder, err := th.App.GetSidebarCategoryOrder(user.Id, th.BasicTeam.Id)
require.Nil(t, err, "Expected no error")
require.Len(t, catOrder, 4)
require.Equal(t, catOrder[1], createdCategory.Id, "the newly created category should be after favorites")
})
}

Просмотреть файл

@@ -2025,6 +2025,28 @@ func (a *OpenTracingAppLayer) CreateSession(session *model.Session) (*model.Sess
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) CreateSidebarCategory(userId string, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateSidebarCategory")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.CreateSidebarCategory(userId, teamId, newCategory)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) CreateTeam(team *model.Team) (*model.Team, *model.AppError) { func (a *OpenTracingAppLayer) CreateTeam(team *model.Team) (*model.Team, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateTeam") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CreateTeam")
@@ -2857,6 +2879,28 @@ func (a *OpenTracingAppLayer) DeleteScheme(schemeId string) (*model.Scheme, *mod
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) DeleteSidebarCategory(userId string, teamId string, categoryId string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteSidebarCategory")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.DeleteSidebarCategory(userId, teamId, categoryId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) DeleteToken(token *model.Token) *model.AppError { func (a *OpenTracingAppLayer) DeleteToken(token *model.Token) *model.AppError {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteToken") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.DeleteToken")
@@ -7539,6 +7583,72 @@ func (a *OpenTracingAppLayer) GetSessions(userId string) ([]*model.Session, *mod
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (a *OpenTracingAppLayer) GetSidebarCategories(userId string, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSidebarCategories")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetSidebarCategories(userId, teamId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSidebarCategory")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetSidebarCategory(categoryId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetSidebarCategoryOrder(userId string, teamId string) ([]string, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSidebarCategoryOrder")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.GetSidebarCategoryOrder(userId, teamId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) GetSinglePost(postId string) (*model.Post, *model.AppError) { func (a *OpenTracingAppLayer) GetSinglePost(postId string) (*model.Post, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSinglePost") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSinglePost")
@@ -12642,6 +12752,23 @@ func (a *OpenTracingAppLayer) SessionHasPermissionTo(session model.Session, perm
return resultVar0 return resultVar0
} }
func (a *OpenTracingAppLayer) SessionHasPermissionToCategory(session model.Session, userId string, teamId string, categoryId string) bool {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SessionHasPermissionToCategory")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.SessionHasPermissionToCategory(session, userId, teamId, categoryId)
return resultVar0
}
func (a *OpenTracingAppLayer) SessionHasPermissionToChannel(session model.Session, channelId string, permission *model.Permission) bool { func (a *OpenTracingAppLayer) SessionHasPermissionToChannel(session model.Session, channelId string, permission *model.Permission) bool {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SessionHasPermissionToChannel") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.SessionHasPermissionToChannel")
@@ -14484,6 +14611,50 @@ func (a *OpenTracingAppLayer) UpdateSessionsIsGuest(userId string, isGuest bool)
a.app.UpdateSessionsIsGuest(userId, isGuest) a.app.UpdateSessionsIsGuest(userId, isGuest)
} }
func (a *OpenTracingAppLayer) UpdateSidebarCategories(userId string, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateSidebarCategories")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.UpdateSidebarCategories(userId, teamId, categories)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (a *OpenTracingAppLayer) UpdateSidebarCategoryOrder(userId string, teamId string, categoryOrder []string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateSidebarCategoryOrder")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.UpdateSidebarCategoryOrder(userId, teamId, categoryOrder)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) { func (a *OpenTracingAppLayer) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) {
origCtx := a.ctx origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateTeam") span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateTeam")

Просмотреть файл

@@ -53,7 +53,13 @@ func (a *App) UpdatePreferences(userId string, preferences model.Preferences) *m
return err return err
} }
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_CHANGED, "", "", userId, nil) if err := a.Srv().Store.Channel().UpdateSidebarChannelsByPreferences(&preferences); err != nil {
return err
}
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_SIDEBAR_CATEGORY_UPDATED, "", "", userId, nil)
a.Publish(message)
message = model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_CHANGED, "", "", userId, nil)
message.Add("preferences", preferences.ToJson()) message.Add("preferences", preferences.ToJson())
a.Publish(message) a.Publish(message)

Просмотреть файл

@@ -653,6 +653,15 @@ func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId
return err return err
} }
if err := a.createInitialSidebarCategories(user, team); err != nil {
mlog.Error(
"Encountered an issue creating default sidebar categories.",
mlog.String("user_id", user.Id),
mlog.String("team_id", team.Id),
mlog.Err(err),
)
}
shouldBeAdmin := team.Email == user.Email shouldBeAdmin := team.Email == user.Email
if !user.IsGuest() { if !user.IsGuest() {
@@ -969,6 +978,10 @@ func (a *App) RemoveTeamMemberFromTeam(teamMember *model.TeamMember, requestorId
return err return err
} }
if err := a.Srv().Store.Channel().ClearSidebarOnTeamLeave(user.Id, teamMember.TeamId); err != nil {
return err
}
// delete the preferences that set the last channel used in the team and other team specific preferences // delete the preferences that set the last channel used in the team and other team specific preferences
if err := a.Srv().Store.Preference().DeleteCategory(user.Id, teamMember.TeamId); err != nil { if err := a.Srv().Store.Preference().DeleteCategory(user.Id, teamMember.TeamId); err != nil {
return err return err
@@ -1031,8 +1044,7 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
} }
} }
err = a.RemoveTeamMemberFromTeam(teamMember, requestorId) if err := a.RemoveTeamMemberFromTeam(teamMember, requestorId); err != nil {
if err != nil {
return err return err
} }

Просмотреть файл

@@ -165,7 +165,21 @@ func TestAddUserToTeam(t *testing.T) {
_, err = th.App.AddUserToTeam(th.BasicTeam.Id, ruser3.Id, "") _, err = th.App.AddUserToTeam(th.BasicTeam.Id, ruser3.Id, "")
require.NotNil(t, err, "Should not have allowed restricted user3") require.NotNil(t, err, "Should not have allowed restricted user3")
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam") require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
})
t.Run("should set up initial sidebar categories when joining a team", func(t *testing.T) {
user := th.CreateUser()
team := th.CreateTeam()
_, err := th.App.AddUserToTeam(team.Id, user.Id, "")
require.Nil(t, err)
res, err := th.App.GetSidebarCategories(user.Id, team.Id)
require.Nil(t, err)
assert.Len(t, res.Categories, 3)
assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type)
assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type)
assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type)
}) })
} }
@@ -390,6 +404,27 @@ func TestAddUserToTeamByToken(t *testing.T) {
require.NotNil(t, err, "Should not add restricted user") require.NotNil(t, err, "Should not add restricted user")
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam") require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
}) })
t.Run("should set up initial sidebar categories when joining a team by token", func(t *testing.T) {
user := th.CreateUser()
team := th.CreateTeam()
token := model.NewToken(
TOKEN_TYPE_TEAM_INVITATION,
model.MapToJson(map[string]string{"teamId": team.Id}),
)
require.Nil(t, th.App.Srv().Store.Token().Save(token))
_, err := th.App.AddUserToTeamByToken(user.Id, token.Token)
require.Nil(t, err)
res, err := th.App.GetSidebarCategories(user.Id, team.Id)
require.Nil(t, err)
assert.Len(t, res.Categories, 3)
assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type)
assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type)
assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type)
})
} }
func TestAddUserToTeamByTeamId(t *testing.T) { func TestAddUserToTeamByTeamId(t *testing.T) {

Просмотреть файл

@@ -1428,6 +1428,10 @@
"id": "api.incoming_webhook.invalid_username.app_error", "id": "api.incoming_webhook.invalid_username.app_error",
"translation": "Invalid username." "translation": "Invalid username."
}, },
{
"id": "api.invalid_channel",
"translation": "Channel listed in the request doesn't belong to the user"
},
{ {
"id": "api.io_error", "id": "api.io_error",
"translation": "input/output error" "translation": "input/output error"
@@ -3030,6 +3034,10 @@
"id": "app.channel.create_direct_channel.internal_error", "id": "app.channel.create_direct_channel.internal_error",
"translation": "Unable to save direct channel." "translation": "Unable to save direct channel."
}, },
{
"id": "app.channel.create_initial_sidebar_categories.internal_error",
"translation": "Unable to create initial sidebar categories for user."
},
{ {
"id": "app.channel.delete.app_error", "id": "app.channel.delete.app_error",
"translation": "Unable to delete the channel." "translation": "Unable to delete the channel."
@@ -4862,6 +4870,14 @@
"id": "migrations.worker.run_migration.unknown_key", "id": "migrations.worker.run_migration.unknown_key",
"translation": "Unable to run migration job due to unknown migration key." "translation": "Unable to run migration job due to unknown migration key."
}, },
{
"id": "migrations.worker.run_sidebar_categories_phase_2_migration.internal_error",
"translation": "Migration failed due to database error."
},
{
"id": "migrations.worker.run_sidebar_categories_phase_2_migration.invalid_progress",
"translation": "Migration failed due to invalid progress data."
},
{ {
"id": "model.access.is_valid.access_token.app_error", "id": "model.access.is_valid.access_token.app_error",
"translation": "Invalid access token." "translation": "Invalid access token."
@@ -6194,6 +6210,18 @@
"id": "searchengine.bleve.disabled.error", "id": "searchengine.bleve.disabled.error",
"translation": "Error purging Bleve indexes: engine is disabled" "translation": "Error purging Bleve indexes: engine is disabled"
}, },
{
"id": "sidebar.category.channels",
"translation": "Channels"
},
{
"id": "sidebar.category.dm",
"translation": "Direct Messages"
},
{
"id": "sidebar.category.favorites",
"translation": "Favorites"
},
{ {
"id": "store.insert_error", "id": "store.insert_error",
"translation": "insert error" "translation": "insert error"
@@ -6426,6 +6454,22 @@
"id": "store.sql_channel.search_group_channels.app_error", "id": "store.sql_channel.search_group_channels.app_error",
"translation": "Unable to get the group channels for the given user and term." "translation": "Unable to get the group channels for the given user and term."
}, },
{
"id": "store.sql_channel.sidebar_categories.app_error",
"translation": "Failed to insert record to database."
},
{
"id": "store.sql_channel.sidebar_categories.commit_transaction.app_error",
"translation": "Unable to commit transaction."
},
{
"id": "store.sql_channel.sidebar_categories.delete_invalid.app_error",
"translation": "Unable to delete non-custom category."
},
{
"id": "store.sql_channel.sidebar_categories.open_transaction.app_error",
"translation": "Failed to open the database transaction."
},
{ {
"id": "store.sql_channel.update_last_viewed_at.app_error", "id": "store.sql_channel.update_last_viewed_at.app_error",
"translation": "Unable to update the last viewed at time." "translation": "Unable to update the last viewed at time."

Просмотреть файл

@@ -32,6 +32,7 @@ func init() {
func MakeMigrationsList() []string { func MakeMigrationsList() []string {
return []string{ return []string{
model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2, model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2,
model.MIGRATION_KEY_SIDEBAR_CATEGORIES_PHASE_2,
} }
} }

128
migrations/sidebar_categories_phase_2.go Обычный файл
Просмотреть файл

@@ -0,0 +1,128 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package migrations
import (
"encoding/json"
"io"
"net/http"
"strings"
"github.com/mattermost/mattermost-server/v5/model"
)
type ProgressStep string
const (
StepCategories ProgressStep = "populateSidebarCategories"
StepFavorites ProgressStep = "migrateFavoriteChannelToSidebarChannels"
StepEnd ProgressStep = "endMigration"
)
type Progress struct {
CurrentStep ProgressStep `json:"current_state"`
LastTeamId string `json:"last_team_id"`
LastChannelId string `json:"last_channel_id"`
LastUserId string `json:"last_user"`
LastSortOrder int64 `json:"last_sort_order"`
}
func (p *Progress) ToJson() string {
b, _ := json.Marshal(p)
return string(b)
}
func ProgressFromJson(data io.Reader) *Progress {
var o *Progress
json.NewDecoder(data).Decode(&o)
return o
}
func (p *Progress) IsValid() bool {
if len(p.LastChannelId) != 26 {
return false
}
if len(p.LastTeamId) != 26 {
return false
}
if len(p.LastUserId) != 26 {
return false
}
switch p.CurrentStep {
case StepCategories, StepFavorites:
return true
default:
return false
}
}
func newProgress(step ProgressStep) *Progress {
progress := new(Progress)
progress.CurrentStep = step
progress.LastChannelId = strings.Repeat("0", 26)
progress.LastTeamId = strings.Repeat("0", 26)
progress.LastUserId = strings.Repeat("0", 26)
progress.LastSortOrder = 0
return progress
}
func (worker *Worker) runSidebarCategoriesPhase2Migration(lastDone string) (bool, string, *model.AppError) {
var progress *Progress
if len(lastDone) == 0 {
progress = newProgress(StepCategories)
} else {
progress = ProgressFromJson(strings.NewReader(lastDone))
if !progress.IsValid() {
return false, "", model.NewAppError("MigrationsWorker.runSidebarCategoriesPhase2Migration", "migrations.worker.run_sidebar_categories_phase_2_migration.invalid_progress", map[string]interface{}{"progress": progress.ToJson()}, "", http.StatusInternalServerError)
}
}
var result map[string]interface{}
var nErr error
var nextStep ProgressStep
switch progress.CurrentStep {
case StepCategories:
result, nErr = worker.srv.Store.Channel().MigrateSidebarCategories(progress.LastTeamId, progress.LastUserId)
nextStep = StepFavorites
case StepFavorites:
result, nErr = worker.srv.Store.Channel().MigrateFavoritesToSidebarChannels(progress.LastUserId, progress.LastSortOrder)
nextStep = StepEnd
default:
return false, "", model.NewAppError("MigrationsWorker.runSidebarCategoriesPhase2Migration", "migrations.worker.run_sidebar_categories_phase_2_migration.invalid_progress", map[string]interface{}{"progress": progress.ToJson()}, "", http.StatusInternalServerError)
}
if nErr != nil {
return false, progress.ToJson(), model.NewAppError("MigrationsWorker.runSidebarCategoriesPhase2Migration", "migrations.worker.run_sidebar_categories_phase_2_migration.internal_error", nil, nErr.Error(), http.StatusInternalServerError)
}
if result == nil {
// We haven't progressed. That means that we've reached the end of this stage of the migration, and should now advance to the next stage or stop
if nextStep != StepEnd {
progress = newProgress(nextStep)
return false, progress.ToJson(), nil
}
return true, progress.ToJson(), nil
}
progress.LastChannelId = strings.Repeat("0", 26)
progress.LastTeamId = strings.Repeat("0", 26)
progress.LastUserId = strings.Repeat("0", 26)
progress.LastSortOrder = 0
if val, ok := result["UserId"].(string); ok {
progress.LastUserId = val
}
if val, ok := result["TeamId"].(string); ok {
progress.LastTeamId = val
}
if val, ok := result["ChannelId"].(string); ok {
progress.LastChannelId = val
}
if val, ok := result["SortOrder"].(int64); ok {
progress.LastSortOrder = val
}
return false, progress.ToJson(), nil
}

Просмотреть файл

@@ -150,6 +150,8 @@ func (worker *Worker) runMigration(key string, lastDone string) (bool, string, *
var err *model.AppError var err *model.AppError
switch key { switch key {
case model.MIGRATION_KEY_SIDEBAR_CATEGORIES_PHASE_2:
done, progress, err = worker.runSidebarCategoriesPhase2Migration(lastDone)
case model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2: case model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2:
done, progress, err = worker.runAdvancedPermissionsPhase2Migration(lastDone) done, progress, err = worker.runAdvancedPermissionsPhase2Migration(lastDone)
default: default:

111
model/channel_sidebar.go Обычный файл
Просмотреть файл

@@ -0,0 +1,111 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
)
type SidebarCategoryType string
type SidebarCategorySorting string
const (
// Each sidebar category has a 'type'. System categories are Channels, Favorites and DMs
// All user-created categories will have type Custom
SidebarCategoryChannels SidebarCategoryType = "channels"
SidebarCategoryDirectMessages SidebarCategoryType = "direct_messages"
SidebarCategoryFavorites SidebarCategoryType = "favorites"
SidebarCategoryCustom SidebarCategoryType = "custom"
// Increment to use when adding/reordering things in the sidebar
MinimalSidebarSortDistance = 10
// Default Sort Orders for categories
DefaultSidebarSortOrderFavorites = 0
DefaultSidebarSortOrderChannels = DefaultSidebarSortOrderFavorites + MinimalSidebarSortDistance
DefaultSidebarSortOrderDMs = DefaultSidebarSortOrderChannels + MinimalSidebarSortDistance
// Sorting modes
// default for all categories except DMs (behaves like manual)
SidebarCategorySortDefault SidebarCategorySorting = ""
// sort manually
SidebarCategorySortManual SidebarCategorySorting = "manual"
// sort by recency (default for DMs)
SidebarCategorySortRecent SidebarCategorySorting = "recent"
// sort by display name alphabetically
SidebarCategorySortAlphabetical SidebarCategorySorting = "alpha"
)
// SidebarCategory represents the corresponding DB table
// SortOrder is never returned to the user and only used for queries
type SidebarCategory struct {
Id string `json:"id"`
UserId string `json:"user_id"`
TeamId string `json:"team_id"`
SortOrder int64 `json:"-"`
Sorting SidebarCategorySorting `json:"sorting"`
Type SidebarCategoryType `json:"type"`
DisplayName string `json:"display_name"`
}
// SidebarCategoryWithChannels combines data from SidebarCategory table with the Channel IDs that belong to that category
type SidebarCategoryWithChannels struct {
SidebarCategory
Channels []string `json:"channel_ids"`
}
type SidebarCategoryOrder []string
// OrderedSidebarCategories combines categories, their channel IDs and an array of Category IDs, sorted
type OrderedSidebarCategories struct {
Categories SidebarCategoriesWithChannels `json:"categories"`
Order SidebarCategoryOrder `json:"order"`
}
type SidebarChannel struct {
ChannelId string `json:"channel_id"`
UserId string `json:"user_id"`
CategoryId string `json:"category_id"`
SortOrder int64 `json:"-"`
}
type SidebarChannels []*SidebarChannel
type SidebarCategoriesWithChannels []*SidebarCategoryWithChannels
func SidebarCategoryFromJson(data io.Reader) (*SidebarCategoryWithChannels, error) {
var o *SidebarCategoryWithChannels
err := json.NewDecoder(data).Decode(&o)
return o, err
}
func SidebarCategoriesFromJson(data io.Reader) ([]*SidebarCategoryWithChannels, error) {
var o []*SidebarCategoryWithChannels
err := json.NewDecoder(data).Decode(&o)
return o, err
}
func OrderedSidebarCategoriesFromJson(data io.Reader) (*OrderedSidebarCategories, error) {
var o *OrderedSidebarCategories
err := json.NewDecoder(data).Decode(&o)
return o, err
}
func (o SidebarCategoryWithChannels) ToJson() []byte {
b, _ := json.Marshal(o)
return b
}
func SidebarCategoriesWithChannelsToJson(o []*SidebarCategoryWithChannels) []byte {
if b, err := json.Marshal(o); err != nil {
return []byte("[]")
} else {
return b
}
}
func (o OrderedSidebarCategories) ToJson() []byte {
if b, err := json.Marshal(o); err != nil {
return []byte("[]")
} else {
return b
}
}

Просмотреть файл

@@ -174,6 +174,10 @@ func (c *Client4) GetUserRoute(userId string) string {
return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId) return fmt.Sprintf(c.GetUsersRoute()+"/%v", userId)
} }
func (c *Client4) GetUserCategoryRoute(userID, teamID string) string {
return c.GetUserRoute(userID) + c.GetTeamRoute(teamID) + "/channels/categories"
}
func (c *Client4) GetUserAccessTokensRoute() string { func (c *Client4) GetUserAccessTokensRoute() string {
return fmt.Sprintf(c.GetUsersRoute() + "/tokens") return fmt.Sprintf(c.GetUsersRoute() + "/tokens")
} }
@@ -5212,3 +5216,83 @@ func (c *Client4) GetGroupStats(groupID string) (*GroupStats, *Response) {
defer closeBody(r) defer closeBody(r)
return GroupStatsFromJson(r.Body), BuildResponse(r) return GroupStatsFromJson(r.Body), BuildResponse(r)
} }
func (c *Client4) GetSidebarCategoriesForTeamForUser(userID, teamID, etag string) (*OrderedSidebarCategories, *Response) {
route := c.GetUserCategoryRoute(userID, teamID)
r, appErr := c.DoApiGet(route, etag)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
cat, err := OrderedSidebarCategoriesFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.GetSidebarCategoriesForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return cat, BuildResponse(r)
}
func (c *Client4) CreateSidebarCategoryForTeamForUser(userID, teamID string, category *SidebarCategoryWithChannels) (*SidebarCategoryWithChannels, *Response) {
payload, _ := json.Marshal(category)
route := c.GetUserCategoryRoute(userID, teamID)
r, appErr := c.doApiPostBytes(route, payload)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
cat, err := SidebarCategoryFromJson(r.Body)
if err != nil {
return nil, BuildErrorResponse(r, NewAppError("Client4.CreateSidebarCategoryForTeamForUser", "model.utils.decode_json.app_error", nil, err.Error(), r.StatusCode))
}
return cat, BuildResponse(r)
}
func (c *Client4) GetSidebarCategoryOrderForTeamForUser(userID, teamID, etag string) ([]string, *Response) {
route := c.GetUserCategoryRoute(userID, teamID) + "/order"
r, err := c.DoApiGet(route, etag)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) UpdateSidebarCategoryOrderForTeamForUser(userID, teamID string, order []string) ([]string, *Response) {
payload, _ := json.Marshal(order)
route := c.GetUserCategoryRoute(userID, teamID) + "/order"
r, err := c.doApiPutBytes(route, payload)
if err != nil {
return nil, BuildErrorResponse(r, err)
}
defer closeBody(r)
return ArrayFromJson(r.Body), BuildResponse(r)
}
func (c *Client4) GetSidebarCategoryForTeamForUser(userID, teamID, categoryID, etag string) (*SidebarCategoryWithChannels, *Response) {
route := c.GetUserCategoryRoute(userID, teamID) + "/" + categoryID
r, appErr := c.DoApiGet(route, etag)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
cat, err := SidebarCategoryFromJson(r.Body)
if err != nil {
return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError(c.GetUserRoute(userID), "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)}
}
return cat, BuildResponse(r)
}
func (c *Client4) UpdateSidebarCategoryForTeamForUser(userID, teamID, categoryID string, category *SidebarCategoryWithChannels) (*SidebarCategoryWithChannels, *Response) {
payload, _ := json.Marshal(category)
route := c.GetUserCategoryRoute(userID, teamID) + "/" + categoryID
r, appErr := c.doApiPutBytes(route, payload)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
cat, err := SidebarCategoryFromJson(r.Body)
if err != nil {
return nil, &Response{StatusCode: http.StatusBadRequest, Error: NewAppError(c.GetUserRoute(userID), "model.client.connecting.app_error", nil, err.Error(), http.StatusForbidden)}
}
return cat, BuildResponse(r)
}

Просмотреть файл

@@ -17,4 +17,6 @@ const (
MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS = "add_manage_guests_permissions" MIGRATION_KEY_ADD_MANAGE_GUESTS_PERMISSIONS = "add_manage_guests_permissions"
MIGRATION_KEY_CHANNEL_MODERATIONS_PERMISSIONS = "channel_moderations_permissions" MIGRATION_KEY_CHANNEL_MODERATIONS_PERMISSIONS = "channel_moderations_permissions"
MIGRATION_KEY_ADD_USE_GROUP_MENTIONS_PERMISSION = "add_use_group_mentions_permission" MIGRATION_KEY_ADD_USE_GROUP_MENTIONS_PERMISSION = "add_use_group_mentions_permission"
MIGRATION_KEY_SIDEBAR_CATEGORIES_PHASE_2 = "migration_sidebar_categories_phase_2"
) )

Просмотреть файл

@@ -14,6 +14,7 @@ import (
const ( const (
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show" PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
PREFERENCE_CATEGORY_GROUP_CHANNEL_SHOW = "group_channel_show"
PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step"
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post" PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"

Просмотреть файл

@@ -62,6 +62,10 @@ const (
WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_TEAM = "received_group_not_associated_to_team" WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_TEAM = "received_group_not_associated_to_team"
WEBSOCKET_EVENT_RECEIVED_GROUP_ASSOCIATED_TO_CHANNEL = "received_group_associated_to_channel" WEBSOCKET_EVENT_RECEIVED_GROUP_ASSOCIATED_TO_CHANNEL = "received_group_associated_to_channel"
WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_CHANNEL = "received_group_not_associated_to_channel" WEBSOCKET_EVENT_RECEIVED_GROUP_NOT_ASSOCIATED_TO_CHANNEL = "received_group_not_associated_to_channel"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_CREATED = "sidebar_category_created"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_UPDATED = "sidebar_category_updated"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_DELETED = "sidebar_category_deleted"
WEBSOCKET_EVENT_SIDEBAR_CATEGORY_ORDER_UPDATED = "sidebar_category_order_updated"
) )
type WebSocketMessage interface { type WebSocketMessage interface {

Просмотреть файл

@@ -576,6 +576,24 @@ func (s *OpenTracingLayerChannelStore) ClearCaches() {
} }
func (s *OpenTracingLayerChannelStore) ClearSidebarOnTeamLeave(userId string, teamId string) *model.AppError {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.ClearSidebarOnTeamLeave")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0 := s.ChannelStore.ClearSidebarOnTeamLeave(userId, teamId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (s *OpenTracingLayerChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) { func (s *OpenTracingLayerChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CountPostsAfter") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CountPostsAfter")
@@ -612,6 +630,42 @@ func (s *OpenTracingLayerChannelStore) CreateDirectChannel(userId *model.User, o
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *OpenTracingLayerChannelStore) CreateInitialSidebarCategories(user *model.User, teamId string) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CreateInitialSidebarCategories")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0 := s.ChannelStore.CreateInitialSidebarCategories(user, teamId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (s *OpenTracingLayerChannelStore) CreateSidebarCategory(userId string, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CreateSidebarCategory")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.CreateSidebarCategory(userId, teamId, newCategory)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) Delete(channelId string, time int64) error { func (s *OpenTracingLayerChannelStore) Delete(channelId string, time int64) error {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.Delete") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.Delete")
@@ -630,6 +684,24 @@ func (s *OpenTracingLayerChannelStore) Delete(channelId string, time int64) erro
return resultVar0 return resultVar0
} }
func (s *OpenTracingLayerChannelStore) DeleteSidebarCategory(categoryId string) *model.AppError {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.DeleteSidebarCategory")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0 := s.ChannelStore.DeleteSidebarCategory(categoryId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (s *OpenTracingLayerChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) { func (s *OpenTracingLayerChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.Get") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.Get")
@@ -1327,6 +1399,60 @@ func (s *OpenTracingLayerChannelStore) GetPublicChannelsForTeam(teamId string, o
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *OpenTracingLayerChannelStore) GetSidebarCategories(userId string, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetSidebarCategories")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.GetSidebarCategories(userId, teamId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetSidebarCategory")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.GetSidebarCategory(categoryId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) GetSidebarCategoryOrder(userId string, teamId string) ([]string, *model.AppError) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetSidebarCategoryOrder")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.GetSidebarCategoryOrder(userId, teamId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) { func (s *OpenTracingLayerChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetTeamChannels") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetTeamChannels")
@@ -1503,6 +1629,24 @@ func (s *OpenTracingLayerChannelStore) MigrateChannelMembers(fromChannelId strin
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *OpenTracingLayerChannelStore) MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.MigrateFavoritesToSidebarChannels")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.MigrateFavoritesToSidebarChannels(lastUserId, runningOrder)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) MigratePublicChannels() error { func (s *OpenTracingLayerChannelStore) MigratePublicChannels() error {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.MigratePublicChannels") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.MigratePublicChannels")
@@ -1521,6 +1665,24 @@ func (s *OpenTracingLayerChannelStore) MigratePublicChannels() error {
return resultVar0 return resultVar0
} }
func (s *OpenTracingLayerChannelStore) MigrateSidebarCategories(fromTeamId string, fromUserId string) (map[string]interface{}, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.MigrateSidebarCategories")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.MigrateSidebarCategories(fromTeamId, fromUserId)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) PermanentDelete(channelId string) error { func (s *OpenTracingLayerChannelStore) PermanentDelete(channelId string) error {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.PermanentDelete") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.PermanentDelete")
@@ -1989,6 +2151,78 @@ func (s *OpenTracingLayerChannelStore) UpdateMultipleMembers(members []*model.Ch
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *OpenTracingLayerChannelStore) UpdateSidebarCategories(userId string, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateSidebarCategories")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0, resultVar1 := s.ChannelStore.UpdateSidebarCategories(userId, teamId, categories)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))
ext.Error.Set(span, true)
}
return resultVar0, resultVar1
}
func (s *OpenTracingLayerChannelStore) UpdateSidebarCategoryOrder(userId string, teamId string, categoryOrder []string) *model.AppError {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateSidebarCategoryOrder")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0 := s.ChannelStore.UpdateSidebarCategoryOrder(userId, teamId, categoryOrder)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (s *OpenTracingLayerChannelStore) UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateSidebarChannelCategoryOnMove")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0 := s.ChannelStore.UpdateSidebarChannelCategoryOnMove(channel, newTeamId)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (s *OpenTracingLayerChannelStore) UpdateSidebarChannelsByPreferences(preferences *model.Preferences) *model.AppError {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateSidebarChannelsByPreferences")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
resultVar0 := s.ChannelStore.UpdateSidebarChannelsByPreferences(preferences)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (s *OpenTracingLayerChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) { func (s *OpenTracingLayerChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UserBelongsToChannels") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UserBelongsToChannels")

Просмотреть файл

@@ -18,6 +18,7 @@ import (
"github.com/mattermost/mattermost-server/v5/model" "github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/services/cache2" "github.com/mattermost/mattermost-server/v5/services/cache2"
"github.com/mattermost/mattermost-server/v5/store" "github.com/mattermost/mattermost-server/v5/store"
"github.com/mattermost/mattermost-server/v5/utils"
sq "github.com/Masterminds/squirrel" sq "github.com/Masterminds/squirrel"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -390,6 +391,19 @@ func newSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface)
tablePublicChannels.SetUniqueTogether("Name", "TeamId") tablePublicChannels.SetUniqueTogether("Name", "TeamId")
tablePublicChannels.ColMap("Header").SetMaxSize(1024) tablePublicChannels.ColMap("Header").SetMaxSize(1024)
tablePublicChannels.ColMap("Purpose").SetMaxSize(250) tablePublicChannels.ColMap("Purpose").SetMaxSize(250)
tableSidebarCategories := db.AddTableWithName(model.SidebarCategory{}, "SidebarCategories").SetKeys(false, "Id")
tableSidebarCategories.ColMap("Id").SetMaxSize(26)
tableSidebarCategories.ColMap("UserId").SetMaxSize(26)
tableSidebarCategories.ColMap("TeamId").SetMaxSize(26)
tableSidebarCategories.ColMap("Sorting").SetMaxSize(64)
tableSidebarCategories.ColMap("Type").SetMaxSize(64)
tableSidebarCategories.ColMap("DisplayName").SetMaxSize(64)
tableSidebarChannels := db.AddTableWithName(model.SidebarChannel{}, "SidebarChannels").SetKeys(false, "ChannelId", "UserId", "CategoryId")
tableSidebarChannels.ColMap("ChannelId").SetMaxSize(26)
tableSidebarChannels.ColMap("UserId").SetMaxSize(26)
tableSidebarChannels.ColMap("CategoryId").SetMaxSize(26)
} }
return s return s
@@ -423,6 +437,217 @@ func (s SqlChannelStore) createIndexesIfNotExists() {
s.CreateIndexIfNotExists("idx_channels_scheme_id", "Channels", "SchemeId") s.CreateIndexIfNotExists("idx_channels_scheme_id", "Channels", "SchemeId")
} }
// MigrateSidebarCategories creates 3 initial categories for all existing user/team pairs
// **IMPORTANT** This function should only be called from the migration task and shouldn't be used by itself
func (s SqlChannelStore) MigrateSidebarCategories(fromTeamId, fromUserId string) (map[string]interface{}, error) {
var userTeamMap []struct {
UserId string
TeamId string
Locale *string
}
transaction, err := s.GetMaster().Begin()
if err != nil {
return nil, err
}
defer finalizeTransaction(transaction)
if _, err := transaction.Select(&userTeamMap, "SELECT TeamId, UserId, Users.Locale FROM TeamMembers LEFT JOIN Users ON Users.Id=UserId WHERE (TeamId, UserId) > (:FromTeamId, :FromUserId) ORDER BY TeamId, UserId LIMIT 100", map[string]interface{}{"FromTeamId": fromTeamId, "FromUserId": fromUserId}); err != nil {
return nil, err
}
if len(userTeamMap) == 0 {
// No more team members in query result means that the migration has finished.
return nil, nil
}
for _, u := range userTeamMap {
locale := "en"
if u.Locale != nil {
locale = *u.Locale
}
if err := s.createInitialSidebarCategoriesT(transaction, &model.User{Id: u.UserId, Locale: locale}, u.TeamId); err != nil {
return nil, err
}
}
if err := transaction.Commit(); err != nil {
return nil, err
}
data := make(map[string]interface{})
data["TeamId"] = userTeamMap[len(userTeamMap)-1].TeamId
data["UserId"] = userTeamMap[len(userTeamMap)-1].UserId
return data, nil
}
func (s SqlChannelStore) CreateInitialSidebarCategories(user *model.User, teamId string) error {
transaction, err := s.GetMaster().Begin()
if err != nil {
return err
}
defer finalizeTransaction(transaction)
if err := s.createInitialSidebarCategoriesT(transaction, user, teamId); err != nil {
return err
}
if err := transaction.Commit(); err != nil {
return err
}
return nil
}
func (s SqlChannelStore) createInitialSidebarCategoriesT(transaction *gorp.Transaction, user *model.User, teamId string) error {
T := utils.GetUserTranslations(user.Locale)
selectQuery, selectParams, _ := s.getQueryBuilder().
Select("Type").
From("SidebarCategories").
Where(sq.Eq{
"UserId": user.Id,
"TeamId": teamId,
"Type": []model.SidebarCategoryType{model.SidebarCategoryFavorites, model.SidebarCategoryChannels, model.SidebarCategoryDirectMessages},
}).ToSql()
var existingTypes []model.SidebarCategoryType
_, err := transaction.Select(&existingTypes, selectQuery, selectParams...)
if err != nil {
return err
}
hasCategoryOfType := func(categoryType model.SidebarCategoryType) bool {
for _, existingType := range existingTypes {
if categoryType == existingType {
return true
}
}
return false
}
if !hasCategoryOfType(model.SidebarCategoryFavorites) {
if err := transaction.Insert(&model.SidebarCategory{
DisplayName: T("sidebar.category.favorites"),
Id: model.NewId(),
UserId: user.Id,
TeamId: teamId,
Sorting: model.SidebarCategorySortDefault,
SortOrder: model.DefaultSidebarSortOrderFavorites,
Type: model.SidebarCategoryFavorites,
}); err != nil {
return err
}
}
if !hasCategoryOfType(model.SidebarCategoryChannels) {
if err := transaction.Insert(&model.SidebarCategory{
DisplayName: T("sidebar.category.channels"),
Id: model.NewId(),
UserId: user.Id,
TeamId: teamId,
Sorting: model.SidebarCategorySortDefault,
SortOrder: model.DefaultSidebarSortOrderChannels,
Type: model.SidebarCategoryChannels,
}); err != nil {
return err
}
}
if !hasCategoryOfType(model.SidebarCategoryDirectMessages) {
if err := transaction.Insert(&model.SidebarCategory{
DisplayName: T("sidebar.category.dm"),
Id: model.NewId(),
UserId: user.Id,
TeamId: teamId,
Sorting: model.SidebarCategorySortRecent,
SortOrder: model.DefaultSidebarSortOrderDMs,
Type: model.SidebarCategoryDirectMessages,
}); err != nil {
return err
}
}
return nil
}
type userMembership struct {
UserId string
ChannelId string
CategoryId string
}
func (s SqlChannelStore) migrateMembershipToSidebar(transaction *gorp.Transaction, runningOrder *int64, sql string, args ...interface{}) ([]userMembership, error) {
var memberships []userMembership
if _, err := transaction.Select(&memberships, sql, args...); err != nil {
return nil, err
}
for _, favorite := range memberships {
sql, args, _ := s.getQueryBuilder().
Insert("SidebarChannels").
Columns("ChannelId", "UserId", "CategoryId", "SortOrder").
Values(favorite.ChannelId, favorite.UserId, favorite.CategoryId, *runningOrder).ToSql()
if _, err := transaction.Exec(sql, args...); err != nil && !IsUniqueConstraintError(err, []string{"UserId", "PRIMARY"}) {
return nil, err
}
*runningOrder = *runningOrder + model.MinimalSidebarSortDistance
}
if err := transaction.Commit(); err != nil {
return nil, err
}
return memberships, nil
}
// MigrateFavoritesToSidebarChannels populates the SidebarChannels table by analyzing existing user preferences for favorites
// **IMPORTANT** This function should only be called from the migration task and shouldn't be used by itself
func (s SqlChannelStore) MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error) {
transaction, err := s.GetMaster().Begin()
if err != nil {
return nil, err
}
defer finalizeTransaction(transaction)
sb := s.
getQueryBuilder().
Select("Preferences.UserId", "Preferences.Name AS ChannelId", "SidebarCategories.Id AS CategoryId").
From("Preferences").
Where(sq.And{
sq.Eq{"Preferences.Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL},
sq.NotEq{"Preferences.Value": "false"},
sq.NotEq{"SidebarCategories.Id": nil},
sq.Gt{"Preferences.UserId": lastUserId},
}).
LeftJoin("Channels ON (Channels.Id=Preferences.Name)").
LeftJoin("SidebarCategories ON (SidebarCategories.UserId=Preferences.UserId AND SidebarCategories.Type='"+string(model.SidebarCategoryFavorites)+"' AND (SidebarCategories.TeamId=Channels.TeamId OR Channels.TeamId=''))").
OrderBy("Preferences.UserId", "Channels.Name DESC").
Limit(100)
sql, args, err := sb.ToSql()
if err != nil {
return nil, err
}
userFavorites, err := s.migrateMembershipToSidebar(transaction, &runningOrder, sql, args...)
if err != nil {
return nil, err
}
if len(userFavorites) == 0 {
return nil, nil
}
data := make(map[string]interface{})
data["UserId"] = userFavorites[len(userFavorites)-1].UserId
data["SortOrder"] = runningOrder
return data, nil
}
// MigratePublicChannels initializes the PublicChannels table with data created before this version // MigratePublicChannels initializes the PublicChannels table with data created before this version
// of the Mattermost server kept it up-to-date. // of the Mattermost server kept it up-to-date.
func (s SqlChannelStore) MigratePublicChannels() error { func (s SqlChannelStore) MigratePublicChannels() error {
@@ -1938,6 +2163,21 @@ func (s SqlChannelStore) RemoveMembers(channelId string, userIds []string) *mode
if err != nil { if err != nil {
return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
} }
// cleanup sidebarchannels table if the user is no longer a member of that channel
sql, args, err = s.getQueryBuilder().
Delete("SidebarChannels").
Where(sq.And{
sq.Eq{"ChannelId": channelId},
sq.Eq{"UserId": userIds},
}).ToSql()
if err != nil {
return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
}
_, err = s.GetMaster().Exec(sql, args...)
if err != nil {
return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError)
}
return nil return nil
} }
@@ -3206,3 +3446,569 @@ func (s SqlChannelStore) GroupSyncedChannelCount() (int64, *model.AppError) {
return count, nil return count, nil
} }
type sidebarCategoryForJoin struct {
model.SidebarCategory
ChannelId *string
}
func (s SqlChannelStore) CreateSidebarCategory(userId, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
transaction, err := s.GetMaster().Begin()
if err != nil {
return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer finalizeTransaction(transaction)
categoriesWithOrder, appErr := s.getSidebarCategoriesT(transaction, userId, teamId)
if appErr != nil {
return nil, appErr
}
if len(categoriesWithOrder.Categories) < 1 {
return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError)
}
newOrder := categoriesWithOrder.Order
newCategoryId := model.NewId()
newCategorySortOrder := 0
/*
When a new category is created, it should be placed as follows:
1. If the Favorites category is first, the new category should be placed after it
2. Otherwise, the new category should be placed first.
*/
if categoriesWithOrder.Categories[0].Type == model.SidebarCategoryFavorites {
newOrder = append([]string{newOrder[0], newCategoryId}, newOrder[1:]...)
newCategorySortOrder = model.MinimalSidebarSortDistance
} else {
newOrder = append([]string{newCategoryId}, newOrder...)
}
category := &model.SidebarCategory{
DisplayName: newCategory.DisplayName,
Id: newCategoryId,
UserId: userId,
TeamId: teamId,
Sorting: model.SidebarCategorySortDefault,
SortOrder: int64(model.MinimalSidebarSortDistance * len(newOrder)), // first we place it at the end of the list
Type: model.SidebarCategoryCustom,
}
if err = transaction.Insert(category); err != nil {
return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
var channels []interface{}
runningOrder := 0
for _, channelID := range newCategory.Channels {
channels = append(channels, &model.SidebarChannel{
ChannelId: channelID,
CategoryId: newCategoryId,
SortOrder: int64(runningOrder),
UserId: userId,
})
runningOrder += model.MinimalSidebarSortDistance
}
if err = transaction.Insert(channels...); err != nil {
return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// now we re-order the categories according to the new order
if appErr := s.updateSidebarCategoryOrderT(transaction, userId, teamId, newOrder); appErr != nil {
return nil, appErr
}
if err = transaction.Commit(); err != nil {
return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// patch category to return proper sort order
category.SortOrder = int64(newCategorySortOrder)
result := &model.SidebarCategoryWithChannels{
SidebarCategory: *category,
Channels: newCategory.Channels,
}
return result, nil
}
func (s SqlChannelStore) completePopulatingCategoryChannels(category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
if category.Type == model.SidebarCategoryCustom || category.Type == model.SidebarCategoryFavorites {
return category, nil
}
var channelTypeFilter sq.Sqlizer
if category.Type == model.SidebarCategoryDirectMessages {
// any DM/GM channels that aren't in any category should be returned as part of the Direct Messages category
channelTypeFilter = sq.Eq{"Channels.Type": []string{model.CHANNEL_DIRECT, model.CHANNEL_GROUP}}
} else if category.Type == model.SidebarCategoryChannels {
// any public/private channels that are on the current team and aren't in any category should be returned as part of the Channels category
channelTypeFilter = sq.And{
sq.Eq{"Channels.Type": []string{model.CHANNEL_OPEN, model.CHANNEL_PRIVATE}},
sq.Eq{"Channels.TeamId": category.TeamId},
}
}
// A subquery that is true if the channel does not have a SidebarChannel entry for the current user on the current team
doesNotHaveSidebarChannel := sq.Select("1").
Prefix("NOT EXISTS (").
From("SidebarChannels").
Join("SidebarCategories on SidebarChannels.CategoryId=SidebarCategories.Id").
Where(sq.And{
sq.Expr("SidebarChannels.ChannelId = ChannelMembers.ChannelId"),
sq.Eq{"SidebarCategories.UserId": category.UserId},
sq.Eq{"SidebarCategories.TeamId": category.TeamId},
}).
Suffix(")")
var channels []string
sql, args, _ := s.getQueryBuilder().
Select("Id").
From("ChannelMembers").
LeftJoin("Channels ON Channels.Id=ChannelMembers.ChannelId").
Where(sq.And{
sq.Eq{"ChannelMembers.UserId": category.UserId},
channelTypeFilter,
sq.Eq{"Channels.DeleteAt": 0},
doesNotHaveSidebarChannel,
}).
OrderBy("DisplayName ASC").ToSql()
if _, err := s.GetReplica().Select(&channels, sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.completePopulatingCategoryChannels", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
}
category.Channels = append(channels, category.Channels...)
return category, nil
}
func (s SqlChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
var categories []*sidebarCategoryForJoin
sql, args, _ := s.getQueryBuilder().
Select("SidebarCategories.*", "SidebarChannels.ChannelId").
From("SidebarCategories").
LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=SidebarCategories.Id").
Where(sq.Eq{"SidebarCategories.Id": categoryId}).
OrderBy("SidebarChannels.SortOrder ASC").ToSql()
if _, err := s.GetReplica().Select(&categories, sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.GetSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
}
result := &model.SidebarCategoryWithChannels{
SidebarCategory: categories[0].SidebarCategory,
Channels: make([]string, 0),
}
for _, category := range categories {
if category.ChannelId != nil {
result.Channels = append(result.Channels, *category.ChannelId)
}
}
return s.completePopulatingCategoryChannels(result)
}
func (s SqlChannelStore) getSidebarCategoriesT(transaction *gorp.Transaction, userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
oc := model.OrderedSidebarCategories{
Categories: make(model.SidebarCategoriesWithChannels, 0),
Order: make([]string, 0),
}
var categories []*sidebarCategoryForJoin
sql, args, _ := s.getQueryBuilder().
Select("SidebarCategories.*", "SidebarChannels.ChannelId").
From("SidebarCategories").
LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=Id").
Where(sq.And{
sq.Eq{"SidebarCategories.UserId": userId},
sq.Eq{"SidebarCategories.TeamId": teamId},
}).
OrderBy("SidebarCategories.SortOrder ASC, SidebarChannels.SortOrder ASC").ToSql()
if _, err := s.GetReplica().Select(&categories, sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
}
for _, category := range categories {
var prevCategory *model.SidebarCategoryWithChannels
for _, existing := range oc.Categories {
if existing.Id == category.Id {
prevCategory = existing
break
}
}
if prevCategory == nil {
prevCategory = &model.SidebarCategoryWithChannels{
SidebarCategory: category.SidebarCategory,
Channels: make([]string, 0),
}
oc.Categories = append(oc.Categories, prevCategory)
oc.Order = append(oc.Order, category.Id)
}
if category.ChannelId != nil {
prevCategory.Channels = append(prevCategory.Channels, *category.ChannelId)
}
}
for _, category := range oc.Categories {
if _, err := s.completePopulatingCategoryChannels(category); err != nil {
return nil, err
}
}
return &oc, nil
}
func (s SqlChannelStore) GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
transaction, err := s.GetMaster().Begin()
if err != nil {
return nil, model.NewAppError("SqlChannelStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer finalizeTransaction(transaction)
oc, appErr := s.getSidebarCategoriesT(transaction, userId, teamId)
if appErr != nil {
return nil, appErr
}
if err = transaction.Commit(); err != nil {
return nil, model.NewAppError("SqlChannelStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return oc, nil
}
func (s SqlChannelStore) GetSidebarCategoryOrder(userId, teamId string) ([]string, *model.AppError) {
var ids []string
sql, args, _ := s.getQueryBuilder().
Select("Id").
From("SidebarCategories").
Where(sq.And{
sq.Eq{"UserId": userId},
sq.Eq{"TeamId": teamId},
}).
OrderBy("SidebarCategories.SortOrder ASC").ToSql()
if _, err := s.GetReplica().Select(&ids, sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.GetSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound)
}
return ids, nil
}
func (s SqlChannelStore) updateSidebarCategoryOrderT(transaction *gorp.Transaction, userId, teamId string, categoryOrder []string) *model.AppError {
var newOrder []interface{}
runningOrder := 0
for _, categoryId := range categoryOrder {
newOrder = append(newOrder, &model.SidebarCategory{
Id: categoryId,
SortOrder: int64(runningOrder),
})
runningOrder += model.MinimalSidebarSortDistance
}
// There's a bug in gorp where UpdateColumns messes up the stored query for any other attempt to use .Update or
// .UpdateColumns on this table, so it's okay to use here as long as we don't use those methods for SidebarCategories
// anywhere else.
if _, err := transaction.UpdateColumns(func(col *gorp.ColumnMap) bool {
return col.ColumnName == "SortOrder"
}, newOrder...); err != nil {
return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return nil
}
func (s SqlChannelStore) UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []string) *model.AppError {
transaction, err := s.GetMaster().Begin()
if err != nil {
return model.NewAppError("SqlChannelStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer finalizeTransaction(transaction)
// Ensure no invalid categories are included and that no categories are left out
existingOrder, appErr := s.GetSidebarCategoryOrder(userId, teamId)
if appErr != nil {
return appErr
}
if len(existingOrder) != len(categoryOrder) {
return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, "Cannot update category order, passed list of categories different size than in DB", http.StatusInternalServerError)
}
for _, originalCategoryId := range existingOrder {
found := false
for _, newCategoryId := range categoryOrder {
if newCategoryId == originalCategoryId {
found = true
break
}
}
if !found {
return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, "Cannot update category order, passed list of categories contains unrecognized category IDs", http.StatusBadRequest)
}
}
if appErr := s.updateSidebarCategoryOrderT(transaction, userId, teamId, categoryOrder); appErr != nil {
return appErr
}
if err = transaction.Commit(); err != nil {
return model.NewAppError("SqlChannelStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return nil
}
func (s SqlChannelStore) UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
transaction, err := s.GetMaster().Begin()
if err != nil {
return nil, model.NewAppError("SqlChannelStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer finalizeTransaction(transaction)
updatedCategories := []*model.SidebarCategoryWithChannels{}
for _, category := range categories {
originalCategory, appErr := s.GetSidebarCategory(category.Id)
if appErr != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, appErr.Error(), http.StatusInternalServerError)
}
// Copy category to avoid modifying an argument
updatedCategory := &model.SidebarCategoryWithChannels{
SidebarCategory: category.SidebarCategory,
}
// Prevent any changes to read-only fields of SidebarCategories
updatedCategory.UserId = originalCategory.UserId
updatedCategory.TeamId = originalCategory.TeamId
updatedCategory.SortOrder = originalCategory.SortOrder
updatedCategory.Type = originalCategory.Type
if updatedCategory.Type != model.SidebarCategoryCustom {
updatedCategory.DisplayName = originalCategory.DisplayName
}
if category.Type != model.SidebarCategoryDirectMessages {
updatedCategory.Channels = make([]string, len(category.Channels))
copy(updatedCategory.Channels, category.Channels)
}
updateQuery, updateParams, _ := s.getQueryBuilder().
Update("SidebarCategories").
Set("DisplayName", updatedCategory.DisplayName).
Set("Sorting", updatedCategory.Sorting).
Where(sq.Eq{"Id": updatedCategory.Id}).ToSql()
if _, err = transaction.Exec(updateQuery, updateParams...); err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// if we are updating DM category, it's order can't channel order cannot be changed.
if category.Type != model.SidebarCategoryDirectMessages {
// Remove any SidebarChannels entries that were either:
// - previously in this category (and any ones that are still in the category will be recreated below)
// - in another category and are being added to this category
sql, args, _ := s.getQueryBuilder().
Delete("SidebarChannels").
Where(
sq.And{
sq.Or{
sq.Eq{"ChannelId": originalCategory.Channels},
sq.Eq{"ChannelId": updatedCategory.Channels},
},
sq.Eq{"CategoryId": category.Id},
},
).ToSql()
if _, err = transaction.Exec(sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
var channels []interface{}
runningOrder := 0
for _, channelID := range category.Channels {
channels = append(channels, &model.SidebarChannel{
ChannelId: channelID,
CategoryId: category.Id,
SortOrder: int64(runningOrder),
UserId: userId,
})
runningOrder += model.MinimalSidebarSortDistance
}
if err = transaction.Insert(channels...); err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
// Update the favorites preferences based on channels moving into or out of the Favorites category for compatibility
if category.Type == model.SidebarCategoryFavorites {
// Remove any old favorites
sql, args, _ := s.getQueryBuilder().Delete("Preferences").Where(
sq.Eq{
"Name": originalCategory.Channels,
"Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
},
).ToSql()
if _, err = transaction.Exec(sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// And then add the new ones
var preferences []interface{}
for _, channelID := range category.Channels {
preferences = append(preferences, &model.Preference{
Name: channelID,
UserId: userId,
Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
Value: "true",
})
}
if err = transaction.Insert(preferences...); err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
} else {
// Remove any old favorites that might have been in this category
sql, args, _ := s.getQueryBuilder().Delete("Preferences").Where(
sq.Eq{
"Name": category.Channels,
"Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
},
).ToSql()
if _, err = transaction.Exec(sql, args...); err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
updatedCategories = append(updatedCategories, updatedCategory)
}
if err = transaction.Commit(); err != nil {
return nil, model.NewAppError("SqlChannelStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// Ensure Channels are populated for Channels/Direct Messages category if they change
for i, updatedCategory := range updatedCategories {
populated, err := s.completePopulatingCategoryChannels(updatedCategory)
if err != nil {
return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
updatedCategories[i] = populated
}
return updatedCategories, nil
}
// UpdateSidebarChannelByPreference is called when the Preference table is being updated to keep SidebarCategories in sync
// At the moment, it's only handling Favorites and NOT DMs/GMs (those will be handled client side)
func (s SqlChannelStore) UpdateSidebarChannelsByPreferences(preferences *model.Preferences) *model.AppError {
transaction, err := s.GetMaster().Begin()
if err != nil {
return model.NewAppError("SqlChannelStore.UpdateSidebarChannelsByPreferences", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer finalizeTransaction(transaction)
for _, preference := range *preferences {
if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL {
continue
}
params := map[string]interface{}{
"UserId": preference.UserId,
"ChannelId": preference.Name,
"CategoryType": model.SidebarCategoryFavorites,
}
// if new preference is false - remove the channel from the appropriate sidebar category
if preference.Value == "false" {
var deleteQuery string
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
deleteQuery = "DELETE SidebarChannels FROM SidebarChannels LEFT JOIN SidebarCategories ON SidebarCategories.Id = SidebarChannels.CategoryId WHERE SidebarCategories.Type=:CategoryType AND SidebarCategories.UserId=:UserId AND SidebarChannels.UserId=:UserId AND ChannelId=:ChannelId"
} else {
deleteQuery = "DELETE FROM SidebarChannels USING SidebarChannels AS chan LEFT OUTER JOIN SidebarCategories AS cat ON cat.Id = chan.CategoryId WHERE cat.Type=:CategoryType AND cat.UserId = :UserId AND chan.UserId = :UserId AND cat.TeamId = :TeamId AND chan.ChannelId=:ChannelId"
}
if _, err := transaction.Exec(deleteQuery, params); err != nil {
return model.NewAppError("SqlChannelStore.UpdateSidebarChannelByPreference", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
} else {
// otherwise - insert new channel into the apropriate category. ignore duplicate error
if _, err := transaction.Exec("INSERT INTO SidebarChannels (ChannelId, UserId, CategoryId, SortOrder) SELECT Id AS CategoryId, :UserId AS UserId, :ChannelId AS ChannelId, MAX(SidebarChannels.SortOrder)+10 FROM SidebarCategories INNER JOIN SidebarChannels ON SidebarChannels.CategoryId = SidebarCategories.Id WHERE SidebarCategories.Type=:CategoryType AND SidebarCategories.UserId=:UserId GROUP BY SidebarChannels.CategoryId, SidebarCategories.Id", params); err != nil && !IsUniqueConstraintError(err, []string{"UserId"}) {
return model.NewAppError("SqlChannelStore.UpdateSidebarChannelByPreference", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
}
if err := transaction.Commit(); err != nil {
return model.NewAppError("SqlChannelStore.UpdateSidebarChannelByPreference", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return nil
}
func (s SqlChannelStore) UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError {
// if channel is being moved, remove it from the categories, since it's possible that there's no matching category in the new team
if _, err := s.GetMaster().Exec("DELETE FROM SidebarChannels WHERE ChannelId=:ChannelId", map[string]interface{}{"ChannelId": channel.Id}); err != nil {
return model.NewAppError("SqlChannelStore.UpdateSidebarChannelCategoryOnMove", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return nil
}
func (s SqlChannelStore) ClearSidebarOnTeamLeave(userId, teamId string) *model.AppError {
// if user leaves the team, clean his team related entries in sidebar channels and categories
params := map[string]interface{}{
"UserId": userId,
"TeamId": teamId,
}
var deleteQuery string
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
deleteQuery = "DELETE SidebarChannels FROM SidebarChannels LEFT JOIN SidebarCategories ON SidebarCategories.Id = SidebarChannels.CategoryId WHERE SidebarCategories.TeamId=:TeamId AND SidebarCategories.UserId=:UserId"
} else {
deleteQuery = "DELETE FROM SidebarChannels USING SidebarChannels AS chan LEFT OUTER JOIN SidebarCategories AS cat ON cat.Id = chan.CategoryId WHERE cat.UserId = :UserId AND cat.TeamId = :TeamId"
}
if _, err := s.GetMaster().Exec(deleteQuery, params); err != nil {
return model.NewAppError("SqlChannelStore.ClearSidebarOnTeamLeave", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if _, err := s.GetMaster().Exec("DELETE FROM SidebarCategories WHERE SidebarCategories.TeamId = :TeamId AND SidebarCategories.UserId = :UserId", params); err != nil {
return model.NewAppError("SqlChannelStore.ClearSidebarOnTeamLeave", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return nil
}
// DeleteSidebarCategory removes a custom category and moves any channels into it into the Channels and Direct Messages
// categories respectively. Assumes that the provided user ID and team ID match the given category ID.
func (s SqlChannelStore) DeleteSidebarCategory(categoryId string) *model.AppError {
transaction, err := s.GetMaster().Begin()
if err != nil {
return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
defer finalizeTransaction(transaction)
// Ensure that we're deleting a custom category
var category *model.SidebarCategory
if err = transaction.SelectOne(&category, "SELECT * FROM SidebarCategories WHERE Id = :Id", map[string]interface{}{"Id": categoryId}); err != nil {
return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if category.Type != model.SidebarCategoryCustom {
return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.delete_invalid.app_error", nil, "", http.StatusBadRequest)
}
// Delete the channels in the category
sql, args, _ := s.getQueryBuilder().
Delete("SidebarChannels").
Where(sq.Eq{"CategoryId": categoryId}).ToSql()
if _, err := transaction.Exec(sql, args...); err != nil {
return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
// Delete the category itself
sql, args, _ = s.getQueryBuilder().
Delete("SidebarCategories").
Where(sq.Eq{"Id": categoryId}).ToSql()
if _, err := transaction.Exec(sql, args...); err != nil {
return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if err := transaction.Commit(); err != nil {
return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError)
}
return nil
}

Просмотреть файл

@@ -65,6 +65,7 @@ type SqlStore interface {
CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool
CreateIndexIfNotExists(indexName string, tableName string, columnName string) bool CreateIndexIfNotExists(indexName string, tableName string, columnName string) bool
CreateCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool CreateCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool
CreateUniqueCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool
CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool
RemoveIndexIfExists(indexName string, tableName string) bool RemoveIndexIfExists(indexName string, tableName string) bool
GetAllConns() []*gorp.DbMap GetAllConns() []*gorp.DbMap

Просмотреть файл

@@ -855,6 +855,10 @@ func (ss *SqlSupplier) CreateCompositeIndexIfNotExists(indexName string, tableNa
return ss.createIndexIfNotExists(indexName, tableName, columnNames, INDEX_TYPE_DEFAULT, false) return ss.createIndexIfNotExists(indexName, tableName, columnNames, INDEX_TYPE_DEFAULT, false)
} }
func (ss *SqlSupplier) CreateUniqueCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool {
return ss.createIndexIfNotExists(indexName, tableName, columnNames, INDEX_TYPE_DEFAULT, true)
}
func (ss *SqlSupplier) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool { func (ss *SqlSupplier) CreateFullTextIndexIfNotExists(indexName string, tableName string, columnName string) bool {
return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_FULL_TEXT, false) return ss.createIndexIfNotExists(indexName, tableName, []string{columnName}, INDEX_TYPE_FULL_TEXT, false)
} }

Просмотреть файл

@@ -138,6 +138,8 @@ type ChannelStore interface {
CreateDirectChannel(userId *model.User, otherUserId *model.User) (*model.Channel, error) CreateDirectChannel(userId *model.User, otherUserId *model.User) (*model.Channel, error)
SaveDirectChannel(channel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) (*model.Channel, error) SaveDirectChannel(channel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) (*model.Channel, error)
Update(channel *model.Channel) (*model.Channel, error) Update(channel *model.Channel) (*model.Channel, error)
UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError
ClearSidebarOnTeamLeave(userId, teamId string) *model.AppError
Get(id string, allowFromCache bool) (*model.Channel, error) Get(id string, allowFromCache bool) (*model.Channel, error)
InvalidateChannel(id string) InvalidateChannel(id string)
InvalidateChannelByName(teamId, name string) InvalidateChannelByName(teamId, name string)
@@ -214,6 +216,17 @@ type ChannelStore interface {
ResetAllChannelSchemes() *model.AppError ResetAllChannelSchemes() *model.AppError
ClearAllCustomRoleAssignments() *model.AppError ClearAllCustomRoleAssignments() *model.AppError
MigratePublicChannels() error MigratePublicChannels() error
MigrateSidebarCategories(fromTeamId, fromUserId string) (map[string]interface{}, error)
CreateInitialSidebarCategories(user *model.User, teamId string) error
MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error)
GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError)
GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError)
GetSidebarCategoryOrder(userId, teamId string) ([]string, *model.AppError)
CreateSidebarCategory(userId, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError)
UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []string) *model.AppError
UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError)
UpdateSidebarChannelsByPreferences(preferences *model.Preferences) *model.AppError
DeleteSidebarCategory(categoryId string) *model.AppError
GetAllChannelsForExportAfter(limit int, afterId string) ([]*model.ChannelForExport, *model.AppError) GetAllChannelsForExportAfter(limit int, afterId string) ([]*model.ChannelForExport, *model.AppError)
GetAllDirectChannelsForExportAfter(limit int, afterId string) ([]*model.DirectChannelForExport, *model.AppError) GetAllDirectChannelsForExportAfter(limit int, afterId string) ([]*model.DirectChannelForExport, *model.AppError)
GetChannelMembersForExport(userId string, teamId string) ([]*model.ChannelMemberForExport, *model.AppError) GetChannelMembersForExport(userId string, teamId string) ([]*model.ChannelMemberForExport, *model.AppError)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@@ -132,6 +132,22 @@ func (_m *ChannelStore) ClearCaches() {
_m.Called() _m.Called()
} }
// ClearSidebarOnTeamLeave provides a mock function with given fields: userId, teamId
func (_m *ChannelStore) ClearSidebarOnTeamLeave(userId string, teamId string) *model.AppError {
ret := _m.Called(userId, teamId)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string, string) *model.AppError); ok {
r0 = rf(userId, teamId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// CountPostsAfter provides a mock function with given fields: channelId, timestamp, userId // CountPostsAfter provides a mock function with given fields: channelId, timestamp, userId
func (_m *ChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) { func (_m *ChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) {
ret := _m.Called(channelId, timestamp, userId) ret := _m.Called(channelId, timestamp, userId)
@@ -178,6 +194,45 @@ func (_m *ChannelStore) CreateDirectChannel(userId *model.User, otherUserId *mod
return r0, r1 return r0, r1
} }
// CreateInitialSidebarCategories provides a mock function with given fields: user, teamId
func (_m *ChannelStore) CreateInitialSidebarCategories(user *model.User, teamId string) error {
ret := _m.Called(user, teamId)
var r0 error
if rf, ok := ret.Get(0).(func(*model.User, string) error); ok {
r0 = rf(user, teamId)
} else {
r0 = ret.Error(0)
}
return r0
}
// CreateSidebarCategory provides a mock function with given fields: userId, teamId, newCategory
func (_m *ChannelStore) CreateSidebarCategory(userId string, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
ret := _m.Called(userId, teamId, newCategory)
var r0 *model.SidebarCategoryWithChannels
if rf, ok := ret.Get(0).(func(string, string, *model.SidebarCategoryWithChannels) *model.SidebarCategoryWithChannels); ok {
r0 = rf(userId, teamId, newCategory)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SidebarCategoryWithChannels)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string, *model.SidebarCategoryWithChannels) *model.AppError); ok {
r1 = rf(userId, teamId, newCategory)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// Delete provides a mock function with given fields: channelId, time // Delete provides a mock function with given fields: channelId, time
func (_m *ChannelStore) Delete(channelId string, time int64) error { func (_m *ChannelStore) Delete(channelId string, time int64) error {
ret := _m.Called(channelId, time) ret := _m.Called(channelId, time)
@@ -192,6 +247,22 @@ func (_m *ChannelStore) Delete(channelId string, time int64) error {
return r0 return r0
} }
// DeleteSidebarCategory provides a mock function with given fields: categoryId
func (_m *ChannelStore) DeleteSidebarCategory(categoryId string) *model.AppError {
ret := _m.Called(categoryId)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string) *model.AppError); ok {
r0 = rf(categoryId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// Get provides a mock function with given fields: id, allowFromCache // Get provides a mock function with given fields: id, allowFromCache
func (_m *ChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) { func (_m *ChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) {
ret := _m.Called(id, allowFromCache) ret := _m.Called(id, allowFromCache)
@@ -1126,6 +1197,81 @@ func (_m *ChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limi
return r0, r1 return r0, r1
} }
// GetSidebarCategories provides a mock function with given fields: userId, teamId
func (_m *ChannelStore) GetSidebarCategories(userId string, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
ret := _m.Called(userId, teamId)
var r0 *model.OrderedSidebarCategories
if rf, ok := ret.Get(0).(func(string, string) *model.OrderedSidebarCategories); ok {
r0 = rf(userId, teamId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.OrderedSidebarCategories)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok {
r1 = rf(userId, teamId)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetSidebarCategory provides a mock function with given fields: categoryId
func (_m *ChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
ret := _m.Called(categoryId)
var r0 *model.SidebarCategoryWithChannels
if rf, ok := ret.Get(0).(func(string) *model.SidebarCategoryWithChannels); ok {
r0 = rf(categoryId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.SidebarCategoryWithChannels)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string) *model.AppError); ok {
r1 = rf(categoryId)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetSidebarCategoryOrder provides a mock function with given fields: userId, teamId
func (_m *ChannelStore) GetSidebarCategoryOrder(userId string, teamId string) ([]string, *model.AppError) {
ret := _m.Called(userId, teamId)
var r0 []string
if rf, ok := ret.Get(0).(func(string, string) []string); ok {
r0 = rf(userId, teamId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]string)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string) *model.AppError); ok {
r1 = rf(userId, teamId)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// GetTeamChannels provides a mock function with given fields: teamId // GetTeamChannels provides a mock function with given fields: teamId
func (_m *ChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) { func (_m *ChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) {
ret := _m.Called(teamId) ret := _m.Called(teamId)
@@ -1264,6 +1410,29 @@ func (_m *ChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId s
return r0, r1 return r0, r1
} }
// MigrateFavoritesToSidebarChannels provides a mock function with given fields: lastUserId, runningOrder
func (_m *ChannelStore) MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error) {
ret := _m.Called(lastUserId, runningOrder)
var r0 map[string]interface{}
if rf, ok := ret.Get(0).(func(string, int64) map[string]interface{}); ok {
r0 = rf(lastUserId, runningOrder)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]interface{})
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, int64) error); ok {
r1 = rf(lastUserId, runningOrder)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MigratePublicChannels provides a mock function with given fields: // MigratePublicChannels provides a mock function with given fields:
func (_m *ChannelStore) MigratePublicChannels() error { func (_m *ChannelStore) MigratePublicChannels() error {
ret := _m.Called() ret := _m.Called()
@@ -1278,6 +1447,29 @@ func (_m *ChannelStore) MigratePublicChannels() error {
return r0 return r0
} }
// MigrateSidebarCategories provides a mock function with given fields: fromTeamId, fromUserId
func (_m *ChannelStore) MigrateSidebarCategories(fromTeamId string, fromUserId string) (map[string]interface{}, error) {
ret := _m.Called(fromTeamId, fromUserId)
var r0 map[string]interface{}
if rf, ok := ret.Get(0).(func(string, string) map[string]interface{}); ok {
r0 = rf(fromTeamId, fromUserId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]interface{})
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string) error); ok {
r1 = rf(fromTeamId, fromUserId)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PermanentDelete provides a mock function with given fields: channelId // PermanentDelete provides a mock function with given fields: channelId
func (_m *ChannelStore) PermanentDelete(channelId string) error { func (_m *ChannelStore) PermanentDelete(channelId string) error {
ret := _m.Called(channelId) ret := _m.Called(channelId)
@@ -1822,6 +2014,79 @@ func (_m *ChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([
return r0, r1 return r0, r1
} }
// UpdateSidebarCategories provides a mock function with given fields: userId, teamId, categories
func (_m *ChannelStore) UpdateSidebarCategories(userId string, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
ret := _m.Called(userId, teamId, categories)
var r0 []*model.SidebarCategoryWithChannels
if rf, ok := ret.Get(0).(func(string, string, []*model.SidebarCategoryWithChannels) []*model.SidebarCategoryWithChannels); ok {
r0 = rf(userId, teamId, categories)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.SidebarCategoryWithChannels)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string, []*model.SidebarCategoryWithChannels) *model.AppError); ok {
r1 = rf(userId, teamId, categories)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// UpdateSidebarCategoryOrder provides a mock function with given fields: userId, teamId, categoryOrder
func (_m *ChannelStore) UpdateSidebarCategoryOrder(userId string, teamId string, categoryOrder []string) *model.AppError {
ret := _m.Called(userId, teamId, categoryOrder)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string, string, []string) *model.AppError); ok {
r0 = rf(userId, teamId, categoryOrder)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// UpdateSidebarChannelCategoryOnMove provides a mock function with given fields: channel, newTeamId
func (_m *ChannelStore) UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError {
ret := _m.Called(channel, newTeamId)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.Channel, string) *model.AppError); ok {
r0 = rf(channel, newTeamId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// UpdateSidebarChannelsByPreferences provides a mock function with given fields: preferences
func (_m *ChannelStore) UpdateSidebarChannelsByPreferences(preferences *model.Preferences) *model.AppError {
ret := _m.Called(preferences)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(*model.Preferences) *model.AppError); ok {
r0 = rf(preferences)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// UserBelongsToChannels provides a mock function with given fields: userId, channelIds // UserBelongsToChannels provides a mock function with given fields: userId, channelIds
func (_m *ChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) { func (_m *ChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) {
ret := _m.Called(userId, channelIds) ret := _m.Called(userId, channelIds)

Просмотреть файл

@@ -247,6 +247,20 @@ func (_m *SqlStore) CreateIndexIfNotExists(indexName string, tableName string, c
return r0 return r0
} }
// CreateUniqueCompositeIndexIfNotExists provides a mock function with given fields: indexName, tableName, columnNames
func (_m *SqlStore) CreateUniqueCompositeIndexIfNotExists(indexName string, tableName string, columnNames []string) bool {
ret := _m.Called(indexName, tableName, columnNames)
var r0 bool
if rf, ok := ret.Get(0).(func(string, string, []string) bool); ok {
r0 = rf(indexName, tableName, columnNames)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// CreateUniqueIndexIfNotExists provides a mock function with given fields: indexName, tableName, columnName // CreateUniqueIndexIfNotExists provides a mock function with given fields: indexName, tableName, columnName
func (_m *SqlStore) CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool { func (_m *SqlStore) CreateUniqueIndexIfNotExists(indexName string, tableName string, columnName string) bool {
ret := _m.Called(indexName, tableName, columnName) ret := _m.Called(indexName, tableName, columnName)

Просмотреть файл

@@ -552,6 +552,22 @@ func (s *TimerLayerChannelStore) ClearCaches() {
} }
} }
func (s *TimerLayerChannelStore) ClearSidebarOnTeamLeave(userId string, teamId string) *model.AppError {
start := timemodule.Now()
resultVar0 := s.ChannelStore.ClearSidebarOnTeamLeave(userId, teamId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar0 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.ClearSidebarOnTeamLeave", success, elapsed)
}
return resultVar0
}
func (s *TimerLayerChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) { func (s *TimerLayerChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) {
start := timemodule.Now() start := timemodule.Now()
@@ -584,6 +600,38 @@ func (s *TimerLayerChannelStore) CreateDirectChannel(userId *model.User, otherUs
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *TimerLayerChannelStore) CreateInitialSidebarCategories(user *model.User, teamId string) error {
start := timemodule.Now()
resultVar0 := s.ChannelStore.CreateInitialSidebarCategories(user, teamId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar0 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.CreateInitialSidebarCategories", success, elapsed)
}
return resultVar0
}
func (s *TimerLayerChannelStore) CreateSidebarCategory(userId string, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.CreateSidebarCategory(userId, teamId, newCategory)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.CreateSidebarCategory", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) Delete(channelId string, time int64) error { func (s *TimerLayerChannelStore) Delete(channelId string, time int64) error {
start := timemodule.Now() start := timemodule.Now()
@@ -600,6 +648,22 @@ func (s *TimerLayerChannelStore) Delete(channelId string, time int64) error {
return resultVar0 return resultVar0
} }
func (s *TimerLayerChannelStore) DeleteSidebarCategory(categoryId string) *model.AppError {
start := timemodule.Now()
resultVar0 := s.ChannelStore.DeleteSidebarCategory(categoryId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar0 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.DeleteSidebarCategory", success, elapsed)
}
return resultVar0
}
func (s *TimerLayerChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) { func (s *TimerLayerChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) {
start := timemodule.Now() start := timemodule.Now()
@@ -1224,6 +1288,54 @@ func (s *TimerLayerChannelStore) GetPublicChannelsForTeam(teamId string, offset
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *TimerLayerChannelStore) GetSidebarCategories(userId string, teamId string) (*model.OrderedSidebarCategories, *model.AppError) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.GetSidebarCategories(userId, teamId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetSidebarCategories", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.GetSidebarCategory(categoryId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetSidebarCategory", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) GetSidebarCategoryOrder(userId string, teamId string) ([]string, *model.AppError) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.GetSidebarCategoryOrder(userId, teamId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetSidebarCategoryOrder", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) { func (s *TimerLayerChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) {
start := timemodule.Now() start := timemodule.Now()
@@ -1409,6 +1521,22 @@ func (s *TimerLayerChannelStore) MigrateChannelMembers(fromChannelId string, fro
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *TimerLayerChannelStore) MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.MigrateFavoritesToSidebarChannels(lastUserId, runningOrder)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.MigrateFavoritesToSidebarChannels", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) MigratePublicChannels() error { func (s *TimerLayerChannelStore) MigratePublicChannels() error {
start := timemodule.Now() start := timemodule.Now()
@@ -1425,6 +1553,22 @@ func (s *TimerLayerChannelStore) MigratePublicChannels() error {
return resultVar0 return resultVar0
} }
func (s *TimerLayerChannelStore) MigrateSidebarCategories(fromTeamId string, fromUserId string) (map[string]interface{}, error) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.MigrateSidebarCategories(fromTeamId, fromUserId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.MigrateSidebarCategories", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) PermanentDelete(channelId string) error { func (s *TimerLayerChannelStore) PermanentDelete(channelId string) error {
start := timemodule.Now() start := timemodule.Now()
@@ -1841,6 +1985,70 @@ func (s *TimerLayerChannelStore) UpdateMultipleMembers(members []*model.ChannelM
return resultVar0, resultVar1 return resultVar0, resultVar1
} }
func (s *TimerLayerChannelStore) UpdateSidebarCategories(userId string, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
start := timemodule.Now()
resultVar0, resultVar1 := s.ChannelStore.UpdateSidebarCategories(userId, teamId, categories)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar1 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.UpdateSidebarCategories", success, elapsed)
}
return resultVar0, resultVar1
}
func (s *TimerLayerChannelStore) UpdateSidebarCategoryOrder(userId string, teamId string, categoryOrder []string) *model.AppError {
start := timemodule.Now()
resultVar0 := s.ChannelStore.UpdateSidebarCategoryOrder(userId, teamId, categoryOrder)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar0 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.UpdateSidebarCategoryOrder", success, elapsed)
}
return resultVar0
}
func (s *TimerLayerChannelStore) UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError {
start := timemodule.Now()
resultVar0 := s.ChannelStore.UpdateSidebarChannelCategoryOnMove(channel, newTeamId)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar0 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.UpdateSidebarChannelCategoryOnMove", success, elapsed)
}
return resultVar0
}
func (s *TimerLayerChannelStore) UpdateSidebarChannelsByPreferences(preferences *model.Preferences) *model.AppError {
start := timemodule.Now()
resultVar0 := s.ChannelStore.UpdateSidebarChannelsByPreferences(preferences)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if resultVar0 == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.UpdateSidebarChannelsByPreferences", success, elapsed)
}
return resultVar0
}
func (s *TimerLayerChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) { func (s *TimerLayerChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) {
start := timemodule.Now() start := timemodule.Now()

Просмотреть файл

@@ -293,6 +293,17 @@ func (c *Context) RequireTeamId() *Context {
return c return c
} }
func (c *Context) RequireCategoryId() *Context {
if c.Err != nil {
return c
}
if len(c.Params.CategoryId) != 26 {
c.SetInvalidUrlParam("category_id")
}
return c
}
func (c *Context) RequireInviteId() *Context { func (c *Context) RequireInviteId() *Context {
if c.Err != nil { if c.Err != nil {
return c return c

Просмотреть файл

@@ -77,6 +77,7 @@ type Params struct {
IncludeDeleted bool IncludeDeleted bool
FilterAllowReference bool FilterAllowReference bool
FilterParentTeamPermitted bool FilterParentTeamPermitted bool
CategoryId string
} }
func ParamsFromRequest(r *http.Request) *Params { func ParamsFromRequest(r *http.Request) *Params {
@@ -93,6 +94,10 @@ func ParamsFromRequest(r *http.Request) *Params {
params.TeamId = val params.TeamId = val
} }
if val, ok := props["category_id"]; ok {
params.CategoryId = val
}
if val, ok := props["invite_id"]; ok { if val, ok := props["invite_id"]; ok {
params.InviteId = val params.InviteId = val
} }