github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/store/sqlstore/channel_store.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "database/sql" 8 "fmt" 9 "net/http" 10 "sort" 11 "strconv" 12 "strings" 13 "time" 14 15 "github.com/mattermost/gorp" 16 "github.com/mattermost/mattermost-server/v5/einterfaces" 17 "github.com/mattermost/mattermost-server/v5/mlog" 18 "github.com/mattermost/mattermost-server/v5/model" 19 "github.com/mattermost/mattermost-server/v5/services/cache" 20 "github.com/mattermost/mattermost-server/v5/store" 21 22 sq "github.com/Masterminds/squirrel" 23 "github.com/pkg/errors" 24 ) 25 26 const ( 27 ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE = model.SESSION_CACHE_SIZE 28 ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_DURATION = 15 * time.Minute // 15 mins 29 30 ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE = model.SESSION_CACHE_SIZE 31 ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_DURATION = 30 * time.Minute // 30 mins 32 33 CHANNEL_CACHE_DURATION = 15 * time.Minute // 15 mins 34 ) 35 36 type SqlChannelStore struct { 37 SqlStore 38 metrics einterfaces.MetricsInterface 39 } 40 41 type channelMember struct { 42 ChannelId string 43 UserId string 44 Roles string 45 LastViewedAt int64 46 MsgCount int64 47 MentionCount int64 48 NotifyProps model.StringMap 49 LastUpdateAt int64 50 SchemeUser sql.NullBool 51 SchemeAdmin sql.NullBool 52 SchemeGuest sql.NullBool 53 } 54 55 func NewChannelMemberFromModel(cm *model.ChannelMember) *channelMember { 56 return &channelMember{ 57 ChannelId: cm.ChannelId, 58 UserId: cm.UserId, 59 Roles: cm.ExplicitRoles, 60 LastViewedAt: cm.LastViewedAt, 61 MsgCount: cm.MsgCount, 62 MentionCount: cm.MentionCount, 63 NotifyProps: cm.NotifyProps, 64 LastUpdateAt: cm.LastUpdateAt, 65 SchemeGuest: sql.NullBool{Valid: true, Bool: cm.SchemeGuest}, 66 SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser}, 67 SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin}, 68 } 69 } 70 71 type channelMemberWithSchemeRoles struct { 72 ChannelId string 73 UserId string 74 Roles string 75 LastViewedAt int64 76 MsgCount int64 77 MentionCount int64 78 NotifyProps model.StringMap 79 LastUpdateAt int64 80 SchemeGuest sql.NullBool 81 SchemeUser sql.NullBool 82 SchemeAdmin sql.NullBool 83 TeamSchemeDefaultGuestRole sql.NullString 84 TeamSchemeDefaultUserRole sql.NullString 85 TeamSchemeDefaultAdminRole sql.NullString 86 ChannelSchemeDefaultGuestRole sql.NullString 87 ChannelSchemeDefaultUserRole sql.NullString 88 ChannelSchemeDefaultAdminRole sql.NullString 89 } 90 91 func channelMemberSliceColumns() []string { 92 return []string{"ChannelId", "UserId", "Roles", "LastViewedAt", "MsgCount", "MentionCount", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"} 93 } 94 95 func channelMemberToSlice(member *model.ChannelMember) []interface{} { 96 resultSlice := []interface{}{} 97 resultSlice = append(resultSlice, member.ChannelId) 98 resultSlice = append(resultSlice, member.UserId) 99 resultSlice = append(resultSlice, member.ExplicitRoles) 100 resultSlice = append(resultSlice, member.LastViewedAt) 101 resultSlice = append(resultSlice, member.MsgCount) 102 resultSlice = append(resultSlice, member.MentionCount) 103 resultSlice = append(resultSlice, model.MapToJson(member.NotifyProps)) 104 resultSlice = append(resultSlice, member.LastUpdateAt) 105 resultSlice = append(resultSlice, member.SchemeUser) 106 resultSlice = append(resultSlice, member.SchemeAdmin) 107 resultSlice = append(resultSlice, member.SchemeGuest) 108 return resultSlice 109 } 110 111 type channelMemberWithSchemeRolesList []channelMemberWithSchemeRoles 112 113 func getChannelRoles(schemeGuest, schemeUser, schemeAdmin bool, defaultTeamGuestRole, defaultTeamUserRole, defaultTeamAdminRole, defaultChannelGuestRole, defaultChannelUserRole, defaultChannelAdminRole string, roles []string) rolesInfo { 114 result := rolesInfo{ 115 roles: []string{}, 116 explicitRoles: []string{}, 117 schemeGuest: schemeGuest, 118 schemeUser: schemeUser, 119 schemeAdmin: schemeAdmin, 120 } 121 122 // Identify any scheme derived roles that are in "Roles" field due to not yet being migrated, and exclude 123 // them from ExplicitRoles field. 124 for _, role := range roles { 125 switch role { 126 case model.CHANNEL_GUEST_ROLE_ID: 127 result.schemeGuest = true 128 case model.CHANNEL_USER_ROLE_ID: 129 result.schemeUser = true 130 case model.CHANNEL_ADMIN_ROLE_ID: 131 result.schemeAdmin = true 132 default: 133 result.explicitRoles = append(result.explicitRoles, role) 134 result.roles = append(result.roles, role) 135 } 136 } 137 138 // Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add 139 // them to the Roles field for backwards compatibility reasons. 140 var schemeImpliedRoles []string 141 if result.schemeGuest { 142 if defaultChannelGuestRole != "" { 143 schemeImpliedRoles = append(schemeImpliedRoles, defaultChannelGuestRole) 144 } else if defaultTeamGuestRole != "" { 145 schemeImpliedRoles = append(schemeImpliedRoles, defaultTeamGuestRole) 146 } else { 147 schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_GUEST_ROLE_ID) 148 } 149 } 150 if result.schemeUser { 151 if defaultChannelUserRole != "" { 152 schemeImpliedRoles = append(schemeImpliedRoles, defaultChannelUserRole) 153 } else if defaultTeamUserRole != "" { 154 schemeImpliedRoles = append(schemeImpliedRoles, defaultTeamUserRole) 155 } else { 156 schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID) 157 } 158 } 159 if result.schemeAdmin { 160 if defaultChannelAdminRole != "" { 161 schemeImpliedRoles = append(schemeImpliedRoles, defaultChannelAdminRole) 162 } else if defaultTeamAdminRole != "" { 163 schemeImpliedRoles = append(schemeImpliedRoles, defaultTeamAdminRole) 164 } else { 165 schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID) 166 } 167 } 168 for _, impliedRole := range schemeImpliedRoles { 169 alreadyThere := false 170 for _, role := range result.roles { 171 if role == impliedRole { 172 alreadyThere = true 173 break 174 } 175 } 176 if !alreadyThere { 177 result.roles = append(result.roles, impliedRole) 178 } 179 } 180 return result 181 } 182 183 func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember { 184 // Identify any system-wide scheme derived roles that are in "Roles" field due to not yet being migrated, 185 // and exclude them from ExplicitRoles field. 186 schemeGuest := db.SchemeGuest.Valid && db.SchemeGuest.Bool 187 schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool 188 schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool 189 190 defaultTeamGuestRole := "" 191 if db.TeamSchemeDefaultGuestRole.Valid { 192 defaultTeamGuestRole = db.TeamSchemeDefaultGuestRole.String 193 } 194 195 defaultTeamUserRole := "" 196 if db.TeamSchemeDefaultUserRole.Valid { 197 defaultTeamUserRole = db.TeamSchemeDefaultUserRole.String 198 } 199 200 defaultTeamAdminRole := "" 201 if db.TeamSchemeDefaultAdminRole.Valid { 202 defaultTeamAdminRole = db.TeamSchemeDefaultAdminRole.String 203 } 204 205 defaultChannelGuestRole := "" 206 if db.ChannelSchemeDefaultGuestRole.Valid { 207 defaultChannelGuestRole = db.ChannelSchemeDefaultGuestRole.String 208 } 209 210 defaultChannelUserRole := "" 211 if db.ChannelSchemeDefaultUserRole.Valid { 212 defaultChannelUserRole = db.ChannelSchemeDefaultUserRole.String 213 } 214 215 defaultChannelAdminRole := "" 216 if db.ChannelSchemeDefaultAdminRole.Valid { 217 defaultChannelAdminRole = db.ChannelSchemeDefaultAdminRole.String 218 } 219 220 rolesResult := getChannelRoles( 221 schemeGuest, schemeUser, schemeAdmin, 222 defaultTeamGuestRole, defaultTeamUserRole, defaultTeamAdminRole, 223 defaultChannelGuestRole, defaultChannelUserRole, defaultChannelAdminRole, 224 strings.Fields(db.Roles), 225 ) 226 return &model.ChannelMember{ 227 ChannelId: db.ChannelId, 228 UserId: db.UserId, 229 Roles: strings.Join(rolesResult.roles, " "), 230 LastViewedAt: db.LastViewedAt, 231 MsgCount: db.MsgCount, 232 MentionCount: db.MentionCount, 233 NotifyProps: db.NotifyProps, 234 LastUpdateAt: db.LastUpdateAt, 235 SchemeAdmin: rolesResult.schemeAdmin, 236 SchemeUser: rolesResult.schemeUser, 237 SchemeGuest: rolesResult.schemeGuest, 238 ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "), 239 } 240 } 241 242 func (db channelMemberWithSchemeRolesList) ToModel() *model.ChannelMembers { 243 cms := model.ChannelMembers{} 244 245 for _, cm := range db { 246 cms = append(cms, *cm.ToModel()) 247 } 248 249 return &cms 250 } 251 252 type allChannelMember struct { 253 ChannelId string 254 Roles string 255 SchemeGuest sql.NullBool 256 SchemeUser sql.NullBool 257 SchemeAdmin sql.NullBool 258 TeamSchemeDefaultGuestRole sql.NullString 259 TeamSchemeDefaultUserRole sql.NullString 260 TeamSchemeDefaultAdminRole sql.NullString 261 ChannelSchemeDefaultGuestRole sql.NullString 262 ChannelSchemeDefaultUserRole sql.NullString 263 ChannelSchemeDefaultAdminRole sql.NullString 264 } 265 266 type allChannelMembers []allChannelMember 267 268 func (db allChannelMember) Process() (string, string) { 269 roles := strings.Fields(db.Roles) 270 271 // Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add 272 // them to the Roles field for backwards compatibility reasons. 273 var schemeImpliedRoles []string 274 if db.SchemeGuest.Valid && db.SchemeGuest.Bool { 275 if db.ChannelSchemeDefaultGuestRole.Valid && db.ChannelSchemeDefaultGuestRole.String != "" { 276 schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultGuestRole.String) 277 } else if db.TeamSchemeDefaultGuestRole.Valid && db.TeamSchemeDefaultGuestRole.String != "" { 278 schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultGuestRole.String) 279 } else { 280 schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_GUEST_ROLE_ID) 281 } 282 } 283 if db.SchemeUser.Valid && db.SchemeUser.Bool { 284 if db.ChannelSchemeDefaultUserRole.Valid && db.ChannelSchemeDefaultUserRole.String != "" { 285 schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultUserRole.String) 286 } else if db.TeamSchemeDefaultUserRole.Valid && db.TeamSchemeDefaultUserRole.String != "" { 287 schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultUserRole.String) 288 } else { 289 schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_USER_ROLE_ID) 290 } 291 } 292 if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool { 293 if db.ChannelSchemeDefaultAdminRole.Valid && db.ChannelSchemeDefaultAdminRole.String != "" { 294 schemeImpliedRoles = append(schemeImpliedRoles, db.ChannelSchemeDefaultAdminRole.String) 295 } else if db.TeamSchemeDefaultAdminRole.Valid && db.TeamSchemeDefaultAdminRole.String != "" { 296 schemeImpliedRoles = append(schemeImpliedRoles, db.TeamSchemeDefaultAdminRole.String) 297 } else { 298 schemeImpliedRoles = append(schemeImpliedRoles, model.CHANNEL_ADMIN_ROLE_ID) 299 } 300 } 301 for _, impliedRole := range schemeImpliedRoles { 302 alreadyThere := false 303 for _, role := range roles { 304 if role == impliedRole { 305 alreadyThere = true 306 } 307 } 308 if !alreadyThere { 309 roles = append(roles, impliedRole) 310 } 311 } 312 313 return db.ChannelId, strings.Join(roles, " ") 314 } 315 316 func (db allChannelMembers) ToMapStringString() map[string]string { 317 result := make(map[string]string) 318 319 for _, item := range db { 320 key, value := item.Process() 321 result[key] = value 322 } 323 324 return result 325 } 326 327 // publicChannel is a subset of the metadata corresponding to public channels only. 328 type publicChannel struct { 329 Id string `json:"id"` 330 DeleteAt int64 `json:"delete_at"` 331 TeamId string `json:"team_id"` 332 DisplayName string `json:"display_name"` 333 Name string `json:"name"` 334 Header string `json:"header"` 335 Purpose string `json:"purpose"` 336 } 337 338 var allChannelMembersForUserCache = cache.NewLRU(&cache.LRUOptions{ 339 Size: ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE, 340 }) 341 var allChannelMembersNotifyPropsForChannelCache = cache.NewLRU(&cache.LRUOptions{ 342 Size: ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_SIZE, 343 }) 344 var channelByNameCache = cache.NewLRU(&cache.LRUOptions{ 345 Size: model.CHANNEL_CACHE_SIZE, 346 }) 347 348 func (s SqlChannelStore) ClearCaches() { 349 allChannelMembersForUserCache.Purge() 350 allChannelMembersNotifyPropsForChannelCache.Purge() 351 channelByNameCache.Purge() 352 353 if s.metrics != nil { 354 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members for User - Purge") 355 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members Notify Props for Channel - Purge") 356 s.metrics.IncrementMemCacheInvalidationCounter("Channel By Name - Purge") 357 } 358 } 359 360 func newSqlChannelStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.ChannelStore { 361 s := &SqlChannelStore{ 362 SqlStore: sqlStore, 363 metrics: metrics, 364 } 365 366 for _, db := range sqlStore.GetAllConns() { 367 table := db.AddTableWithName(model.Channel{}, "Channels").SetKeys(false, "Id") 368 table.ColMap("Id").SetMaxSize(26) 369 table.ColMap("TeamId").SetMaxSize(26) 370 table.ColMap("Type").SetMaxSize(1) 371 table.ColMap("DisplayName").SetMaxSize(64) 372 table.ColMap("Name").SetMaxSize(64) 373 table.SetUniqueTogether("Name", "TeamId") 374 table.ColMap("Header").SetMaxSize(1024) 375 table.ColMap("Purpose").SetMaxSize(250) 376 table.ColMap("CreatorId").SetMaxSize(26) 377 table.ColMap("SchemeId").SetMaxSize(26) 378 379 tablem := db.AddTableWithName(channelMember{}, "ChannelMembers").SetKeys(false, "ChannelId", "UserId") 380 tablem.ColMap("ChannelId").SetMaxSize(26) 381 tablem.ColMap("UserId").SetMaxSize(26) 382 tablem.ColMap("Roles").SetMaxSize(64) 383 tablem.ColMap("NotifyProps").SetMaxSize(2000) 384 385 tablePublicChannels := db.AddTableWithName(publicChannel{}, "PublicChannels").SetKeys(false, "Id") 386 tablePublicChannels.ColMap("Id").SetMaxSize(26) 387 tablePublicChannels.ColMap("TeamId").SetMaxSize(26) 388 tablePublicChannels.ColMap("DisplayName").SetMaxSize(64) 389 tablePublicChannels.ColMap("Name").SetMaxSize(64) 390 tablePublicChannels.SetUniqueTogether("Name", "TeamId") 391 tablePublicChannels.ColMap("Header").SetMaxSize(1024) 392 tablePublicChannels.ColMap("Purpose").SetMaxSize(250) 393 394 tableSidebarCategories := db.AddTableWithName(model.SidebarCategory{}, "SidebarCategories").SetKeys(false, "Id") 395 tableSidebarCategories.ColMap("Id").SetMaxSize(26) 396 tableSidebarCategories.ColMap("UserId").SetMaxSize(26) 397 tableSidebarCategories.ColMap("TeamId").SetMaxSize(26) 398 tableSidebarCategories.ColMap("Sorting").SetMaxSize(64) 399 tableSidebarCategories.ColMap("Type").SetMaxSize(64) 400 tableSidebarCategories.ColMap("DisplayName").SetMaxSize(64) 401 402 tableSidebarChannels := db.AddTableWithName(model.SidebarChannel{}, "SidebarChannels").SetKeys(false, "ChannelId", "UserId", "CategoryId") 403 tableSidebarChannels.ColMap("ChannelId").SetMaxSize(26) 404 tableSidebarChannels.ColMap("UserId").SetMaxSize(26) 405 tableSidebarChannels.ColMap("CategoryId").SetMaxSize(26) 406 } 407 408 return s 409 } 410 411 func (s SqlChannelStore) createIndexesIfNotExists() { 412 s.CreateIndexIfNotExists("idx_channels_team_id", "Channels", "TeamId") 413 s.CreateIndexIfNotExists("idx_channels_name", "Channels", "Name") 414 s.CreateIndexIfNotExists("idx_channels_update_at", "Channels", "UpdateAt") 415 s.CreateIndexIfNotExists("idx_channels_create_at", "Channels", "CreateAt") 416 s.CreateIndexIfNotExists("idx_channels_delete_at", "Channels", "DeleteAt") 417 418 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 419 s.CreateIndexIfNotExists("idx_channels_name_lower", "Channels", "lower(Name)") 420 s.CreateIndexIfNotExists("idx_channels_displayname_lower", "Channels", "lower(DisplayName)") 421 } 422 423 s.CreateIndexIfNotExists("idx_channelmembers_channel_id", "ChannelMembers", "ChannelId") 424 s.CreateIndexIfNotExists("idx_channelmembers_user_id", "ChannelMembers", "UserId") 425 426 s.CreateFullTextIndexIfNotExists("idx_channel_search_txt", "Channels", "Name, DisplayName, Purpose") 427 428 s.CreateIndexIfNotExists("idx_publicchannels_team_id", "PublicChannels", "TeamId") 429 s.CreateIndexIfNotExists("idx_publicchannels_name", "PublicChannels", "Name") 430 s.CreateIndexIfNotExists("idx_publicchannels_delete_at", "PublicChannels", "DeleteAt") 431 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 432 s.CreateIndexIfNotExists("idx_publicchannels_name_lower", "PublicChannels", "lower(Name)") 433 s.CreateIndexIfNotExists("idx_publicchannels_displayname_lower", "PublicChannels", "lower(DisplayName)") 434 } 435 s.CreateFullTextIndexIfNotExists("idx_publicchannels_search_txt", "PublicChannels", "Name, DisplayName, Purpose") 436 s.CreateIndexIfNotExists("idx_channels_scheme_id", "Channels", "SchemeId") 437 } 438 439 func (s SqlChannelStore) CreateInitialSidebarCategories(userId, teamId string) error { 440 transaction, err := s.GetMaster().Begin() 441 if err != nil { 442 return errors.Wrap(err, "CreateInitialSidebarCategories: begin_transaction") 443 } 444 defer finalizeTransaction(transaction) 445 446 if err := s.createInitialSidebarCategoriesT(transaction, userId, teamId); err != nil { 447 return errors.Wrap(err, "CreateInitialSidebarCategories: createInitialSidebarCategoriesT") 448 } 449 450 if err := transaction.Commit(); err != nil { 451 return errors.Wrap(err, "CreateInitialSidebarCategories: commit_transaction") 452 } 453 454 return nil 455 } 456 457 func (s SqlChannelStore) createInitialSidebarCategoriesT(transaction *gorp.Transaction, userId, teamId string) error { 458 selectQuery, selectParams, _ := s.getQueryBuilder(). 459 Select("Type"). 460 From("SidebarCategories"). 461 Where(sq.Eq{ 462 "UserId": userId, 463 "TeamId": teamId, 464 "Type": []model.SidebarCategoryType{model.SidebarCategoryFavorites, model.SidebarCategoryChannels, model.SidebarCategoryDirectMessages}, 465 }).ToSql() 466 467 var existingTypes []model.SidebarCategoryType 468 _, err := transaction.Select(&existingTypes, selectQuery, selectParams...) 469 if err != nil { 470 return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to select existing categories") 471 } 472 473 hasCategoryOfType := make(map[model.SidebarCategoryType]bool, len(existingTypes)) 474 for _, existingType := range existingTypes { 475 hasCategoryOfType[existingType] = true 476 } 477 478 if !hasCategoryOfType[model.SidebarCategoryFavorites] { 479 favoritesCategoryId := model.NewId() 480 481 // Create the SidebarChannels first since there's more opportunity for something to fail here 482 if err := s.migrateFavoritesToSidebarT(transaction, userId, teamId, favoritesCategoryId); err != nil { 483 return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to migrate favorites to sidebar") 484 } 485 486 if err := transaction.Insert(&model.SidebarCategory{ 487 DisplayName: "Favorites", // This will be retranslated by the client into the user's locale 488 Id: favoritesCategoryId, 489 UserId: userId, 490 TeamId: teamId, 491 Sorting: model.SidebarCategorySortDefault, 492 SortOrder: model.DefaultSidebarSortOrderFavorites, 493 Type: model.SidebarCategoryFavorites, 494 }); err != nil { 495 return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert favorites category") 496 } 497 } 498 499 if !hasCategoryOfType[model.SidebarCategoryChannels] { 500 if err := transaction.Insert(&model.SidebarCategory{ 501 DisplayName: "Channels", // This will be retranslateed by the client into the user's locale 502 Id: model.NewId(), 503 UserId: userId, 504 TeamId: teamId, 505 Sorting: model.SidebarCategorySortDefault, 506 SortOrder: model.DefaultSidebarSortOrderChannels, 507 Type: model.SidebarCategoryChannels, 508 }); err != nil { 509 return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert channels category") 510 } 511 } 512 513 if !hasCategoryOfType[model.SidebarCategoryDirectMessages] { 514 if err := transaction.Insert(&model.SidebarCategory{ 515 DisplayName: "Direct Messages", // This will be retranslateed by the client into the user's locale 516 Id: model.NewId(), 517 UserId: userId, 518 TeamId: teamId, 519 Sorting: model.SidebarCategorySortRecent, 520 SortOrder: model.DefaultSidebarSortOrderDMs, 521 Type: model.SidebarCategoryDirectMessages, 522 }); err != nil { 523 return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert direct messages category") 524 } 525 } 526 527 return nil 528 } 529 530 type userMembership struct { 531 UserId string 532 ChannelId string 533 CategoryId string 534 } 535 536 func (s SqlChannelStore) migrateMembershipToSidebar(transaction *gorp.Transaction, runningOrder *int64, sql string, args ...interface{}) ([]userMembership, error) { 537 var memberships []userMembership 538 if _, err := transaction.Select(&memberships, sql, args...); err != nil { 539 return nil, err 540 } 541 542 for _, favorite := range memberships { 543 sql, args, _ := s.getQueryBuilder(). 544 Insert("SidebarChannels"). 545 Columns("ChannelId", "UserId", "CategoryId", "SortOrder"). 546 Values(favorite.ChannelId, favorite.UserId, favorite.CategoryId, *runningOrder).ToSql() 547 548 if _, err := transaction.Exec(sql, args...); err != nil && !IsUniqueConstraintError(err, []string{"UserId", "PRIMARY"}) { 549 return nil, err 550 } 551 *runningOrder = *runningOrder + model.MinimalSidebarSortDistance 552 } 553 554 if err := transaction.Commit(); err != nil { 555 return nil, err 556 } 557 return memberships, nil 558 } 559 560 func (s SqlChannelStore) migrateFavoritesToSidebarT(transaction *gorp.Transaction, userId, teamId, favoritesCategoryId string) error { 561 favoritesQuery, favoritesParams, _ := s.getQueryBuilder(). 562 Select("Preferences.Name"). 563 From("Preferences"). 564 Join("Channels on Preferences.Name = Channels.Id"). 565 Join("ChannelMembers on Preferences.Name = ChannelMembers.ChannelId and Preferences.UserId = ChannelMembers.UserId"). 566 Where(sq.Eq{ 567 "Preferences.UserId": userId, 568 "Preferences.Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 569 "Preferences.Value": "true", 570 }). 571 Where(sq.Or{ 572 sq.Eq{"Channels.TeamId": teamId}, 573 sq.Eq{"Channels.TeamId": ""}, 574 }). 575 OrderBy( 576 "Channels.DisplayName", 577 "Channels.Name ASC", 578 ).ToSql() 579 580 var favoriteChannelIds []string 581 if _, err := transaction.Select(&favoriteChannelIds, favoritesQuery, favoritesParams...); err != nil { 582 return errors.Wrap(err, "migrateFavoritesToSidebarT: unable to get favorite channel IDs") 583 } 584 585 for i, channelId := range favoriteChannelIds { 586 if err := transaction.Insert(&model.SidebarChannel{ 587 ChannelId: channelId, 588 CategoryId: favoritesCategoryId, 589 UserId: userId, 590 SortOrder: int64(i * model.MinimalSidebarSortDistance), 591 }); err != nil { 592 return errors.Wrap(err, "migrateFavoritesToSidebarT: unable to insert SidebarChannel") 593 } 594 } 595 596 return nil 597 } 598 599 // MigrateFavoritesToSidebarChannels populates the SidebarChannels table by analyzing existing user preferences for favorites 600 // **IMPORTANT** This function should only be called from the migration task and shouldn't be used by itself 601 func (s SqlChannelStore) MigrateFavoritesToSidebarChannels(lastUserId string, runningOrder int64) (map[string]interface{}, error) { 602 transaction, err := s.GetMaster().Begin() 603 if err != nil { 604 return nil, err 605 } 606 607 defer finalizeTransaction(transaction) 608 609 sb := s. 610 getQueryBuilder(). 611 Select("Preferences.UserId", "Preferences.Name AS ChannelId", "SidebarCategories.Id AS CategoryId"). 612 From("Preferences"). 613 Where(sq.And{ 614 sq.Eq{"Preferences.Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL}, 615 sq.NotEq{"Preferences.Value": "false"}, 616 sq.NotEq{"SidebarCategories.Id": nil}, 617 sq.Gt{"Preferences.UserId": lastUserId}, 618 }). 619 LeftJoin("Channels ON (Channels.Id=Preferences.Name)"). 620 LeftJoin("SidebarCategories ON (SidebarCategories.UserId=Preferences.UserId AND SidebarCategories.Type='"+string(model.SidebarCategoryFavorites)+"' AND (SidebarCategories.TeamId=Channels.TeamId OR Channels.TeamId=''))"). 621 OrderBy("Preferences.UserId", "Channels.Name DESC"). 622 Limit(100) 623 624 sql, args, err := sb.ToSql() 625 if err != nil { 626 return nil, err 627 } 628 629 userFavorites, err := s.migrateMembershipToSidebar(transaction, &runningOrder, sql, args...) 630 if err != nil { 631 return nil, err 632 } 633 if len(userFavorites) == 0 { 634 return nil, nil 635 } 636 637 data := make(map[string]interface{}) 638 data["UserId"] = userFavorites[len(userFavorites)-1].UserId 639 data["SortOrder"] = runningOrder 640 return data, nil 641 } 642 643 // MigratePublicChannels initializes the PublicChannels table with data created before this version 644 // of the Mattermost server kept it up-to-date. 645 func (s SqlChannelStore) MigratePublicChannels() error { 646 if _, err := s.GetMaster().Exec(` 647 INSERT INTO PublicChannels 648 (Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose) 649 SELECT 650 c.Id, c.DeleteAt, c.TeamId, c.DisplayName, c.Name, c.Header, c.Purpose 651 FROM 652 Channels c 653 LEFT JOIN 654 PublicChannels pc ON (pc.Id = c.Id) 655 WHERE 656 c.Type = 'O' 657 AND pc.Id IS NULL 658 `); err != nil { 659 return err 660 } 661 662 return nil 663 } 664 665 func (s SqlChannelStore) upsertPublicChannelT(transaction *gorp.Transaction, channel *model.Channel) error { 666 publicChannel := &publicChannel{ 667 Id: channel.Id, 668 DeleteAt: channel.DeleteAt, 669 TeamId: channel.TeamId, 670 DisplayName: channel.DisplayName, 671 Name: channel.Name, 672 Header: channel.Header, 673 Purpose: channel.Purpose, 674 } 675 676 if channel.Type != model.CHANNEL_OPEN { 677 if _, err := transaction.Delete(publicChannel); err != nil { 678 return errors.Wrap(err, "failed to delete public channel") 679 } 680 681 return nil 682 } 683 684 if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 685 // Leverage native upsert for MySQL, since RowsAffected returns 0 if the row exists 686 // but no changes were made, breaking the update-then-insert paradigm below when 687 // the row already exists. (Postgres 9.4 doesn't support native upsert.) 688 if _, err := transaction.Exec(` 689 INSERT INTO 690 PublicChannels(Id, DeleteAt, TeamId, DisplayName, Name, Header, Purpose) 691 VALUES 692 (:Id, :DeleteAt, :TeamId, :DisplayName, :Name, :Header, :Purpose) 693 ON DUPLICATE KEY UPDATE 694 DeleteAt = :DeleteAt, 695 TeamId = :TeamId, 696 DisplayName = :DisplayName, 697 Name = :Name, 698 Header = :Header, 699 Purpose = :Purpose; 700 `, map[string]interface{}{ 701 "Id": publicChannel.Id, 702 "DeleteAt": publicChannel.DeleteAt, 703 "TeamId": publicChannel.TeamId, 704 "DisplayName": publicChannel.DisplayName, 705 "Name": publicChannel.Name, 706 "Header": publicChannel.Header, 707 "Purpose": publicChannel.Purpose, 708 }); err != nil { 709 return errors.Wrap(err, "failed to insert public channel") 710 } 711 } else { 712 count, err := transaction.Update(publicChannel) 713 if err != nil { 714 return errors.Wrap(err, "failed to update public channel") 715 } 716 if count > 0 { 717 return nil 718 } 719 720 if err := transaction.Insert(publicChannel); err != nil { 721 return errors.Wrap(err, "failed to insert public channel") 722 } 723 } 724 725 return nil 726 } 727 728 // Save writes the (non-direct) channel channel to the database. 729 func (s SqlChannelStore) Save(channel *model.Channel, maxChannelsPerTeam int64) (*model.Channel, error) { 730 if channel.DeleteAt != 0 { 731 return nil, store.NewErrInvalidInput("Channel", "DeleteAt", channel.DeleteAt) 732 } 733 734 if channel.Type == model.CHANNEL_DIRECT { 735 return nil, store.NewErrInvalidInput("Channel", "Type", channel.Type) 736 } 737 738 var newChannel *model.Channel 739 err := store.WithDeadlockRetry(func() error { 740 transaction, err := s.GetMaster().Begin() 741 if err != nil { 742 return errors.Wrap(err, "begin_transaction") 743 } 744 defer finalizeTransaction(transaction) 745 746 newChannel, err = s.saveChannelT(transaction, channel, maxChannelsPerTeam) 747 if err != nil { 748 return err 749 } 750 751 // Additionally propagate the write to the PublicChannels table. 752 if err := s.upsertPublicChannelT(transaction, newChannel); err != nil { 753 return errors.Wrap(err, "upsert_public_channel") 754 } 755 756 if err := transaction.Commit(); err != nil { 757 return errors.Wrap(err, "commit_transaction") 758 } 759 return nil 760 }) 761 // There are cases when in case of conflict, the original channel value is returned. 762 // So we return both and let the caller do the checks. 763 return newChannel, err 764 } 765 766 func (s SqlChannelStore) CreateDirectChannel(user *model.User, otherUser *model.User) (*model.Channel, error) { 767 channel := new(model.Channel) 768 769 channel.DisplayName = "" 770 channel.Name = model.GetDMNameFromIds(otherUser.Id, user.Id) 771 772 channel.Header = "" 773 channel.Type = model.CHANNEL_DIRECT 774 775 cm1 := &model.ChannelMember{ 776 UserId: user.Id, 777 NotifyProps: model.GetDefaultChannelNotifyProps(), 778 SchemeGuest: user.IsGuest(), 779 SchemeUser: !user.IsGuest(), 780 } 781 cm2 := &model.ChannelMember{ 782 UserId: otherUser.Id, 783 NotifyProps: model.GetDefaultChannelNotifyProps(), 784 SchemeGuest: otherUser.IsGuest(), 785 SchemeUser: !otherUser.IsGuest(), 786 } 787 788 return s.SaveDirectChannel(channel, cm1, cm2) 789 } 790 791 func (s SqlChannelStore) SaveDirectChannel(directchannel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) (*model.Channel, error) { 792 if directchannel.DeleteAt != 0 { 793 return nil, store.NewErrInvalidInput("Channel", "DeleteAt", directchannel.DeleteAt) 794 } 795 796 if directchannel.Type != model.CHANNEL_DIRECT { 797 return nil, store.NewErrInvalidInput("Channel", "Type", directchannel.Type) 798 } 799 800 transaction, err := s.GetMaster().Begin() 801 if err != nil { 802 return nil, errors.Wrap(err, "begin_transaction") 803 } 804 defer finalizeTransaction(transaction) 805 806 directchannel.TeamId = "" 807 newChannel, err := s.saveChannelT(transaction, directchannel, 0) 808 if err != nil { 809 return newChannel, err 810 } 811 812 // Members need new channel ID 813 member1.ChannelId = newChannel.Id 814 member2.ChannelId = newChannel.Id 815 816 if member1.UserId != member2.UserId { 817 _, err = s.saveMultipleMembersT(transaction, []*model.ChannelMember{member1, member2}) 818 } else { 819 _, err = s.saveMemberT(transaction, member2) 820 } 821 if err != nil { 822 return nil, err 823 } 824 825 if err := transaction.Commit(); err != nil { 826 return nil, errors.Wrap(err, "commit_transaction") 827 } 828 829 return newChannel, nil 830 831 } 832 833 func (s SqlChannelStore) saveChannelT(transaction *gorp.Transaction, channel *model.Channel, maxChannelsPerTeam int64) (*model.Channel, error) { 834 if len(channel.Id) > 0 { 835 return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id) 836 } 837 838 channel.PreSave() 839 if err := channel.IsValid(); err != nil { // TODO: this needs to return plain error in v6. 840 return nil, err // we just pass through the error as-is for now. 841 } 842 843 if channel.Type != model.CHANNEL_DIRECT && channel.Type != model.CHANNEL_GROUP && maxChannelsPerTeam >= 0 { 844 if count, err := transaction.SelectInt("SELECT COUNT(0) FROM Channels WHERE TeamId = :TeamId AND DeleteAt = 0 AND (Type = 'O' OR Type = 'P')", map[string]interface{}{"TeamId": channel.TeamId}); err != nil { 845 return nil, errors.Wrapf(err, "save_channel_count: teamId=%s", channel.TeamId) 846 } else if count >= maxChannelsPerTeam { 847 return nil, store.NewErrLimitExceeded("channels_per_team", int(count), "teamId="+channel.TeamId) 848 } 849 } 850 851 if err := transaction.Insert(channel); err != nil { 852 if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) { 853 dupChannel := model.Channel{} 854 s.GetMaster().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name = :Name", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) 855 return &dupChannel, store.NewErrConflict("Channel", err, "id="+channel.Id) 856 } 857 return nil, errors.Wrapf(err, "save_channel: id=%s", channel.Id) 858 } 859 return channel, nil 860 } 861 862 // Update writes the updated channel to the database. 863 func (s SqlChannelStore) Update(channel *model.Channel) (*model.Channel, error) { 864 transaction, err := s.GetMaster().Begin() 865 if err != nil { 866 return nil, errors.Wrap(err, "begin_transaction") 867 } 868 defer finalizeTransaction(transaction) 869 870 updatedChannel, appErr := s.updateChannelT(transaction, channel) 871 if appErr != nil { 872 return nil, appErr 873 } 874 875 // Additionally propagate the write to the PublicChannels table. 876 if err := s.upsertPublicChannelT(transaction, updatedChannel); err != nil { 877 return nil, errors.Wrap(err, "upsertPublicChannelT: failed to upsert channel") 878 } 879 880 if err := transaction.Commit(); err != nil { 881 return nil, errors.Wrap(err, "commit_transaction") 882 } 883 return updatedChannel, nil 884 } 885 886 func (s SqlChannelStore) updateChannelT(transaction *gorp.Transaction, channel *model.Channel) (*model.Channel, error) { 887 channel.PreUpdate() 888 889 if channel.DeleteAt != 0 { 890 return nil, store.NewErrInvalidInput("Channel", "DeleteAt", channel.DeleteAt) 891 } 892 893 if err := channel.IsValid(); err != nil { 894 return nil, err 895 } 896 897 count, err := transaction.Update(channel) 898 if err != nil { 899 if IsUniqueConstraintError(err, []string{"Name", "channels_name_teamid_key"}) { 900 dupChannel := model.Channel{} 901 s.GetReplica().SelectOne(&dupChannel, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Name= :Name AND DeleteAt > 0", map[string]interface{}{"TeamId": channel.TeamId, "Name": channel.Name}) 902 if dupChannel.DeleteAt > 0 { 903 return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id) 904 } 905 return nil, store.NewErrInvalidInput("Channel", "Id", channel.Id) 906 } 907 return nil, errors.Wrapf(err, "failed to update channel with id=%s", channel.Id) 908 } 909 910 if count != 1 { 911 return nil, fmt.Errorf("the expected number of channels to be updated is 1 but was %d", count) 912 } 913 914 return channel, nil 915 } 916 917 func (s SqlChannelStore) GetChannelUnread(channelId, userId string) (*model.ChannelUnread, *model.AppError) { 918 var unreadChannel model.ChannelUnread 919 err := s.GetReplica().SelectOne(&unreadChannel, 920 `SELECT 921 Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, ChannelMembers.MentionCount MentionCount, ChannelMembers.NotifyProps NotifyProps 922 FROM 923 Channels, ChannelMembers 924 WHERE 925 Id = ChannelId 926 AND Id = :ChannelId 927 AND UserId = :UserId 928 AND DeleteAt = 0`, 929 map[string]interface{}{"ChannelId": channelId, "UserId": userId}) 930 931 if err != nil { 932 if err == sql.ErrNoRows { 933 return nil, model.NewAppError("SqlChannelStore.GetChannelUnread", "store.sql_channel.get_unread.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusNotFound) 934 } 935 return nil, model.NewAppError("SqlChannelStore.GetChannelUnread", "store.sql_channel.get_unread.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError) 936 } 937 return &unreadChannel, nil 938 } 939 940 func (s SqlChannelStore) InvalidateChannel(id string) { 941 } 942 943 func (s SqlChannelStore) InvalidateChannelByName(teamId, name string) { 944 channelByNameCache.Remove(teamId + name) 945 if s.metrics != nil { 946 s.metrics.IncrementMemCacheInvalidationCounter("Channel by Name - Remove by TeamId and Name") 947 } 948 } 949 950 func (s SqlChannelStore) Get(id string, allowFromCache bool) (*model.Channel, error) { 951 return s.get(id, false, allowFromCache) 952 } 953 954 func (s SqlChannelStore) GetPinnedPosts(channelId string) (*model.PostList, *model.AppError) { 955 pl := model.NewPostList() 956 957 var posts []*model.Post 958 if _, err := s.GetReplica().Select(&posts, "SELECT *, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0) as ReplyCount FROM Posts p WHERE IsPinned = true AND ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt ASC", map[string]interface{}{"ChannelId": channelId}); err != nil { 959 return nil, model.NewAppError("SqlPostStore.GetPinnedPosts", "store.sql_channel.pinned_posts.app_error", nil, err.Error(), http.StatusInternalServerError) 960 } 961 for _, post := range posts { 962 pl.AddPost(post) 963 pl.AddOrder(post.Id) 964 } 965 return pl, nil 966 } 967 968 func (s SqlChannelStore) GetFromMaster(id string) (*model.Channel, error) { 969 return s.get(id, true, false) 970 } 971 972 func (s SqlChannelStore) get(id string, master bool, allowFromCache bool) (*model.Channel, error) { 973 var db *gorp.DbMap 974 975 if master { 976 db = s.GetMaster() 977 } else { 978 db = s.GetReplica() 979 } 980 981 obj, err := db.Get(model.Channel{}, id) 982 if err != nil { 983 return nil, errors.Wrapf(err, "failed to find channel with id = %s", id) 984 } 985 986 if obj == nil { 987 return nil, store.NewErrNotFound("Channel", id) 988 } 989 990 ch := obj.(*model.Channel) 991 return ch, nil 992 } 993 994 // Delete records the given deleted timestamp to the channel in question. 995 func (s SqlChannelStore) Delete(channelId string, time int64) error { 996 return s.SetDeleteAt(channelId, time, time) 997 } 998 999 // Restore reverts a previous deleted timestamp from the channel in question. 1000 func (s SqlChannelStore) Restore(channelId string, time int64) error { 1001 return s.SetDeleteAt(channelId, 0, time) 1002 } 1003 1004 // SetDeleteAt records the given deleted and updated timestamp to the channel in question. 1005 func (s SqlChannelStore) SetDeleteAt(channelId string, deleteAt, updateAt int64) error { 1006 defer s.InvalidateChannel(channelId) 1007 1008 transaction, err := s.GetMaster().Begin() 1009 if err != nil { 1010 return errors.Wrap(err, "SetDeleteAt: begin_transaction") 1011 } 1012 defer finalizeTransaction(transaction) 1013 1014 err = s.setDeleteAtT(transaction, channelId, deleteAt, updateAt) 1015 if err != nil { 1016 return errors.Wrap(err, "setDeleteAtT") 1017 } 1018 1019 // Additionally propagate the write to the PublicChannels table. 1020 if _, err := transaction.Exec(` 1021 UPDATE 1022 PublicChannels 1023 SET 1024 DeleteAt = :DeleteAt 1025 WHERE 1026 Id = :ChannelId 1027 `, map[string]interface{}{ 1028 "DeleteAt": deleteAt, 1029 "ChannelId": channelId, 1030 }); err != nil { 1031 return errors.Wrapf(err, "failed to delete public channels with id=%s", channelId) 1032 } 1033 1034 if err := transaction.Commit(); err != nil { 1035 return errors.Wrapf(err, "SetDeleteAt: commit_transaction") 1036 } 1037 1038 return nil 1039 } 1040 1041 func (s SqlChannelStore) setDeleteAtT(transaction *gorp.Transaction, channelId string, deleteAt, updateAt int64) error { 1042 _, err := transaction.Exec("Update Channels SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :ChannelId", map[string]interface{}{"DeleteAt": deleteAt, "UpdateAt": updateAt, "ChannelId": channelId}) 1043 if err != nil { 1044 return errors.Wrapf(err, "failed to delete channel with id=%s", channelId) 1045 } 1046 1047 return nil 1048 } 1049 1050 // PermanentDeleteByTeam removes all channels for the given team from the database. 1051 func (s SqlChannelStore) PermanentDeleteByTeam(teamId string) error { 1052 transaction, err := s.GetMaster().Begin() 1053 if err != nil { 1054 return errors.Wrap(err, "PermanentDeleteByTeam: begin_transaction") 1055 } 1056 defer finalizeTransaction(transaction) 1057 1058 if err := s.permanentDeleteByTeamtT(transaction, teamId); err != nil { 1059 return errors.Wrap(err, "permanentDeleteByTeamtT") 1060 } 1061 1062 // Additionally propagate the deletions to the PublicChannels table. 1063 if _, err := transaction.Exec(` 1064 DELETE FROM 1065 PublicChannels 1066 WHERE 1067 TeamId = :TeamId 1068 `, map[string]interface{}{ 1069 "TeamId": teamId, 1070 }); err != nil { 1071 return errors.Wrapf(err, "failed to delete public channels by team with teamId=%s", teamId) 1072 } 1073 1074 if err := transaction.Commit(); err != nil { 1075 return errors.Wrap(err, "PermanentDeleteByTeam: commit_transaction") 1076 } 1077 1078 return nil 1079 } 1080 1081 func (s SqlChannelStore) permanentDeleteByTeamtT(transaction *gorp.Transaction, teamId string) error { 1082 if _, err := transaction.Exec("DELETE FROM Channels WHERE TeamId = :TeamId", map[string]interface{}{"TeamId": teamId}); err != nil { 1083 return errors.Wrapf(err, "failed to delete channel by team with teamId=%s", teamId) 1084 } 1085 1086 return nil 1087 } 1088 1089 // PermanentDelete removes the given channel from the database. 1090 func (s SqlChannelStore) PermanentDelete(channelId string) error { 1091 transaction, err := s.GetMaster().Begin() 1092 if err != nil { 1093 return errors.Wrap(err, "PermanentDelete: begin_transaction") 1094 } 1095 defer finalizeTransaction(transaction) 1096 1097 if err := s.permanentDeleteT(transaction, channelId); err != nil { 1098 return errors.Wrap(err, "permanentDeleteT") 1099 } 1100 1101 // Additionally propagate the deletion to the PublicChannels table. 1102 if _, err := transaction.Exec(` 1103 DELETE FROM 1104 PublicChannels 1105 WHERE 1106 Id = :ChannelId 1107 `, map[string]interface{}{ 1108 "ChannelId": channelId, 1109 }); err != nil { 1110 return errors.Wrapf(err, "failed to delete public channels with id=%s", channelId) 1111 } 1112 1113 if err := transaction.Commit(); err != nil { 1114 return errors.Wrap(err, "PermanentDelete: commit_transaction") 1115 } 1116 1117 return nil 1118 } 1119 1120 func (s SqlChannelStore) permanentDeleteT(transaction *gorp.Transaction, channelId string) error { 1121 if _, err := transaction.Exec("DELETE FROM Channels WHERE Id = :ChannelId", map[string]interface{}{"ChannelId": channelId}); err != nil { 1122 return errors.Wrapf(err, "failed to delete channel with id=%s", channelId) 1123 } 1124 1125 return nil 1126 } 1127 1128 func (s SqlChannelStore) PermanentDeleteMembersByChannel(channelId string) *model.AppError { 1129 _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE ChannelId = :ChannelId", map[string]interface{}{"ChannelId": channelId}) 1130 if err != nil { 1131 return model.NewAppError("SqlChannelStore.RemoveAllMembersByChannel", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 1132 } 1133 1134 return nil 1135 } 1136 1137 func (s SqlChannelStore) GetChannels(teamId string, userId string, includeDeleted bool, lastDeleteAt int) (*model.ChannelList, error) { 1138 query := s.getQueryBuilder(). 1139 Select("Channels.*"). 1140 From("Channels, ChannelMembers"). 1141 Where( 1142 sq.And{ 1143 sq.Expr("Id = ChannelId"), 1144 sq.Eq{"UserId": userId}, 1145 sq.Or{ 1146 sq.Eq{"TeamId": teamId}, 1147 sq.Eq{"TeamId": ""}, 1148 }, 1149 }, 1150 ). 1151 OrderBy("DisplayName") 1152 1153 if includeDeleted { 1154 if lastDeleteAt != 0 { 1155 // We filter by non-archived, and archived >= a timestamp. 1156 query = query.Where(sq.Or{ 1157 sq.Eq{"DeleteAt": 0}, 1158 sq.GtOrEq{"DeleteAt": lastDeleteAt}, 1159 }) 1160 } 1161 // If lastDeleteAt is not set, we include everything. That means no filter is needed. 1162 } else { 1163 // Don't include archived channels. 1164 query = query.Where(sq.Eq{"DeleteAt": 0}) 1165 } 1166 1167 channels := &model.ChannelList{} 1168 sql, args, err := query.ToSql() 1169 if err != nil { 1170 return nil, errors.Wrapf(err, "getchannels_tosql") 1171 } 1172 1173 _, err = s.GetReplica().Select(channels, sql, args...) 1174 if err != nil { 1175 return nil, errors.Wrapf(err, "failed to get channels with TeamId=%s and UserId=%s", teamId, userId) 1176 } 1177 1178 if len(*channels) == 0 { 1179 return nil, store.NewErrNotFound("Channel", "userId="+userId) 1180 } 1181 1182 return channels, nil 1183 } 1184 1185 func (s SqlChannelStore) GetAllChannels(offset, limit int, opts store.ChannelSearchOpts) (*model.ChannelListWithTeamData, error) { 1186 query := s.getAllChannelsQuery(opts, false) 1187 1188 query = query.OrderBy("c.DisplayName, Teams.DisplayName").Limit(uint64(limit)).Offset(uint64(offset)) 1189 1190 queryString, args, err := query.ToSql() 1191 if err != nil { 1192 return nil, errors.Wrap(err, "failed to create query") 1193 } 1194 1195 data := &model.ChannelListWithTeamData{} 1196 _, err = s.GetReplica().Select(data, queryString, args...) 1197 1198 if err != nil { 1199 return nil, errors.Wrap(err, "failed to get all channels") 1200 } 1201 1202 return data, nil 1203 } 1204 1205 func (s SqlChannelStore) GetAllChannelsCount(opts store.ChannelSearchOpts) (int64, error) { 1206 query := s.getAllChannelsQuery(opts, true) 1207 1208 queryString, args, err := query.ToSql() 1209 if err != nil { 1210 return 0, errors.Wrap(err, "failed to create query") 1211 } 1212 1213 count, err := s.GetReplica().SelectInt(queryString, args...) 1214 if err != nil { 1215 return 0, errors.Wrap(err, "failed to count all channels") 1216 } 1217 1218 return count, nil 1219 } 1220 1221 func (s SqlChannelStore) getAllChannelsQuery(opts store.ChannelSearchOpts, forCount bool) sq.SelectBuilder { 1222 var selectStr string 1223 if forCount { 1224 selectStr = "count(c.Id)" 1225 } else { 1226 selectStr = "c.*, Teams.DisplayName AS TeamDisplayName, Teams.Name AS TeamName, Teams.UpdateAt AS TeamUpdateAt" 1227 } 1228 1229 query := s.getQueryBuilder(). 1230 Select(selectStr). 1231 From("Channels AS c"). 1232 Where(sq.Eq{"c.Type": []string{model.CHANNEL_PRIVATE, model.CHANNEL_OPEN}}) 1233 1234 if !forCount { 1235 query = query.Join("Teams ON Teams.Id = c.TeamId") 1236 } 1237 1238 if !opts.IncludeDeleted { 1239 query = query.Where(sq.Eq{"c.DeleteAt": int(0)}) 1240 } 1241 1242 if len(opts.NotAssociatedToGroup) > 0 { 1243 query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup) 1244 } 1245 1246 if len(opts.ExcludeChannelNames) > 0 { 1247 query = query.Where(sq.NotEq{"c.Name": opts.ExcludeChannelNames}) 1248 } 1249 1250 return query 1251 } 1252 1253 func (s SqlChannelStore) GetMoreChannels(teamId string, userId string, offset int, limit int) (*model.ChannelList, error) { 1254 channels := &model.ChannelList{} 1255 _, err := s.GetReplica().Select(channels, ` 1256 SELECT 1257 Channels.* 1258 FROM 1259 Channels 1260 JOIN 1261 PublicChannels c ON (c.Id = Channels.Id) 1262 WHERE 1263 c.TeamId = :TeamId 1264 AND c.DeleteAt = 0 1265 AND c.Id NOT IN ( 1266 SELECT 1267 c.Id 1268 FROM 1269 PublicChannels c 1270 JOIN 1271 ChannelMembers cm ON (cm.ChannelId = c.Id) 1272 WHERE 1273 c.TeamId = :TeamId 1274 AND cm.UserId = :UserId 1275 AND c.DeleteAt = 0 1276 ) 1277 ORDER BY 1278 c.DisplayName 1279 LIMIT :Limit 1280 OFFSET :Offset 1281 `, map[string]interface{}{ 1282 "TeamId": teamId, 1283 "UserId": userId, 1284 "Limit": limit, 1285 "Offset": offset, 1286 }) 1287 1288 if err != nil { 1289 return nil, errors.Wrapf(err, "failed getting channels with teamId=%s and userId=%s", teamId, userId) 1290 } 1291 1292 return channels, nil 1293 } 1294 1295 func (s SqlChannelStore) GetPrivateChannelsForTeam(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) { 1296 channels := &model.ChannelList{} 1297 1298 query := s.getQueryBuilder(). 1299 Select("*"). 1300 From("Channels"). 1301 Where(sq.Eq{"Type": model.CHANNEL_PRIVATE, "TeamId": teamId, "DeleteAt": 0}). 1302 OrderBy("DisplayName"). 1303 Limit(uint64(limit)). 1304 Offset(uint64(offset)) 1305 1306 sql, args, err := query.ToSql() 1307 if err != nil { 1308 return nil, model.NewAppError("SqlChannelStore.GetPrivateChannelsForTeam", "store.sql_channel.get_private_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 1309 } 1310 1311 _, err = s.GetReplica().Select(channels, sql, args...) 1312 if err != nil { 1313 return nil, model.NewAppError("SqlChannelStore.GetPrivateChannelsForTeam", "store.sql_channel.get_private_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 1314 } 1315 return channels, nil 1316 } 1317 1318 func (s SqlChannelStore) GetPublicChannelsForTeam(teamId string, offset int, limit int) (*model.ChannelList, *model.AppError) { 1319 channels := &model.ChannelList{} 1320 _, err := s.GetReplica().Select(channels, ` 1321 SELECT 1322 Channels.* 1323 FROM 1324 Channels 1325 JOIN 1326 PublicChannels pc ON (pc.Id = Channels.Id) 1327 WHERE 1328 pc.TeamId = :TeamId 1329 AND pc.DeleteAt = 0 1330 ORDER BY pc.DisplayName 1331 LIMIT :Limit 1332 OFFSET :Offset 1333 `, map[string]interface{}{ 1334 "TeamId": teamId, 1335 "Limit": limit, 1336 "Offset": offset, 1337 }) 1338 1339 if err != nil { 1340 return nil, model.NewAppError("SqlChannelStore.GetPublicChannelsForTeam", "store.sql_channel.get_public_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 1341 } 1342 1343 return channels, nil 1344 } 1345 1346 func (s SqlChannelStore) GetPublicChannelsByIdsForTeam(teamId string, channelIds []string) (*model.ChannelList, *model.AppError) { 1347 props := make(map[string]interface{}) 1348 props["teamId"] = teamId 1349 1350 idQuery := "" 1351 1352 for index, channelId := range channelIds { 1353 if len(idQuery) > 0 { 1354 idQuery += ", " 1355 } 1356 1357 props["channelId"+strconv.Itoa(index)] = channelId 1358 idQuery += ":channelId" + strconv.Itoa(index) 1359 } 1360 1361 data := &model.ChannelList{} 1362 _, err := s.GetReplica().Select(data, ` 1363 SELECT 1364 Channels.* 1365 FROM 1366 Channels 1367 JOIN 1368 PublicChannels pc ON (pc.Id = Channels.Id) 1369 WHERE 1370 pc.TeamId = :teamId 1371 AND pc.DeleteAt = 0 1372 AND pc.Id IN (`+idQuery+`) 1373 ORDER BY pc.DisplayName 1374 `, props) 1375 1376 if err != nil { 1377 return nil, model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.get.app_error", nil, err.Error(), http.StatusInternalServerError) 1378 } 1379 1380 if len(*data) == 0 { 1381 return nil, model.NewAppError("SqlChannelStore.GetPublicChannelsByIdsForTeam", "store.sql_channel.get_channels_by_ids.not_found.app_error", nil, "", http.StatusNotFound) 1382 } 1383 1384 return data, nil 1385 } 1386 1387 type channelIdWithCountAndUpdateAt struct { 1388 Id string 1389 TotalMsgCount int64 1390 UpdateAt int64 1391 } 1392 1393 func (s SqlChannelStore) GetChannelCounts(teamId string, userId string) (*model.ChannelCounts, *model.AppError) { 1394 var data []channelIdWithCountAndUpdateAt 1395 _, err := s.GetReplica().Select(&data, "SELECT Id, TotalMsgCount, UpdateAt FROM Channels WHERE Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId) AND (TeamId = :TeamId OR TeamId = '') AND DeleteAt = 0 ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId}) 1396 1397 if err != nil { 1398 return nil, model.NewAppError("SqlChannelStore.GetChannelCounts", "store.sql_channel.get_channel_counts.get.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 1399 } 1400 1401 counts := &model.ChannelCounts{Counts: make(map[string]int64), UpdateTimes: make(map[string]int64)} 1402 for i := range data { 1403 v := data[i] 1404 counts.Counts[v.Id] = v.TotalMsgCount 1405 counts.UpdateTimes[v.Id] = v.UpdateAt 1406 } 1407 1408 return counts, nil 1409 } 1410 1411 func (s SqlChannelStore) GetTeamChannels(teamId string) (*model.ChannelList, *model.AppError) { 1412 data := &model.ChannelList{} 1413 _, err := s.GetReplica().Select(data, "SELECT * FROM Channels WHERE TeamId = :TeamId And Type != 'D' ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId}) 1414 1415 if err != nil { 1416 // TODO: This error key would go away once this store method is migrated to return plain errors 1417 return nil, model.NewAppError("SqlChannelStore.GetTeamChannels", "app.channel.get_channels.get.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 1418 } 1419 1420 if len(*data) == 0 { 1421 // TODO: This error key would go away once this store method is migrated to return plain errors 1422 return nil, model.NewAppError("SqlChannelStore.GetTeamChannels", "app.channel.get_channels.not_found.app_error", nil, "teamId="+teamId, http.StatusNotFound) 1423 } 1424 1425 return data, nil 1426 } 1427 1428 func (s SqlChannelStore) GetByName(teamId string, name string, allowFromCache bool) (*model.Channel, error) { 1429 return s.getByName(teamId, name, false, allowFromCache) 1430 } 1431 1432 func (s SqlChannelStore) GetByNames(teamId string, names []string, allowFromCache bool) ([]*model.Channel, error) { 1433 var channels []*model.Channel 1434 1435 if allowFromCache { 1436 var misses []string 1437 visited := make(map[string]struct{}) 1438 for _, name := range names { 1439 if _, ok := visited[name]; ok { 1440 continue 1441 } 1442 visited[name] = struct{}{} 1443 var cacheItem *model.Channel 1444 if err := channelByNameCache.Get(teamId+name, &cacheItem); err == nil { 1445 channels = append(channels, cacheItem) 1446 } else { 1447 misses = append(misses, name) 1448 } 1449 } 1450 names = misses 1451 } 1452 1453 if len(names) > 0 { 1454 props := map[string]interface{}{} 1455 var namePlaceholders []string 1456 for _, name := range names { 1457 key := fmt.Sprintf("Name%v", len(namePlaceholders)) 1458 props[key] = name 1459 namePlaceholders = append(namePlaceholders, ":"+key) 1460 } 1461 1462 var query string 1463 if teamId == "" { 1464 query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND DeleteAt = 0` 1465 } else { 1466 props["TeamId"] = teamId 1467 query = `SELECT * FROM Channels WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND TeamId = :TeamId AND DeleteAt = 0` 1468 } 1469 1470 var dbChannels []*model.Channel 1471 if _, err := s.GetReplica().Select(&dbChannels, query, props); err != nil && err != sql.ErrNoRows { 1472 msg := fmt.Sprintf("failed to get channels with names=%v", names) 1473 if teamId != "" { 1474 msg += fmt.Sprintf("teamId=%s", teamId) 1475 } 1476 return nil, errors.Wrap(err, msg) 1477 } 1478 for _, channel := range dbChannels { 1479 channelByNameCache.SetWithExpiry(teamId+channel.Name, channel, CHANNEL_CACHE_DURATION) 1480 channels = append(channels, channel) 1481 } 1482 // Not all channels are in cache. Increment aggregate miss counter. 1483 if s.metrics != nil { 1484 s.metrics.IncrementMemCacheMissCounter("Channel By Name - Aggregate") 1485 } 1486 } else { 1487 // All of the channel names are in cache. Increment aggregate hit counter. 1488 if s.metrics != nil { 1489 s.metrics.IncrementMemCacheHitCounter("Channel By Name - Aggregate") 1490 } 1491 } 1492 1493 return channels, nil 1494 } 1495 1496 func (s SqlChannelStore) GetByNameIncludeDeleted(teamId string, name string, allowFromCache bool) (*model.Channel, error) { 1497 return s.getByName(teamId, name, true, allowFromCache) 1498 } 1499 1500 func (s SqlChannelStore) getByName(teamId string, name string, includeDeleted bool, allowFromCache bool) (*model.Channel, error) { 1501 var query string 1502 if includeDeleted { 1503 query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name" 1504 } else { 1505 query = "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name AND DeleteAt = 0" 1506 } 1507 channel := model.Channel{} 1508 1509 if allowFromCache { 1510 var cacheItem *model.Channel 1511 if err := channelByNameCache.Get(teamId+name, &cacheItem); err == nil { 1512 if s.metrics != nil { 1513 s.metrics.IncrementMemCacheHitCounter("Channel By Name") 1514 } 1515 return cacheItem, nil 1516 } 1517 if s.metrics != nil { 1518 s.metrics.IncrementMemCacheMissCounter("Channel By Name") 1519 } 1520 } 1521 1522 if err := s.GetReplica().SelectOne(&channel, query, map[string]interface{}{"TeamId": teamId, "Name": name}); err != nil { 1523 if err == sql.ErrNoRows { 1524 return nil, store.NewErrNotFound("Channel", fmt.Sprintf("TeamId=%s&Name=%s", teamId, name)) 1525 } 1526 return nil, errors.Wrapf(err, "failed to find channel with TeamId=%s and Name=%s", teamId, name) 1527 } 1528 1529 channelByNameCache.SetWithExpiry(teamId+name, &channel, CHANNEL_CACHE_DURATION) 1530 return &channel, nil 1531 } 1532 1533 func (s SqlChannelStore) GetDeletedByName(teamId string, name string) (*model.Channel, error) { 1534 channel := model.Channel{} 1535 1536 if err := s.GetReplica().SelectOne(&channel, "SELECT * FROM Channels WHERE (TeamId = :TeamId OR TeamId = '') AND Name = :Name AND DeleteAt != 0", map[string]interface{}{"TeamId": teamId, "Name": name}); err != nil { 1537 if err == sql.ErrNoRows { 1538 return nil, store.NewErrNotFound("Channel", fmt.Sprintf("name=%s", name)) 1539 } 1540 return nil, errors.Wrapf(err, "failed to get channel by teamId=%s and name=%s", teamId, name) 1541 } 1542 1543 return &channel, nil 1544 } 1545 1546 func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int, userId string) (*model.ChannelList, error) { 1547 channels := &model.ChannelList{} 1548 1549 query := ` 1550 SELECT * FROM Channels 1551 WHERE (TeamId = :TeamId OR TeamId = '') 1552 AND DeleteAt != 0 1553 AND Type != 'P' 1554 UNION 1555 SELECT * FROM Channels 1556 WHERE (TeamId = :TeamId OR TeamId = '') 1557 AND DeleteAt != 0 1558 AND Type = 'P' 1559 AND Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId) 1560 ORDER BY DisplayName LIMIT :Limit OFFSET :Offset 1561 ` 1562 1563 if _, err := s.GetReplica().Select(channels, query, map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset, "UserId": userId}); err != nil { 1564 if err == sql.ErrNoRows { 1565 return nil, store.NewErrNotFound("Channel", fmt.Sprintf("TeamId=%s,UserId=%s", teamId, userId)) 1566 } 1567 return nil, errors.Wrapf(err, "failed to get deleted channels with TeamId=%s and UserId=%s", teamId, userId) 1568 } 1569 1570 return channels, nil 1571 } 1572 1573 var CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY = ` 1574 SELECT 1575 ChannelMembers.*, 1576 TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole, 1577 TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole, 1578 TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole, 1579 ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole, 1580 ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole, 1581 ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole 1582 FROM 1583 ChannelMembers 1584 INNER JOIN 1585 Channels ON ChannelMembers.ChannelId = Channels.Id 1586 LEFT JOIN 1587 Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id 1588 LEFT JOIN 1589 Teams ON Channels.TeamId = Teams.Id 1590 LEFT JOIN 1591 Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id 1592 ` 1593 1594 func (s SqlChannelStore) SaveMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, *model.AppError) { 1595 for _, member := range members { 1596 defer s.InvalidateAllChannelMembersForUser(member.UserId) 1597 } 1598 1599 transaction, err := s.GetMaster().Begin() 1600 if err != nil { 1601 return nil, model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1602 } 1603 defer finalizeTransaction(transaction) 1604 1605 newMembers, err := s.saveMultipleMembersT(transaction, members) 1606 if err != nil { // TODO: this will go away once SaveMultipleMembers is migrated too. 1607 var cErr *store.ErrConflict 1608 var appErr *model.AppError 1609 switch { 1610 case errors.As(err, &cErr): 1611 switch cErr.Resource { 1612 case "ChannelMembers": 1613 return nil, model.NewAppError("CreateChannel", "store.sql_channel.save_member.exists.app_error", nil, cErr.Error(), http.StatusBadRequest) 1614 } 1615 case errors.As(err, &appErr): // in case we haven't converted to plain error. 1616 return nil, appErr 1617 default: // last fallback in case it doesn't map to an existing app error. 1618 // TODO: This error key would go away once this store method is migrated to return plain errors 1619 return nil, model.NewAppError("CreateDirectChannel", "app.channel.create_direct_channel.internal_error", nil, err.Error(), http.StatusInternalServerError) 1620 } 1621 } 1622 1623 if err := transaction.Commit(); err != nil { 1624 return nil, model.NewAppError("SqlChannelStore.SaveMember", "store.sql_channel.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1625 } 1626 1627 return newMembers, nil 1628 } 1629 1630 func (s SqlChannelStore) SaveMember(member *model.ChannelMember) (*model.ChannelMember, *model.AppError) { 1631 newMembers, appErr := s.SaveMultipleMembers([]*model.ChannelMember{member}) 1632 if appErr != nil { 1633 return nil, appErr 1634 } 1635 return newMembers[0], nil 1636 } 1637 1638 func (s SqlChannelStore) saveMultipleMembersT(transaction *gorp.Transaction, members []*model.ChannelMember) ([]*model.ChannelMember, error) { 1639 newChannelMembers := map[string]int{} 1640 users := map[string]bool{} 1641 for _, member := range members { 1642 if val, ok := newChannelMembers[member.ChannelId]; val < 1 || !ok { 1643 newChannelMembers[member.ChannelId] = 1 1644 } else { 1645 newChannelMembers[member.ChannelId]++ 1646 } 1647 users[member.UserId] = true 1648 1649 member.PreSave() 1650 if err := member.IsValid(); err != nil { // TODO: this needs to return plain error in v6. 1651 return nil, err 1652 } 1653 } 1654 1655 channels := []string{} 1656 for channel := range newChannelMembers { 1657 channels = append(channels, channel) 1658 } 1659 1660 defaultChannelRolesByChannel := map[string]struct { 1661 Id string 1662 Guest sql.NullString 1663 User sql.NullString 1664 Admin sql.NullString 1665 }{} 1666 1667 channelRolesQuery := s.getQueryBuilder(). 1668 Select( 1669 "Channels.Id as Id", 1670 "ChannelScheme.DefaultChannelGuestRole as Guest", 1671 "ChannelScheme.DefaultChannelUserRole as User", 1672 "ChannelScheme.DefaultChannelAdminRole as Admin", 1673 ). 1674 From("Channels"). 1675 LeftJoin("Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id"). 1676 Where(sq.Eq{"Channels.Id": channels}) 1677 1678 channelRolesSql, channelRolesArgs, err := channelRolesQuery.ToSql() 1679 if err != nil { 1680 return nil, errors.Wrap(err, "channel_roles_tosql") 1681 } 1682 1683 var defaultChannelsRoles []struct { 1684 Id string 1685 Guest sql.NullString 1686 User sql.NullString 1687 Admin sql.NullString 1688 } 1689 _, err = s.GetMaster().Select(&defaultChannelsRoles, channelRolesSql, channelRolesArgs...) 1690 if err != nil { 1691 return nil, errors.Wrap(err, "default_channel_roles_select") 1692 } 1693 1694 for _, defaultRoles := range defaultChannelsRoles { 1695 defaultChannelRolesByChannel[defaultRoles.Id] = defaultRoles 1696 } 1697 1698 defaultTeamRolesByChannel := map[string]struct { 1699 Id string 1700 Guest sql.NullString 1701 User sql.NullString 1702 Admin sql.NullString 1703 }{} 1704 1705 teamRolesQuery := s.getQueryBuilder(). 1706 Select( 1707 "Channels.Id as Id", 1708 "TeamScheme.DefaultChannelGuestRole as Guest", 1709 "TeamScheme.DefaultChannelUserRole as User", 1710 "TeamScheme.DefaultChannelAdminRole as Admin", 1711 ). 1712 From("Channels"). 1713 LeftJoin("Teams ON Teams.Id = Channels.TeamId"). 1714 LeftJoin("Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id"). 1715 Where(sq.Eq{"Channels.Id": channels}) 1716 1717 teamRolesSql, teamRolesArgs, err := teamRolesQuery.ToSql() 1718 if err != nil { 1719 return nil, errors.Wrap(err, "team_roles_tosql") 1720 } 1721 1722 var defaultTeamsRoles []struct { 1723 Id string 1724 Guest sql.NullString 1725 User sql.NullString 1726 Admin sql.NullString 1727 } 1728 _, err = s.GetMaster().Select(&defaultTeamsRoles, teamRolesSql, teamRolesArgs...) 1729 if err != nil { 1730 return nil, errors.Wrap(err, "default_team_roles_select") 1731 } 1732 1733 for _, defaultRoles := range defaultTeamsRoles { 1734 defaultTeamRolesByChannel[defaultRoles.Id] = defaultRoles 1735 } 1736 1737 query := s.getQueryBuilder().Insert("ChannelMembers").Columns(channelMemberSliceColumns()...) 1738 for _, member := range members { 1739 query = query.Values(channelMemberToSlice(member)...) 1740 } 1741 1742 sql, args, err := query.ToSql() 1743 if err != nil { 1744 return nil, errors.Wrap(err, "channel_members_tosql") 1745 } 1746 1747 if _, err := s.GetMaster().Exec(sql, args...); err != nil { 1748 if IsUniqueConstraintError(err, []string{"ChannelId", "channelmembers_pkey", "PRIMARY"}) { 1749 return nil, store.NewErrConflict("ChannelMembers", err, "") 1750 } 1751 return nil, errors.Wrap(err, "channel_members_save") 1752 } 1753 1754 newMembers := []*model.ChannelMember{} 1755 for _, member := range members { 1756 defaultTeamGuestRole := defaultTeamRolesByChannel[member.ChannelId].Guest.String 1757 defaultTeamUserRole := defaultTeamRolesByChannel[member.ChannelId].User.String 1758 defaultTeamAdminRole := defaultTeamRolesByChannel[member.ChannelId].Admin.String 1759 defaultChannelGuestRole := defaultChannelRolesByChannel[member.ChannelId].Guest.String 1760 defaultChannelUserRole := defaultChannelRolesByChannel[member.ChannelId].User.String 1761 defaultChannelAdminRole := defaultChannelRolesByChannel[member.ChannelId].Admin.String 1762 rolesResult := getChannelRoles( 1763 member.SchemeGuest, member.SchemeUser, member.SchemeAdmin, 1764 defaultTeamGuestRole, defaultTeamUserRole, defaultTeamAdminRole, 1765 defaultChannelGuestRole, defaultChannelUserRole, defaultChannelAdminRole, 1766 strings.Fields(member.ExplicitRoles), 1767 ) 1768 newMember := *member 1769 newMember.SchemeGuest = rolesResult.schemeGuest 1770 newMember.SchemeUser = rolesResult.schemeUser 1771 newMember.SchemeAdmin = rolesResult.schemeAdmin 1772 newMember.Roles = strings.Join(rolesResult.roles, " ") 1773 newMember.ExplicitRoles = strings.Join(rolesResult.explicitRoles, " ") 1774 newMembers = append(newMembers, &newMember) 1775 } 1776 return newMembers, nil 1777 } 1778 1779 func (s SqlChannelStore) saveMemberT(transaction *gorp.Transaction, member *model.ChannelMember) (*model.ChannelMember, error) { 1780 members, err := s.saveMultipleMembersT(transaction, []*model.ChannelMember{member}) 1781 if err != nil { 1782 return nil, err 1783 } 1784 return members[0], nil 1785 } 1786 1787 func (s SqlChannelStore) UpdateMultipleMembers(members []*model.ChannelMember) ([]*model.ChannelMember, *model.AppError) { 1788 for _, member := range members { 1789 member.PreUpdate() 1790 1791 if err := member.IsValid(); err != nil { 1792 return nil, err 1793 } 1794 } 1795 1796 var transaction *gorp.Transaction 1797 var err error 1798 1799 if transaction, err = s.GetMaster().Begin(); err != nil { 1800 return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1801 } 1802 defer finalizeTransaction(transaction) 1803 1804 updatedMembers := []*model.ChannelMember{} 1805 for _, member := range members { 1806 if _, err := transaction.Update(NewChannelMemberFromModel(member)); err != nil { 1807 return nil, model.NewAppError("SqlChannelStore.UpdateMember", "store.sql_channel.update_member.app_error", nil, "channel_id="+member.ChannelId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) 1808 } 1809 1810 // TODO: Get this out of the transaction when is possible 1811 var dbMember channelMemberWithSchemeRoles 1812 if err := transaction.SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil { 1813 if err == sql.ErrNoRows { 1814 return nil, model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound) 1815 } 1816 return nil, model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+member.ChannelId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError) 1817 } 1818 updatedMembers = append(updatedMembers, dbMember.ToModel()) 1819 } 1820 1821 if err := transaction.Commit(); err != nil { 1822 return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1823 } 1824 return updatedMembers, nil 1825 } 1826 1827 func (s SqlChannelStore) UpdateMember(member *model.ChannelMember) (*model.ChannelMember, *model.AppError) { 1828 updatedMembers, err := s.UpdateMultipleMembers([]*model.ChannelMember{member}) 1829 if err != nil { 1830 return nil, err 1831 } 1832 return updatedMembers[0], nil 1833 } 1834 1835 func (s SqlChannelStore) GetMembers(channelId string, offset, limit int) (*model.ChannelMembers, *model.AppError) { 1836 var dbMembers channelMemberWithSchemeRolesList 1837 _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelId = :ChannelId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Limit": limit, "Offset": offset}) 1838 if err != nil { 1839 return nil, model.NewAppError("SqlChannelStore.GetMembers", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+","+err.Error(), http.StatusInternalServerError) 1840 } 1841 1842 return dbMembers.ToModel(), nil 1843 } 1844 1845 func (s SqlChannelStore) GetChannelMembersTimezones(channelId string) ([]model.StringMap, *model.AppError) { 1846 var dbMembersTimezone []model.StringMap 1847 _, err := s.GetReplica().Select(&dbMembersTimezone, ` 1848 SELECT 1849 Users.Timezone 1850 FROM 1851 ChannelMembers 1852 LEFT JOIN 1853 Users ON ChannelMembers.UserId = Id 1854 WHERE ChannelId = :ChannelId 1855 `, map[string]interface{}{"ChannelId": channelId}) 1856 1857 if err != nil { 1858 return nil, model.NewAppError("SqlChannelStore.GetChannelMembersTimezones", "store.sql_channel.get_members.app_error", nil, "channel_id="+channelId+","+err.Error(), http.StatusInternalServerError) 1859 } 1860 1861 return dbMembersTimezone, nil 1862 } 1863 1864 func (s SqlChannelStore) GetMember(channelId string, userId string) (*model.ChannelMember, *model.AppError) { 1865 var dbMember channelMemberWithSchemeRoles 1866 1867 if err := s.GetReplica().SelectOne(&dbMember, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil { 1868 if err == sql.ErrNoRows { 1869 return nil, model.NewAppError("SqlChannelStore.GetMember", store.MISSING_CHANNEL_MEMBER_ERROR, nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusNotFound) 1870 } 1871 return nil, model.NewAppError("SqlChannelStore.GetMember", "store.sql_channel.get_member.app_error", nil, "channel_id="+channelId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError) 1872 } 1873 1874 return dbMember.ToModel(), nil 1875 } 1876 1877 func (s SqlChannelStore) InvalidateAllChannelMembersForUser(userId string) { 1878 allChannelMembersForUserCache.Remove(userId) 1879 allChannelMembersForUserCache.Remove(userId + "_deleted") 1880 if s.metrics != nil { 1881 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members for User - Remove by UserId") 1882 } 1883 } 1884 1885 func (s SqlChannelStore) IsUserInChannelUseCache(userId string, channelId string) bool { 1886 var ids map[string]string 1887 if err := allChannelMembersForUserCache.Get(userId, &ids); err == nil { 1888 if s.metrics != nil { 1889 s.metrics.IncrementMemCacheHitCounter("All Channel Members for User") 1890 } 1891 if _, ok := ids[channelId]; ok { 1892 return true 1893 } 1894 return false 1895 } 1896 1897 if s.metrics != nil { 1898 s.metrics.IncrementMemCacheMissCounter("All Channel Members for User") 1899 } 1900 1901 ids, err := s.GetAllChannelMembersForUser(userId, true, false) 1902 if err != nil { 1903 mlog.Error("Error getting all channel members for user", mlog.Err(err)) 1904 return false 1905 } 1906 1907 if _, ok := ids[channelId]; ok { 1908 return true 1909 } 1910 1911 return false 1912 } 1913 1914 func (s SqlChannelStore) GetMemberForPost(postId string, userId string) (*model.ChannelMember, *model.AppError) { 1915 var dbMember channelMemberWithSchemeRoles 1916 query := ` 1917 SELECT 1918 ChannelMembers.*, 1919 TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole, 1920 TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole, 1921 TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole, 1922 ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole, 1923 ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole, 1924 ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole 1925 FROM 1926 ChannelMembers 1927 INNER JOIN 1928 Posts ON ChannelMembers.ChannelId = Posts.ChannelId 1929 INNER JOIN 1930 Channels ON ChannelMembers.ChannelId = Channels.Id 1931 LEFT JOIN 1932 Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id 1933 LEFT JOIN 1934 Teams ON Channels.TeamId = Teams.Id 1935 LEFT JOIN 1936 Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id 1937 WHERE 1938 ChannelMembers.UserId = :UserId 1939 AND 1940 Posts.Id = :PostId` 1941 if err := s.GetReplica().SelectOne(&dbMember, query, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil { 1942 return nil, model.NewAppError("SqlChannelStore.GetMemberForPost", "store.sql_channel.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) 1943 } 1944 return dbMember.ToModel(), nil 1945 } 1946 1947 func (s SqlChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) (map[string]string, *model.AppError) { 1948 cache_key := userId 1949 if includeDeleted { 1950 cache_key += "_deleted" 1951 } 1952 if allowFromCache { 1953 var ids map[string]string 1954 if err := allChannelMembersForUserCache.Get(cache_key, &ids); err == nil { 1955 if s.metrics != nil { 1956 s.metrics.IncrementMemCacheHitCounter("All Channel Members for User") 1957 } 1958 return ids, nil 1959 } 1960 } 1961 1962 if s.metrics != nil { 1963 s.metrics.IncrementMemCacheMissCounter("All Channel Members for User") 1964 } 1965 1966 failure := func(err error) *model.AppError { 1967 // TODO: This error key would go away once this store method is migrated to return plain errors 1968 return model.NewAppError( 1969 "SqlChannelStore.GetAllChannelMembersForUser", 1970 "app.channel.get_channels.get.app_error", 1971 nil, 1972 "userId="+userId+", err="+err.Error(), 1973 http.StatusInternalServerError, 1974 ) 1975 } 1976 1977 query := s.getQueryBuilder(). 1978 Select(` 1979 ChannelMembers.ChannelId, ChannelMembers.Roles, ChannelMembers.SchemeGuest, 1980 ChannelMembers.SchemeUser, ChannelMembers.SchemeAdmin, 1981 TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole, 1982 TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole, 1983 TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole, 1984 ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole, 1985 ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole, 1986 ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole 1987 `). 1988 From("ChannelMembers"). 1989 Join("Channels ON ChannelMembers.ChannelId = Channels.Id"). 1990 LeftJoin("Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id"). 1991 LeftJoin("Teams ON Channels.TeamId = Teams.Id"). 1992 LeftJoin("Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id"). 1993 Where(sq.Eq{"ChannelMembers.UserId": userId}) 1994 if !includeDeleted { 1995 query = query.Where(sq.Eq{"Channels.DeleteAt": 0}) 1996 } 1997 queryString, args, err := query.ToSql() 1998 if err != nil { 1999 return nil, failure(err) 2000 } 2001 2002 rows, err := s.GetReplica().Db.Query(queryString, args...) 2003 if err != nil { 2004 return nil, failure(err) 2005 } 2006 2007 var data allChannelMembers 2008 defer rows.Close() 2009 for rows.Next() { 2010 var cm allChannelMember 2011 err = rows.Scan( 2012 &cm.ChannelId, &cm.Roles, &cm.SchemeGuest, &cm.SchemeUser, 2013 &cm.SchemeAdmin, &cm.TeamSchemeDefaultGuestRole, &cm.TeamSchemeDefaultUserRole, 2014 &cm.TeamSchemeDefaultAdminRole, &cm.ChannelSchemeDefaultGuestRole, 2015 &cm.ChannelSchemeDefaultUserRole, &cm.ChannelSchemeDefaultAdminRole, 2016 ) 2017 if err != nil { 2018 return nil, failure(err) 2019 } 2020 data = append(data, cm) 2021 } 2022 if err = rows.Err(); err != nil { 2023 return nil, failure(err) 2024 } 2025 ids := data.ToMapStringString() 2026 2027 if allowFromCache { 2028 allChannelMembersForUserCache.SetWithExpiry(cache_key, ids, ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_DURATION) 2029 } 2030 return ids, nil 2031 } 2032 2033 func (s SqlChannelStore) InvalidateCacheForChannelMembersNotifyProps(channelId string) { 2034 allChannelMembersNotifyPropsForChannelCache.Remove(channelId) 2035 if s.metrics != nil { 2036 s.metrics.IncrementMemCacheInvalidationCounter("All Channel Members Notify Props for Channel - Remove by ChannelId") 2037 } 2038 } 2039 2040 type allChannelMemberNotifyProps struct { 2041 UserId string 2042 NotifyProps model.StringMap 2043 } 2044 2045 func (s SqlChannelStore) GetAllChannelMembersNotifyPropsForChannel(channelId string, allowFromCache bool) (map[string]model.StringMap, *model.AppError) { 2046 if allowFromCache { 2047 var cacheItem map[string]model.StringMap 2048 if err := allChannelMembersNotifyPropsForChannelCache.Get(channelId, &cacheItem); err == nil { 2049 if s.metrics != nil { 2050 s.metrics.IncrementMemCacheHitCounter("All Channel Members Notify Props for Channel") 2051 } 2052 return cacheItem, nil 2053 } 2054 } 2055 2056 if s.metrics != nil { 2057 s.metrics.IncrementMemCacheMissCounter("All Channel Members Notify Props for Channel") 2058 } 2059 2060 var data []allChannelMemberNotifyProps 2061 _, err := s.GetReplica().Select(&data, ` 2062 SELECT UserId, NotifyProps 2063 FROM ChannelMembers 2064 WHERE ChannelId = :ChannelId`, map[string]interface{}{"ChannelId": channelId}) 2065 2066 if err != nil { 2067 return nil, model.NewAppError("SqlChannelStore.GetAllChannelMembersPropsForChannel", "store.sql_channel.get_members.app_error", nil, "channelId="+channelId+", err="+err.Error(), http.StatusInternalServerError) 2068 } 2069 2070 props := make(map[string]model.StringMap) 2071 for i := range data { 2072 props[data[i].UserId] = data[i].NotifyProps 2073 } 2074 2075 allChannelMembersNotifyPropsForChannelCache.SetWithExpiry(channelId, props, ALL_CHANNEL_MEMBERS_NOTIFY_PROPS_FOR_CHANNEL_CACHE_DURATION) 2076 2077 return props, nil 2078 } 2079 2080 func (s SqlChannelStore) InvalidateMemberCount(channelId string) { 2081 } 2082 2083 func (s SqlChannelStore) GetMemberCountFromCache(channelId string) int64 { 2084 count, _ := s.GetMemberCount(channelId, true) 2085 return count 2086 } 2087 2088 func (s SqlChannelStore) GetMemberCount(channelId string, allowFromCache bool) (int64, *model.AppError) { 2089 count, err := s.GetReplica().SelectInt(` 2090 SELECT 2091 count(*) 2092 FROM 2093 ChannelMembers, 2094 Users 2095 WHERE 2096 ChannelMembers.UserId = Users.Id 2097 AND ChannelMembers.ChannelId = :ChannelId 2098 AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) 2099 if err != nil { 2100 return 0, model.NewAppError("SqlChannelStore.GetMemberCount", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2101 } 2102 2103 return count, nil 2104 } 2105 2106 // GetMemberCountsByGroup returns a slice of ChannelMemberCountByGroup for a given channel 2107 // which contains the number of channel members for each group and optionally the number of unique timezones present for each group in the channel 2108 func (s SqlChannelStore) GetMemberCountsByGroup(channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError) { 2109 selectStr := "GroupMembers.GroupId, COUNT(ChannelMembers.UserId) AS ChannelMemberCount" 2110 2111 if includeTimezones { 2112 // Length of default timezone (len {"automaticTimezone":"","manualTimezone":"","useAutomaticTimezone":"true"}) 2113 defaultTimezoneLength := `74` 2114 2115 // Beginning and end of the value for the automatic and manual timezones respectively 2116 autoTimezone := `LOCATE(':', Users.Timezone) + 2` 2117 autoTimezoneEnd := `LOCATE(',', Users.Timezone) - LOCATE(':', Users.Timezone) - 3` 2118 manualTimezone := `LOCATE(',', Users.Timezone) + 19` 2119 manualTimezoneEnd := `LOCATE('useAutomaticTimezone', Users.Timezone) - 22 - LOCATE(',', Users.Timezone)` 2120 2121 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 2122 autoTimezone = `POSITION(':' IN Users.Timezone) + 2` 2123 autoTimezoneEnd = `POSITION(',' IN Users.Timezone) - POSITION(':' IN Users.Timezone) - 3` 2124 manualTimezone = `POSITION(',' IN Users.Timezone) + 19` 2125 manualTimezoneEnd = `POSITION('useAutomaticTimezone' IN Users.Timezone) - 22 - POSITION(',' IN Users.Timezone)` 2126 } 2127 2128 selectStr = ` 2129 GroupMembers.GroupId, 2130 COUNT(ChannelMembers.UserId) AS ChannelMemberCount, 2131 COUNT(DISTINCT 2132 ( 2133 CASE WHEN Timezone like '%"useAutomaticTimezone":"true"}' AND LENGTH(Timezone) > ` + defaultTimezoneLength + ` 2134 THEN 2135 SUBSTRING( 2136 Timezone 2137 FROM ` + autoTimezone + ` 2138 FOR ` + autoTimezoneEnd + ` 2139 ) 2140 WHEN Timezone like '%"useAutomaticTimezone":"false"}' AND LENGTH(Timezone) > ` + defaultTimezoneLength + ` 2141 THEN 2142 SUBSTRING( 2143 Timezone 2144 FROM ` + manualTimezone + ` 2145 FOR ` + manualTimezoneEnd + ` 2146 ) 2147 END 2148 ) 2149 ) AS ChannelMemberTimezonesCount 2150 ` 2151 } 2152 2153 query := s.getQueryBuilder(). 2154 Select(selectStr). 2155 From("ChannelMembers"). 2156 Join("GroupMembers ON GroupMembers.UserId = ChannelMembers.UserId") 2157 2158 if includeTimezones { 2159 query = query.Join("Users ON Users.Id = GroupMembers.UserId") 2160 } 2161 2162 query = query.Where(sq.Eq{"ChannelMembers.ChannelId": channelID}).GroupBy("GroupMembers.GroupId") 2163 2164 queryString, args, err := query.ToSql() 2165 if err != nil { 2166 return nil, model.NewAppError("SqlChannelStore.GetMemberCountsByGroup", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError) 2167 } 2168 var data []*model.ChannelMemberCountByGroup 2169 if _, err = s.GetReplica().Select(&data, queryString, args...); err != nil { 2170 return nil, model.NewAppError("SqlChannelStore.GetMemberCountsByGroup", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelID+", "+err.Error(), http.StatusInternalServerError) 2171 } 2172 2173 return data, nil 2174 } 2175 2176 func (s SqlChannelStore) InvalidatePinnedPostCount(channelId string) { 2177 } 2178 2179 func (s SqlChannelStore) GetPinnedPostCount(channelId string, allowFromCache bool) (int64, *model.AppError) { 2180 count, err := s.GetReplica().SelectInt(` 2181 SELECT count(*) 2182 FROM Posts 2183 WHERE 2184 IsPinned = true 2185 AND ChannelId = :ChannelId 2186 AND DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) 2187 2188 if err != nil { 2189 return 0, model.NewAppError("SqlChannelStore.GetPinnedPostCount", "store.sql_channel.get_pinnedpost_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2190 } 2191 2192 return count, nil 2193 } 2194 2195 func (s SqlChannelStore) InvalidateGuestCount(channelId string) { 2196 } 2197 2198 func (s SqlChannelStore) GetGuestCount(channelId string, allowFromCache bool) (int64, *model.AppError) { 2199 count, err := s.GetReplica().SelectInt(` 2200 SELECT 2201 count(*) 2202 FROM 2203 ChannelMembers, 2204 Users 2205 WHERE 2206 ChannelMembers.UserId = Users.Id 2207 AND ChannelMembers.ChannelId = :ChannelId 2208 AND ChannelMembers.SchemeGuest = TRUE 2209 AND Users.DeleteAt = 0`, map[string]interface{}{"ChannelId": channelId}) 2210 if err != nil { 2211 return 0, model.NewAppError("SqlChannelStore.GetGuestCount", "store.sql_channel.get_member_count.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2212 } 2213 return count, nil 2214 } 2215 2216 func (s SqlChannelStore) RemoveMembers(channelId string, userIds []string) *model.AppError { 2217 query := s.getQueryBuilder(). 2218 Delete("ChannelMembers"). 2219 Where(sq.Eq{"ChannelId": channelId}). 2220 Where(sq.Eq{"UserId": userIds}) 2221 sql, args, err := query.ToSql() 2222 if err != nil { 2223 return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2224 } 2225 _, err = s.GetMaster().Exec(sql, args...) 2226 if err != nil { 2227 return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2228 } 2229 2230 // cleanup sidebarchannels table if the user is no longer a member of that channel 2231 sql, args, err = s.getQueryBuilder(). 2232 Delete("SidebarChannels"). 2233 Where(sq.And{ 2234 sq.Eq{"ChannelId": channelId}, 2235 sq.Eq{"UserId": userIds}, 2236 }).ToSql() 2237 if err != nil { 2238 return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2239 } 2240 _, err = s.GetMaster().Exec(sql, args...) 2241 if err != nil { 2242 return model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_channel.remove_member.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2243 } 2244 return nil 2245 } 2246 2247 func (s SqlChannelStore) RemoveMember(channelId string, userId string) *model.AppError { 2248 return s.RemoveMembers(channelId, []string{userId}) 2249 } 2250 2251 func (s SqlChannelStore) RemoveAllDeactivatedMembers(channelId string) *model.AppError { 2252 query := ` 2253 DELETE 2254 FROM 2255 ChannelMembers 2256 WHERE 2257 UserId IN ( 2258 SELECT 2259 Id 2260 FROM 2261 Users 2262 WHERE 2263 Users.DeleteAt != 0 2264 ) 2265 AND 2266 ChannelMembers.ChannelId = :ChannelId 2267 ` 2268 2269 _, err := s.GetMaster().Exec(query, map[string]interface{}{"ChannelId": channelId}) 2270 if err != nil { 2271 return model.NewAppError("SqlChannelStore.RemoveAllDeactivatedMembers", "store.sql_channel.remove_all_deactivated_members.app_error", nil, "channel_id="+channelId+", "+err.Error(), http.StatusInternalServerError) 2272 } 2273 return nil 2274 } 2275 2276 func (s SqlChannelStore) PermanentDeleteMembersByUser(userId string) *model.AppError { 2277 if _, err := s.GetMaster().Exec("DELETE FROM ChannelMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { 2278 return model.NewAppError("SqlChannelStore.ChannelPermanentDeleteMembersByUser", "store.sql_channel.permanent_delete_members_by_user.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 2279 } 2280 return nil 2281 } 2282 2283 func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string) (map[string]int64, *model.AppError) { 2284 keys, props := MapStringsToQueryParams(channelIds, "Channel") 2285 props["UserId"] = userId 2286 2287 var lastPostAtTimes []struct { 2288 Id string 2289 LastPostAt int64 2290 TotalMsgCount int64 2291 } 2292 2293 query := `SELECT Id, LastPostAt, TotalMsgCount FROM Channels WHERE Id IN ` + keys 2294 // TODO: use a CTE for mysql too when version 8 becomes the minimum supported version. 2295 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 2296 query = `WITH c AS ( ` + query + `), 2297 updated AS ( 2298 UPDATE 2299 ChannelMembers cm 2300 SET 2301 MentionCount = 0, 2302 MsgCount = greatest(cm.MsgCount, c.TotalMsgCount), 2303 LastViewedAt = greatest(cm.LastViewedAt, c.LastPostAt), 2304 LastUpdateAt = greatest(cm.LastViewedAt, c.LastPostAt) 2305 FROM c 2306 WHERE cm.UserId = :UserId 2307 AND c.Id=cm.ChannelId 2308 ) 2309 SELECT Id, LastPostAt FROM c` 2310 } 2311 2312 _, err := s.GetMaster().Select(&lastPostAtTimes, query, props) 2313 if err != nil || len(lastPostAtTimes) == 0 { 2314 status := http.StatusInternalServerError 2315 var extra string 2316 if err == nil { 2317 status = http.StatusBadRequest 2318 extra = "No channels found" 2319 } else { 2320 extra = err.Error() 2321 } 2322 2323 return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAt", 2324 "store.sql_channel.update_last_viewed_at.app_error", 2325 nil, 2326 "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+extra, 2327 status) 2328 } 2329 2330 times := map[string]int64{} 2331 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 2332 for _, t := range lastPostAtTimes { 2333 times[t.Id] = t.LastPostAt 2334 } 2335 return times, nil 2336 } 2337 2338 msgCountQuery := "" 2339 lastViewedQuery := "" 2340 for index, t := range lastPostAtTimes { 2341 times[t.Id] = t.LastPostAt 2342 2343 props["msgCount"+strconv.Itoa(index)] = t.TotalMsgCount 2344 msgCountQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(MsgCount, :msgCount%d) ", index, index) 2345 2346 props["lastViewed"+strconv.Itoa(index)] = t.LastPostAt 2347 lastViewedQuery += fmt.Sprintf("WHEN :channelId%d THEN GREATEST(LastViewedAt, :lastViewed%d) ", index, index) 2348 2349 props["channelId"+strconv.Itoa(index)] = t.Id 2350 } 2351 2352 updateQuery := `UPDATE 2353 ChannelMembers 2354 SET 2355 MentionCount = 0, 2356 MsgCount = CASE ChannelId ` + msgCountQuery + ` END, 2357 LastViewedAt = CASE ChannelId ` + lastViewedQuery + ` END, 2358 LastUpdateAt = LastViewedAt 2359 WHERE 2360 UserId = :UserId 2361 AND ChannelId IN ` + keys 2362 2363 if _, err := s.GetMaster().Exec(updateQuery, props); err != nil { 2364 return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAt", "store.sql_channel.update_last_viewed_at.app_error", nil, "channel_ids="+strings.Join(channelIds, ",")+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 2365 } 2366 2367 return times, nil 2368 } 2369 2370 // CountPostsAfter returns the number of posts in the given channel created after but not including the given timestamp. If given a non-empty user ID, only counts posts made by that user. 2371 func (s SqlChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, *model.AppError) { 2372 joinLeavePostTypes, params := MapStringsToQueryParams([]string{ 2373 // These types correspond to the ones checked by Post.IsJoinLeaveMessage 2374 model.POST_JOIN_LEAVE, 2375 model.POST_ADD_REMOVE, 2376 model.POST_JOIN_CHANNEL, 2377 model.POST_LEAVE_CHANNEL, 2378 model.POST_JOIN_TEAM, 2379 model.POST_LEAVE_TEAM, 2380 model.POST_ADD_TO_CHANNEL, 2381 model.POST_REMOVE_FROM_CHANNEL, 2382 model.POST_ADD_TO_TEAM, 2383 model.POST_REMOVE_FROM_TEAM, 2384 }, "PostType") 2385 2386 query := ` 2387 SELECT count(*) 2388 FROM Posts 2389 WHERE 2390 ChannelId = :ChannelId 2391 AND CreateAt > :CreateAt 2392 AND Type NOT IN ` + joinLeavePostTypes + ` 2393 AND DeleteAt = 0 2394 ` 2395 2396 params["ChannelId"] = channelId 2397 params["CreateAt"] = timestamp 2398 2399 if userId != "" { 2400 query += " AND UserId = :UserId" 2401 params["UserId"] = userId 2402 } 2403 2404 unread, err := s.GetReplica().SelectInt(query, params) 2405 if err != nil { 2406 return 0, model.NewAppError("SqlChannelStore.CountPostsAfter", "store.sql_channel.count_posts_since.app_error", nil, fmt.Sprintf("channel_id=%s, timestamp=%d, err=%s", channelId, timestamp, err), http.StatusInternalServerError) 2407 } 2408 return int(unread), nil 2409 } 2410 2411 // UpdateLastViewedAtPost updates a ChannelMember as if the user last read the channel at the time of the given post. 2412 // If the provided mentionCount is -1, the given post and all posts after it are considered to be mentions. Returns 2413 // an updated model.ChannelUnreadAt that can be returned to the client. 2414 func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int) (*model.ChannelUnreadAt, *model.AppError) { 2415 unreadDate := unreadPost.CreateAt - 1 2416 2417 unread, appErr := s.CountPostsAfter(unreadPost.ChannelId, unreadDate, "") 2418 if appErr != nil { 2419 return nil, appErr 2420 } 2421 2422 params := map[string]interface{}{ 2423 "mentions": mentionCount, 2424 "unreadCount": unread, 2425 "lastViewedAt": unreadDate, 2426 "userId": userID, 2427 "channelId": unreadPost.ChannelId, 2428 "updatedAt": model.GetMillis(), 2429 } 2430 2431 // msg count uses the value from channels to prevent counting on older channels where no. of messages can be high. 2432 // we only count the unread which will be a lot less in 99% cases 2433 setUnreadQuery := ` 2434 UPDATE 2435 ChannelMembers 2436 SET 2437 MentionCount = :mentions, 2438 MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelId) - :unreadCount, 2439 LastViewedAt = :lastViewedAt, 2440 LastUpdateAt = :updatedAt 2441 WHERE 2442 UserId = :userId 2443 AND ChannelId = :channelId 2444 ` 2445 _, err := s.GetMaster().Exec(setUnreadQuery, params) 2446 if err != nil { 2447 return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAtPost", "store.sql_channel.update_last_viewed_at_post.app_error", params, "Error setting channel "+unreadPost.ChannelId+" as unread: "+err.Error(), http.StatusInternalServerError) 2448 } 2449 2450 chanUnreadQuery := ` 2451 SELECT 2452 c.TeamId TeamId, 2453 cm.UserId UserId, 2454 cm.ChannelId ChannelId, 2455 cm.MsgCount MsgCount, 2456 cm.MentionCount MentionCount, 2457 cm.LastViewedAt LastViewedAt, 2458 cm.NotifyProps NotifyProps 2459 FROM 2460 ChannelMembers cm 2461 LEFT JOIN Channels c ON c.Id=cm.ChannelId 2462 WHERE 2463 cm.UserId = :userId 2464 AND cm.channelId = :channelId 2465 AND c.DeleteAt = 0 2466 ` 2467 result := &model.ChannelUnreadAt{} 2468 if err = s.GetMaster().SelectOne(result, chanUnreadQuery, params); err != nil { 2469 return nil, model.NewAppError("SqlChannelStore.UpdateLastViewedAtPost", "store.sql_channel.update_last_viewed_at_post.app_error", params, "Error retrieving unread status from channel "+unreadPost.ChannelId+", error was: "+err.Error(), http.StatusInternalServerError) 2470 } 2471 return result, nil 2472 } 2473 2474 func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string) *model.AppError { 2475 _, err := s.GetMaster().Exec( 2476 `UPDATE 2477 ChannelMembers 2478 SET 2479 MentionCount = MentionCount + 1, 2480 LastUpdateAt = :LastUpdateAt 2481 WHERE 2482 UserId = :UserId 2483 AND ChannelId = :ChannelId`, 2484 map[string]interface{}{"ChannelId": channelId, "UserId": userId, "LastUpdateAt": model.GetMillis()}) 2485 if err != nil { 2486 return model.NewAppError("SqlChannelStore.IncrementMentionCount", "store.sql_channel.increment_mention_count.app_error", nil, "channel_id="+channelId+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 2487 } 2488 2489 return nil 2490 } 2491 2492 func (s SqlChannelStore) GetAll(teamId string) ([]*model.Channel, *model.AppError) { 2493 var data []*model.Channel 2494 _, err := s.GetReplica().Select(&data, "SELECT * FROM Channels WHERE TeamId = :TeamId AND Type != 'D' ORDER BY Name", map[string]interface{}{"TeamId": teamId}) 2495 2496 if err != nil { 2497 return nil, model.NewAppError("SqlChannelStore.GetAll", "store.sql_channel.get_all.app_error", nil, "teamId="+teamId+", err="+err.Error(), http.StatusInternalServerError) 2498 } 2499 2500 return data, nil 2501 } 2502 2503 func (s SqlChannelStore) GetChannelsByIds(channelIds []string, includeDeleted bool) ([]*model.Channel, *model.AppError) { 2504 keys, params := MapStringsToQueryParams(channelIds, "Channel") 2505 query := `SELECT * FROM Channels WHERE Id IN ` + keys + ` ORDER BY Name` 2506 if !includeDeleted { 2507 query = `SELECT * FROM Channels WHERE DeleteAt=0 AND Id IN ` + keys + ` ORDER BY Name` 2508 } 2509 2510 var channels []*model.Channel 2511 _, err := s.GetReplica().Select(&channels, query, params) 2512 2513 if err != nil { 2514 mlog.Error("Query error getting channels by ids", mlog.Err(err)) 2515 return nil, model.NewAppError("SqlChannelStore.GetChannelsByIds", "store.sql_channel.get_channels_by_ids.app_error", nil, "", http.StatusInternalServerError) 2516 } 2517 return channels, nil 2518 } 2519 2520 func (s SqlChannelStore) GetForPost(postId string) (*model.Channel, *model.AppError) { 2521 channel := &model.Channel{} 2522 if err := s.GetReplica().SelectOne( 2523 channel, 2524 `SELECT 2525 Channels.* 2526 FROM 2527 Channels, 2528 Posts 2529 WHERE 2530 Channels.Id = Posts.ChannelId 2531 AND Posts.Id = :PostId`, map[string]interface{}{"PostId": postId}); err != nil { 2532 return nil, model.NewAppError("SqlChannelStore.GetForPost", "store.sql_channel.get_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) 2533 2534 } 2535 return channel, nil 2536 } 2537 2538 func (s SqlChannelStore) AnalyticsTypeCount(teamId string, channelType string) (int64, *model.AppError) { 2539 query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType" 2540 2541 if len(teamId) > 0 { 2542 query += " AND TeamId = :TeamId" 2543 } 2544 2545 value, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType}) 2546 if err != nil { 2547 return int64(0), model.NewAppError("SqlChannelStore.AnalyticsTypeCount", "store.sql_channel.analytics_type_count.app_error", nil, err.Error(), http.StatusInternalServerError) 2548 } 2549 return value, nil 2550 } 2551 2552 func (s SqlChannelStore) AnalyticsDeletedTypeCount(teamId string, channelType string) (int64, *model.AppError) { 2553 query := "SELECT COUNT(Id) AS Value FROM Channels WHERE Type = :ChannelType AND DeleteAt > 0" 2554 2555 if len(teamId) > 0 { 2556 query += " AND TeamId = :TeamId" 2557 } 2558 2559 v, err := s.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId, "ChannelType": channelType}) 2560 if err != nil { 2561 return 0, model.NewAppError("SqlChannelStore.AnalyticsDeletedTypeCount", "store.sql_channel.analytics_deleted_type_count.app_error", nil, err.Error(), http.StatusInternalServerError) 2562 } 2563 2564 return v, nil 2565 } 2566 2567 func (s SqlChannelStore) GetMembersForUser(teamId string, userId string) (*model.ChannelMembers, *model.AppError) { 2568 var dbMembers channelMemberWithSchemeRolesList 2569 _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId AND (Teams.Id = :TeamId OR Teams.Id = '' OR Teams.Id IS NULL)", map[string]interface{}{"TeamId": teamId, "UserId": userId}) 2570 if err != nil { 2571 return nil, model.NewAppError("SqlChannelStore.GetMembersForUser", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 2572 } 2573 2574 return dbMembers.ToModel(), nil 2575 } 2576 2577 func (s SqlChannelStore) GetMembersForUserWithPagination(teamId, userId string, page, perPage int) (*model.ChannelMembers, *model.AppError) { 2578 var dbMembers channelMemberWithSchemeRolesList 2579 offset := page * perPage 2580 _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.UserId = :UserId Limit :Limit Offset :Offset", map[string]interface{}{"TeamId": teamId, "UserId": userId, "Limit": perPage, "Offset": offset}) 2581 2582 if err != nil { 2583 return nil, model.NewAppError("SqlChannelStore.GetMembersForUserWithPagination", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 2584 } 2585 2586 return dbMembers.ToModel(), nil 2587 } 2588 2589 func (s SqlChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { 2590 deleteFilter := "AND c.DeleteAt = 0" 2591 if includeDeleted { 2592 deleteFilter = "" 2593 } 2594 2595 queryFormat := ` 2596 SELECT 2597 Channels.* 2598 FROM 2599 Channels 2600 JOIN 2601 PublicChannels c ON (c.Id = Channels.Id) 2602 WHERE 2603 c.TeamId = :TeamId 2604 ` + deleteFilter + ` 2605 %v 2606 LIMIT ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT) 2607 2608 var channels model.ChannelList 2609 2610 if likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose"); likeClause == "" { 2611 if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId}); err != nil { 2612 return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2613 } 2614 } else { 2615 // Using a UNION results in index_merge and fulltext queries and is much faster than the ref 2616 // query you would get using an OR of the LIKE and full-text clauses. 2617 fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose") 2618 likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause) 2619 fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause) 2620 query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery) 2621 2622 if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil { 2623 return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeam", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2624 } 2625 } 2626 2627 sort.Slice(channels, func(a, b int) bool { 2628 return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName) 2629 }) 2630 return &channels, nil 2631 } 2632 2633 func (s SqlChannelStore) AutocompleteInTeamForSearch(teamId string, userId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { 2634 deleteFilter := "AND DeleteAt = 0" 2635 if includeDeleted { 2636 deleteFilter = "" 2637 } 2638 2639 queryFormat := ` 2640 SELECT 2641 C.* 2642 FROM 2643 Channels AS C 2644 JOIN 2645 ChannelMembers AS CM ON CM.ChannelId = C.Id 2646 WHERE 2647 (C.TeamId = :TeamId OR (C.TeamId = '' AND C.Type = 'G')) 2648 AND CM.UserId = :UserId 2649 ` + deleteFilter + ` 2650 %v 2651 LIMIT 50` 2652 2653 var channels model.ChannelList 2654 2655 if likeClause, likeTerm := s.buildLIKEClause(term, "Name, DisplayName, Purpose"); likeClause == "" { 2656 if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"TeamId": teamId, "UserId": userId}); err != nil { 2657 return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2658 } 2659 } else { 2660 // Using a UNION results in index_merge and fulltext queries and is much faster than the ref 2661 // query you would get using an OR of the LIKE and full-text clauses. 2662 fulltextClause, fulltextTerm := s.buildFulltextClause(term, "Name, DisplayName, Purpose") 2663 likeQuery := fmt.Sprintf(queryFormat, "AND "+likeClause) 2664 fulltextQuery := fmt.Sprintf(queryFormat, "AND "+fulltextClause) 2665 query := fmt.Sprintf("(%v) UNION (%v) LIMIT 50", likeQuery, fulltextQuery) 2666 2667 if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"TeamId": teamId, "UserId": userId, "LikeTerm": likeTerm, "FulltextTerm": fulltextTerm}); err != nil { 2668 return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2669 } 2670 } 2671 2672 directChannels, err := s.autocompleteInTeamForSearchDirectMessages(userId, term) 2673 if err != nil { 2674 return nil, err 2675 } 2676 2677 channels = append(channels, directChannels...) 2678 2679 sort.Slice(channels, func(a, b int) bool { 2680 return strings.ToLower(channels[a].DisplayName) < strings.ToLower(channels[b].DisplayName) 2681 }) 2682 return &channels, nil 2683 } 2684 2685 func (s SqlChannelStore) autocompleteInTeamForSearchDirectMessages(userId string, term string) ([]*model.Channel, *model.AppError) { 2686 queryFormat := ` 2687 SELECT 2688 C.*, 2689 OtherUsers.Username as DisplayName 2690 FROM 2691 Channels AS C 2692 JOIN 2693 ChannelMembers AS CM ON CM.ChannelId = C.Id 2694 INNER JOIN ( 2695 SELECT 2696 ICM.ChannelId AS ChannelId, IU.Username AS Username 2697 FROM 2698 Users as IU 2699 JOIN 2700 ChannelMembers AS ICM ON ICM.UserId = IU.Id 2701 WHERE 2702 IU.Id != :UserId 2703 %v 2704 ) AS OtherUsers ON OtherUsers.ChannelId = C.Id 2705 WHERE 2706 C.Type = 'D' 2707 AND CM.UserId = :UserId 2708 LIMIT 50` 2709 2710 var channels model.ChannelList 2711 2712 if likeClause, likeTerm := s.buildLIKEClause(term, "IU.Username, IU.Nickname"); likeClause == "" { 2713 if _, err := s.GetReplica().Select(&channels, fmt.Sprintf(queryFormat, ""), map[string]interface{}{"UserId": userId}); err != nil { 2714 return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2715 } 2716 } else { 2717 query := fmt.Sprintf(queryFormat, "AND "+likeClause) 2718 2719 if _, err := s.GetReplica().Select(&channels, query, map[string]interface{}{"UserId": userId, "LikeTerm": likeTerm}); err != nil { 2720 return nil, model.NewAppError("SqlChannelStore.AutocompleteInTeamForSearch", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2721 } 2722 } 2723 2724 return channels, nil 2725 } 2726 2727 func (s SqlChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { 2728 deleteFilter := "AND c.DeleteAt = 0" 2729 if includeDeleted { 2730 deleteFilter = "" 2731 } 2732 2733 return s.performSearch(` 2734 SELECT 2735 Channels.* 2736 FROM 2737 Channels 2738 JOIN 2739 PublicChannels c ON (c.Id = Channels.Id) 2740 WHERE 2741 c.TeamId = :TeamId 2742 `+deleteFilter+` 2743 SEARCH_CLAUSE 2744 ORDER BY c.DisplayName 2745 LIMIT 100 2746 `, term, map[string]interface{}{ 2747 "TeamId": teamId, 2748 }) 2749 } 2750 2751 func (s SqlChannelStore) SearchArchivedInTeam(teamId string, term string, userId string) (*model.ChannelList, *model.AppError) { 2752 publicChannels, publicErr := s.performSearch(` 2753 SELECT 2754 Channels.* 2755 FROM 2756 Channels 2757 JOIN 2758 Channels c ON (c.Id = Channels.Id) 2759 WHERE 2760 c.TeamId = :TeamId 2761 SEARCH_CLAUSE 2762 AND c.DeleteAt != 0 2763 AND c.Type != 'P' 2764 ORDER BY c.DisplayName 2765 LIMIT 100 2766 `, term, map[string]interface{}{ 2767 "TeamId": teamId, 2768 "UserId": userId, 2769 }) 2770 2771 privateChannels, privateErr := s.performSearch(` 2772 SELECT 2773 Channels.* 2774 FROM 2775 Channels 2776 JOIN 2777 Channels c ON (c.Id = Channels.Id) 2778 WHERE 2779 c.TeamId = :TeamId 2780 SEARCH_CLAUSE 2781 AND c.DeleteAt != 0 2782 AND c.Type = 'P' 2783 AND c.Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId) 2784 ORDER BY c.DisplayName 2785 LIMIT 100 2786 `, term, map[string]interface{}{ 2787 "TeamId": teamId, 2788 "UserId": userId, 2789 }) 2790 2791 output := *publicChannels 2792 output = append(output, *privateChannels...) 2793 2794 outputErr := publicErr 2795 if privateErr != nil { 2796 outputErr = privateErr 2797 } 2798 2799 return &output, outputErr 2800 } 2801 2802 func (s SqlChannelStore) SearchForUserInTeam(userId string, teamId string, term string, includeDeleted bool) (*model.ChannelList, *model.AppError) { 2803 deleteFilter := "AND c.DeleteAt = 0" 2804 if includeDeleted { 2805 deleteFilter = "" 2806 } 2807 2808 return s.performSearch(` 2809 SELECT 2810 Channels.* 2811 FROM 2812 Channels 2813 JOIN 2814 PublicChannels c ON (c.Id = Channels.Id) 2815 JOIN 2816 ChannelMembers cm ON (c.Id = cm.ChannelId) 2817 WHERE 2818 c.TeamId = :TeamId 2819 AND 2820 cm.UserId = :UserId 2821 `+deleteFilter+` 2822 SEARCH_CLAUSE 2823 ORDER BY c.DisplayName 2824 LIMIT 100 2825 `, term, map[string]interface{}{ 2826 "TeamId": teamId, 2827 "UserId": userId, 2828 }) 2829 } 2830 2831 func (s SqlChannelStore) channelSearchQuery(term string, opts store.ChannelSearchOpts, countQuery bool) sq.SelectBuilder { 2832 var limit int 2833 if opts.PerPage != nil { 2834 limit = *opts.PerPage 2835 } else { 2836 limit = 100 2837 } 2838 2839 var selectStr string 2840 if countQuery { 2841 selectStr = "count(*)" 2842 } else { 2843 selectStr = "c.*, t.DisplayName AS TeamDisplayName, t.Name AS TeamName, t.UpdateAt as TeamUpdateAt" 2844 } 2845 2846 query := s.getQueryBuilder(). 2847 Select(selectStr). 2848 From("Channels AS c"). 2849 Join("Teams AS t ON t.Id = c.TeamId") 2850 2851 // don't bother ordering or limiting if we're just getting the count 2852 if !countQuery { 2853 query = query. 2854 OrderBy("c.DisplayName, t.DisplayName"). 2855 Limit(uint64(limit)) 2856 } 2857 if opts.Deleted { 2858 query = query.Where(sq.NotEq{"c.DeleteAt": int(0)}) 2859 } else if !opts.IncludeDeleted { 2860 query = query.Where(sq.Eq{"c.DeleteAt": int(0)}) 2861 } 2862 2863 if opts.IsPaginated() && !countQuery { 2864 query = query.Offset(uint64(*opts.Page * *opts.PerPage)) 2865 } 2866 2867 likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose") 2868 if likeTerm != "" { 2869 likeClause = strings.ReplaceAll(likeClause, ":LikeTerm", "?") 2870 fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose") 2871 fulltextClause = strings.ReplaceAll(fulltextClause, ":FulltextTerm", "?") 2872 query = query.Where(sq.Or{ 2873 sq.Expr(likeClause, likeTerm, likeTerm, likeTerm), // Keep the number of likeTerms same as the number 2874 // of columns (c.Name, c.DisplayName, c.Purpose) 2875 sq.Expr(fulltextClause, fulltextTerm), 2876 }) 2877 } 2878 2879 if len(opts.ExcludeChannelNames) > 0 { 2880 query = query.Where(sq.NotEq{"c.Name": opts.ExcludeChannelNames}) 2881 } 2882 2883 if len(opts.NotAssociatedToGroup) > 0 { 2884 query = query.Where("c.Id NOT IN (SELECT ChannelId FROM GroupChannels WHERE GroupChannels.GroupId = ? AND GroupChannels.DeleteAt = 0)", opts.NotAssociatedToGroup) 2885 } 2886 2887 if len(opts.TeamIds) > 0 { 2888 query = query.Where(sq.Eq{"c.TeamId": opts.TeamIds}) 2889 } 2890 2891 if opts.GroupConstrained { 2892 query = query.Where(sq.Eq{"c.GroupConstrained": true}) 2893 } else if opts.ExcludeGroupConstrained { 2894 query = query.Where(sq.Or{ 2895 sq.NotEq{"c.GroupConstrained": true}, 2896 sq.Eq{"c.GroupConstrained": nil}, 2897 }) 2898 } 2899 2900 if opts.Public && !opts.Private { 2901 query = query.Where(sq.Eq{"c.Type": model.CHANNEL_OPEN}) 2902 } else if opts.Private && !opts.Public { 2903 query = query.Where(sq.Eq{"c.Type": model.CHANNEL_PRIVATE}) 2904 } else { 2905 query = query.Where(sq.Or{ 2906 sq.Eq{"c.Type": model.CHANNEL_OPEN}, 2907 sq.Eq{"c.Type": model.CHANNEL_PRIVATE}, 2908 }) 2909 } 2910 2911 return query 2912 } 2913 2914 func (s SqlChannelStore) SearchAllChannels(term string, opts store.ChannelSearchOpts) (*model.ChannelListWithTeamData, int64, *model.AppError) { 2915 queryString, args, err := s.channelSearchQuery(term, opts, false).ToSql() 2916 if err != nil { 2917 return nil, 0, model.NewAppError("SqlChannelStore.SearchAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError) 2918 } 2919 var channels model.ChannelListWithTeamData 2920 if _, err = s.GetReplica().Select(&channels, queryString, args...); err != nil { 2921 return nil, 0, model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2922 } 2923 2924 var totalCount int64 2925 2926 // only query a 2nd time for the count if the results are being requested paginated. 2927 if opts.IsPaginated() { 2928 queryString, args, err = s.channelSearchQuery(term, opts, true).ToSql() 2929 if err != nil { 2930 return nil, 0, model.NewAppError("SqlChannelStore.SearchAllChannels", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError) 2931 } 2932 if totalCount, err = s.GetReplica().SelectInt(queryString, args...); err != nil { 2933 return nil, 0, model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 2934 } 2935 } else { 2936 totalCount = int64(len(channels)) 2937 } 2938 2939 return &channels, totalCount, nil 2940 } 2941 2942 func (s SqlChannelStore) SearchMore(userId string, teamId string, term string) (*model.ChannelList, *model.AppError) { 2943 return s.performSearch(` 2944 SELECT 2945 Channels.* 2946 FROM 2947 Channels 2948 JOIN 2949 PublicChannels c ON (c.Id = Channels.Id) 2950 WHERE 2951 c.TeamId = :TeamId 2952 AND c.DeleteAt = 0 2953 AND c.Id NOT IN ( 2954 SELECT 2955 c.Id 2956 FROM 2957 PublicChannels c 2958 JOIN 2959 ChannelMembers cm ON (cm.ChannelId = c.Id) 2960 WHERE 2961 c.TeamId = :TeamId 2962 AND cm.UserId = :UserId 2963 AND c.DeleteAt = 0 2964 ) 2965 SEARCH_CLAUSE 2966 ORDER BY c.DisplayName 2967 LIMIT 100 2968 `, term, map[string]interface{}{ 2969 "TeamId": teamId, 2970 "UserId": userId, 2971 }) 2972 } 2973 2974 func (s SqlChannelStore) buildLIKEClause(term string, searchColumns string) (likeClause, likeTerm string) { 2975 likeTerm = sanitizeSearchTerm(term, "*") 2976 2977 if likeTerm == "" { 2978 return 2979 } 2980 2981 // Prepare the LIKE portion of the query. 2982 var searchFields []string 2983 for _, field := range strings.Split(searchColumns, ", ") { 2984 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 2985 searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*'", field, ":LikeTerm")) 2986 } else { 2987 searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*'", field, ":LikeTerm")) 2988 } 2989 } 2990 2991 likeClause = fmt.Sprintf("(%s)", strings.Join(searchFields, " OR ")) 2992 likeTerm = wildcardSearchTerm(likeTerm) 2993 return 2994 } 2995 2996 func (s SqlChannelStore) buildFulltextClause(term string, searchColumns string) (fulltextClause, fulltextTerm string) { 2997 // Copy the terms as we will need to prepare them differently for each search type. 2998 fulltextTerm = term 2999 3000 // These chars must be treated as spaces in the fulltext query. 3001 for _, c := range spaceFulltextSearchChar { 3002 fulltextTerm = strings.Replace(fulltextTerm, c, " ", -1) 3003 } 3004 3005 // Prepare the FULLTEXT portion of the query. 3006 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 3007 fulltextTerm = strings.Replace(fulltextTerm, "|", "", -1) 3008 3009 splitTerm := strings.Fields(fulltextTerm) 3010 for i, t := range strings.Fields(fulltextTerm) { 3011 if i == len(splitTerm)-1 { 3012 splitTerm[i] = t + ":*" 3013 } else { 3014 splitTerm[i] = t + ":* &" 3015 } 3016 } 3017 3018 fulltextTerm = strings.Join(splitTerm, " ") 3019 3020 fulltextClause = fmt.Sprintf("((to_tsvector('english', %s)) @@ to_tsquery('english', :FulltextTerm))", convertMySQLFullTextColumnsToPostgres(searchColumns)) 3021 } else if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 3022 splitTerm := strings.Fields(fulltextTerm) 3023 for i, t := range strings.Fields(fulltextTerm) { 3024 splitTerm[i] = "+" + t + "*" 3025 } 3026 3027 fulltextTerm = strings.Join(splitTerm, " ") 3028 3029 fulltextClause = fmt.Sprintf("MATCH(%s) AGAINST (:FulltextTerm IN BOOLEAN MODE)", searchColumns) 3030 } 3031 3032 return 3033 } 3034 3035 func (s SqlChannelStore) performSearch(searchQuery string, term string, parameters map[string]interface{}) (*model.ChannelList, *model.AppError) { 3036 likeClause, likeTerm := s.buildLIKEClause(term, "c.Name, c.DisplayName, c.Purpose") 3037 if likeTerm == "" { 3038 // If the likeTerm is empty after preparing, then don't bother searching. 3039 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1) 3040 } else { 3041 parameters["LikeTerm"] = likeTerm 3042 fulltextClause, fulltextTerm := s.buildFulltextClause(term, "c.Name, c.DisplayName, c.Purpose") 3043 parameters["FulltextTerm"] = fulltextTerm 3044 searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "AND ("+likeClause+" OR "+fulltextClause+")", 1) 3045 } 3046 3047 var channels model.ChannelList 3048 3049 if _, err := s.GetReplica().Select(&channels, searchQuery, parameters); err != nil { 3050 return nil, model.NewAppError("SqlChannelStore.Search", "store.sql_channel.search.app_error", nil, "term="+term+", "+", "+err.Error(), http.StatusInternalServerError) 3051 } 3052 3053 return &channels, nil 3054 } 3055 3056 func (s SqlChannelStore) getSearchGroupChannelsQuery(userId, term string, isPostgreSQL bool) (string, map[string]interface{}) { 3057 var query, baseLikeClause string 3058 if isPostgreSQL { 3059 baseLikeClause = "ARRAY_TO_STRING(ARRAY_AGG(u.Username), ', ') LIKE %s" 3060 query = ` 3061 SELECT 3062 * 3063 FROM 3064 Channels 3065 WHERE 3066 Id IN ( 3067 SELECT 3068 cc.Id 3069 FROM ( 3070 SELECT 3071 c.Id 3072 FROM 3073 Channels c 3074 JOIN 3075 ChannelMembers cm on c.Id = cm.ChannelId 3076 JOIN 3077 Users u on u.Id = cm.UserId 3078 WHERE 3079 c.Type = 'G' 3080 AND 3081 u.Id = :UserId 3082 GROUP BY 3083 c.Id 3084 ) cc 3085 JOIN 3086 ChannelMembers cm on cc.Id = cm.ChannelId 3087 JOIN 3088 Users u on u.Id = cm.UserId 3089 GROUP BY 3090 cc.Id 3091 HAVING 3092 %s 3093 LIMIT 3094 ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT) + ` 3095 )` 3096 } else { 3097 baseLikeClause = "GROUP_CONCAT(u.Username SEPARATOR ', ') LIKE %s" 3098 query = ` 3099 SELECT 3100 cc.* 3101 FROM ( 3102 SELECT 3103 c.* 3104 FROM 3105 Channels c 3106 JOIN 3107 ChannelMembers cm on c.Id = cm.ChannelId 3108 JOIN 3109 Users u on u.Id = cm.UserId 3110 WHERE 3111 c.Type = 'G' 3112 AND 3113 u.Id = :UserId 3114 GROUP BY 3115 c.Id 3116 ) cc 3117 JOIN 3118 ChannelMembers cm on cc.Id = cm.ChannelId 3119 JOIN 3120 Users u on u.Id = cm.UserId 3121 GROUP BY 3122 cc.Id 3123 HAVING 3124 %s 3125 LIMIT 3126 ` + strconv.Itoa(model.CHANNEL_SEARCH_DEFAULT_LIMIT) 3127 } 3128 3129 var likeClauses []string 3130 args := map[string]interface{}{"UserId": userId} 3131 terms := strings.Split(strings.ToLower(strings.Trim(term, " ")), " ") 3132 3133 for idx, term := range terms { 3134 argName := fmt.Sprintf("Term%v", idx) 3135 term = sanitizeSearchTerm(term, "\\") 3136 likeClauses = append(likeClauses, fmt.Sprintf(baseLikeClause, ":"+argName)) 3137 args[argName] = "%" + term + "%" 3138 } 3139 3140 query = fmt.Sprintf(query, strings.Join(likeClauses, " AND ")) 3141 return query, args 3142 } 3143 3144 func (s SqlChannelStore) SearchGroupChannels(userId, term string) (*model.ChannelList, *model.AppError) { 3145 isPostgreSQL := s.DriverName() == model.DATABASE_DRIVER_POSTGRES 3146 queryString, args := s.getSearchGroupChannelsQuery(userId, term, isPostgreSQL) 3147 3148 var groupChannels model.ChannelList 3149 if _, err := s.GetReplica().Select(&groupChannels, queryString, args); err != nil { 3150 return nil, model.NewAppError("SqlChannelStore.SearchGroupChannels", "store.sql_channel.search_group_channels.app_error", nil, "userId="+userId+", term="+term+", err="+err.Error(), http.StatusInternalServerError) 3151 } 3152 return &groupChannels, nil 3153 } 3154 3155 func (s SqlChannelStore) GetMembersByIds(channelId string, userIds []string) (*model.ChannelMembers, *model.AppError) { 3156 var dbMembers channelMemberWithSchemeRolesList 3157 props := make(map[string]interface{}) 3158 idQuery := "" 3159 3160 for index, userId := range userIds { 3161 if len(idQuery) > 0 { 3162 idQuery += ", " 3163 } 3164 3165 props["userId"+strconv.Itoa(index)] = userId 3166 idQuery += ":userId" + strconv.Itoa(index) 3167 } 3168 3169 props["ChannelId"] = channelId 3170 3171 if _, err := s.GetReplica().Select(&dbMembers, CHANNEL_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ChannelMembers.ChannelId = :ChannelId AND ChannelMembers.UserId IN ("+idQuery+")", props); err != nil { 3172 return nil, model.NewAppError("SqlChannelStore.GetMembersByIds", "store.sql_channel.get_members_by_ids.app_error", nil, "channelId="+channelId+" "+err.Error(), http.StatusInternalServerError) 3173 } 3174 3175 return dbMembers.ToModel(), nil 3176 } 3177 3178 func (s SqlChannelStore) GetChannelsByScheme(schemeId string, offset int, limit int) (model.ChannelList, *model.AppError) { 3179 var channels model.ChannelList 3180 _, err := s.GetReplica().Select(&channels, "SELECT * FROM Channels WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit}) 3181 if err != nil { 3182 return nil, model.NewAppError("SqlChannelStore.GetChannelsByScheme", "store.sql_channel.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError) 3183 } 3184 return channels, nil 3185 } 3186 3187 // This function does the Advanced Permissions Phase 2 migration for ChannelMember objects. It performs the migration 3188 // in batches as a single transaction per batch to ensure consistency but to also minimise execution time to avoid 3189 // causing unnecessary table locks. **THIS FUNCTION SHOULD NOT BE USED FOR ANY OTHER PURPOSE.** Executing this function 3190 // *after* the new Schemes functionality has been used on an installation will have unintended consequences. 3191 func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId string) (map[string]string, *model.AppError) { 3192 var transaction *gorp.Transaction 3193 var err error 3194 3195 if transaction, err = s.GetMaster().Begin(); err != nil { 3196 return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3197 } 3198 defer finalizeTransaction(transaction) 3199 3200 var channelMembers []channelMember 3201 if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:FromChannelId, :FromUserId) ORDER BY ChannelId, UserId LIMIT 100", map[string]interface{}{"FromChannelId": fromChannelId, "FromUserId": fromUserId}); err != nil { 3202 return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.select.app_error", nil, err.Error(), http.StatusInternalServerError) 3203 } 3204 3205 if len(channelMembers) == 0 { 3206 // No more channel members in query result means that the migration has finished. 3207 return nil, nil 3208 } 3209 3210 for i := range channelMembers { 3211 member := channelMembers[i] 3212 roles := strings.Fields(member.Roles) 3213 var newRoles []string 3214 if !member.SchemeAdmin.Valid { 3215 member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true} 3216 } 3217 if !member.SchemeUser.Valid { 3218 member.SchemeUser = sql.NullBool{Bool: false, Valid: true} 3219 } 3220 if !member.SchemeGuest.Valid { 3221 member.SchemeGuest = sql.NullBool{Bool: false, Valid: true} 3222 } 3223 for _, role := range roles { 3224 if role == model.CHANNEL_ADMIN_ROLE_ID { 3225 member.SchemeAdmin = sql.NullBool{Bool: true, Valid: true} 3226 } else if role == model.CHANNEL_USER_ROLE_ID { 3227 member.SchemeUser = sql.NullBool{Bool: true, Valid: true} 3228 } else if role == model.CHANNEL_GUEST_ROLE_ID { 3229 member.SchemeGuest = sql.NullBool{Bool: true, Valid: true} 3230 } else { 3231 newRoles = append(newRoles, role) 3232 } 3233 } 3234 member.Roles = strings.Join(newRoles, " ") 3235 3236 if _, err := transaction.Update(&member); err != nil { 3237 return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.update.app_error", nil, err.Error(), http.StatusInternalServerError) 3238 } 3239 3240 } 3241 3242 if err := transaction.Commit(); err != nil { 3243 return nil, model.NewAppError("SqlChannelStore.MigrateChannelMembers", "store.sql_channel.migrate_channel_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3244 } 3245 3246 data := make(map[string]string) 3247 data["ChannelId"] = channelMembers[len(channelMembers)-1].ChannelId 3248 data["UserId"] = channelMembers[len(channelMembers)-1].UserId 3249 return data, nil 3250 } 3251 3252 func (s SqlChannelStore) ResetAllChannelSchemes() *model.AppError { 3253 transaction, err := s.GetMaster().Begin() 3254 if err != nil { 3255 return model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3256 } 3257 defer finalizeTransaction(transaction) 3258 3259 resetErr := s.resetAllChannelSchemesT(transaction) 3260 if resetErr != nil { 3261 return resetErr 3262 } 3263 3264 if err := transaction.Commit(); err != nil { 3265 return model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3266 } 3267 3268 return nil 3269 } 3270 3271 func (s SqlChannelStore) resetAllChannelSchemesT(transaction *gorp.Transaction) *model.AppError { 3272 if _, err := transaction.Exec("UPDATE Channels SET SchemeId=''"); err != nil { 3273 return model.NewAppError("SqlChannelStore.ResetAllChannelSchemes", "store.sql_channel.reset_all_channel_schemes.app_error", nil, err.Error(), http.StatusInternalServerError) 3274 } 3275 3276 return nil 3277 } 3278 3279 func (s SqlChannelStore) ClearAllCustomRoleAssignments() *model.AppError { 3280 builtInRoles := model.MakeDefaultRoles() 3281 lastUserId := strings.Repeat("0", 26) 3282 lastChannelId := strings.Repeat("0", 26) 3283 3284 for { 3285 var transaction *gorp.Transaction 3286 var err error 3287 3288 if transaction, err = s.GetMaster().Begin(); err != nil { 3289 return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3290 } 3291 3292 var channelMembers []*channelMember 3293 if _, err := transaction.Select(&channelMembers, "SELECT * from ChannelMembers WHERE (ChannelId, UserId) > (:ChannelId, :UserId) ORDER BY ChannelId, UserId LIMIT 1000", map[string]interface{}{"ChannelId": lastChannelId, "UserId": lastUserId}); err != nil { 3294 finalizeTransaction(transaction) 3295 return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.select.app_error", nil, err.Error(), http.StatusInternalServerError) 3296 } 3297 3298 if len(channelMembers) == 0 { 3299 finalizeTransaction(transaction) 3300 break 3301 } 3302 3303 for _, member := range channelMembers { 3304 lastUserId = member.UserId 3305 lastChannelId = member.ChannelId 3306 3307 var newRoles []string 3308 3309 for _, role := range strings.Fields(member.Roles) { 3310 for name := range builtInRoles { 3311 if name == role { 3312 newRoles = append(newRoles, role) 3313 break 3314 } 3315 } 3316 } 3317 3318 newRolesString := strings.Join(newRoles, " ") 3319 if newRolesString != member.Roles { 3320 if _, err := transaction.Exec("UPDATE ChannelMembers SET Roles = :Roles WHERE UserId = :UserId AND ChannelId = :ChannelId", map[string]interface{}{"Roles": newRolesString, "ChannelId": member.ChannelId, "UserId": member.UserId}); err != nil { 3321 finalizeTransaction(transaction) 3322 return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.update.app_error", nil, err.Error(), http.StatusInternalServerError) 3323 } 3324 } 3325 } 3326 3327 if err := transaction.Commit(); err != nil { 3328 finalizeTransaction(transaction) 3329 return model.NewAppError("SqlChannelStore.ClearAllCustomRoleAssignments", "store.sql_channel.clear_all_custom_role_assignments.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3330 } 3331 } 3332 3333 return nil 3334 } 3335 3336 func (s SqlChannelStore) GetAllChannelsForExportAfter(limit int, afterId string) ([]*model.ChannelForExport, *model.AppError) { 3337 var channels []*model.ChannelForExport 3338 if _, err := s.GetReplica().Select(&channels, ` 3339 SELECT 3340 Channels.*, 3341 Teams.Name as TeamName, 3342 Schemes.Name as SchemeName 3343 FROM Channels 3344 INNER JOIN 3345 Teams ON Channels.TeamId = Teams.Id 3346 LEFT JOIN 3347 Schemes ON Channels.SchemeId = Schemes.Id 3348 WHERE 3349 Channels.Id > :AfterId 3350 AND Channels.Type IN ('O', 'P') 3351 ORDER BY 3352 Id 3353 LIMIT :Limit`, 3354 map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil { 3355 return nil, model.NewAppError("SqlChannelStore.GetAllChannelsForExportAfter", "store.sql_channel.get_all.app_error", nil, err.Error(), http.StatusInternalServerError) 3356 } 3357 3358 return channels, nil 3359 } 3360 3361 func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string) ([]*model.ChannelMemberForExport, *model.AppError) { 3362 var members []*model.ChannelMemberForExport 3363 _, err := s.GetReplica().Select(&members, ` 3364 SELECT 3365 ChannelMembers.ChannelId, 3366 ChannelMembers.UserId, 3367 ChannelMembers.Roles, 3368 ChannelMembers.LastViewedAt, 3369 ChannelMembers.MsgCount, 3370 ChannelMembers.MentionCount, 3371 ChannelMembers.NotifyProps, 3372 ChannelMembers.LastUpdateAt, 3373 ChannelMembers.SchemeUser, 3374 ChannelMembers.SchemeAdmin, 3375 (ChannelMembers.SchemeGuest IS NOT NULL AND ChannelMembers.SchemeGuest) as SchemeGuest, 3376 Channels.Name as ChannelName 3377 FROM 3378 ChannelMembers 3379 INNER JOIN 3380 Channels ON ChannelMembers.ChannelId = Channels.Id 3381 WHERE 3382 ChannelMembers.UserId = :UserId 3383 AND Channels.TeamId = :TeamId 3384 AND Channels.DeleteAt = 0`, 3385 map[string]interface{}{"TeamId": teamId, "UserId": userId}) 3386 3387 if err != nil { 3388 return nil, model.NewAppError("SqlChannelStore.GetChannelMembersForExport", "store.sql_channel.get_members.app_error", nil, "teamId="+teamId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 3389 } 3390 3391 return members, nil 3392 } 3393 3394 func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId string) ([]*model.DirectChannelForExport, *model.AppError) { 3395 var directChannelsForExport []*model.DirectChannelForExport 3396 query := s.getQueryBuilder(). 3397 Select("Channels.*"). 3398 From("Channels"). 3399 Where(sq.And{ 3400 sq.Gt{"Channels.Id": afterId}, 3401 sq.Eq{"Channels.DeleteAt": int(0)}, 3402 sq.Eq{"Channels.Type": []string{"D", "G"}}, 3403 }). 3404 OrderBy("Channels.Id"). 3405 Limit(uint64(limit)) 3406 3407 queryString, args, err := query.ToSql() 3408 if err != nil { 3409 return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError) 3410 } 3411 3412 if _, err = s.GetReplica().Select(&directChannelsForExport, queryString, args...); err != nil { 3413 return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError) 3414 } 3415 3416 var channelIds []string 3417 for _, channel := range directChannelsForExport { 3418 channelIds = append(channelIds, channel.Id) 3419 } 3420 query = s.getQueryBuilder(). 3421 Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest"). 3422 From("ChannelMembers cm"). 3423 Join("Users u ON ( u.Id = cm.UserId )"). 3424 Where(sq.And{ 3425 sq.Eq{"cm.ChannelId": channelIds}, 3426 sq.Eq{"u.DeleteAt": int(0)}, 3427 }) 3428 3429 queryString, args, err = query.ToSql() 3430 if err != nil { 3431 return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError) 3432 } 3433 3434 var channelMembers []*model.ChannelMemberForExport 3435 if _, err := s.GetReplica().Select(&channelMembers, queryString, args...); err != nil { 3436 return nil, model.NewAppError("SqlChannelStore.GetAllDirectChannelsForExportAfter", "store.sql_channel.get_all_direct.app_error", nil, err.Error(), http.StatusInternalServerError) 3437 } 3438 3439 // Populate each channel with its members 3440 dmChannelsMap := make(map[string]*model.DirectChannelForExport) 3441 for _, channel := range directChannelsForExport { 3442 channel.Members = &[]string{} 3443 dmChannelsMap[channel.Id] = channel 3444 } 3445 for _, member := range channelMembers { 3446 members := dmChannelsMap[member.ChannelId].Members 3447 *members = append(*members, member.Username) 3448 } 3449 3450 return directChannelsForExport, nil 3451 } 3452 3453 func (s SqlChannelStore) GetChannelsBatchForIndexing(startTime, endTime int64, limit int) ([]*model.Channel, *model.AppError) { 3454 query := 3455 `SELECT 3456 * 3457 FROM 3458 Channels 3459 WHERE 3460 Type = 'O' 3461 AND 3462 CreateAt >= :StartTime 3463 AND 3464 CreateAt < :EndTime 3465 ORDER BY 3466 CreateAt 3467 LIMIT 3468 :NumChannels` 3469 3470 var channels []*model.Channel 3471 _, err := s.GetSearchReplica().Select(&channels, query, map[string]interface{}{"StartTime": startTime, "EndTime": endTime, "NumChannels": limit}) 3472 if err != nil { 3473 return nil, model.NewAppError("SqlChannelStore.GetChannelsBatchForIndexing", "store.sql_channel.get_channels_batch_for_indexing.get.app_error", nil, err.Error(), http.StatusInternalServerError) 3474 } 3475 3476 return channels, nil 3477 } 3478 3479 func (s SqlChannelStore) UserBelongsToChannels(userId string, channelIds []string) (bool, *model.AppError) { 3480 query := s.getQueryBuilder(). 3481 Select("Count(*)"). 3482 From("ChannelMembers"). 3483 Where(sq.And{ 3484 sq.Eq{"UserId": userId}, 3485 sq.Eq{"ChannelId": channelIds}, 3486 }) 3487 3488 queryString, args, err := query.ToSql() 3489 if err != nil { 3490 return false, model.NewAppError("SqlChannelStore.UserBelongsToChannels", "store.sql_channel.user_belongs_to_channels.app_error", nil, err.Error(), http.StatusInternalServerError) 3491 } 3492 c, err := s.GetReplica().SelectInt(queryString, args...) 3493 if err != nil { 3494 return false, model.NewAppError("SqlChannelStore.UserBelongsToChannels", "store.sql_channel.user_belongs_to_channels.app_error", nil, err.Error(), http.StatusInternalServerError) 3495 } 3496 return c > 0, nil 3497 } 3498 3499 func (s SqlChannelStore) UpdateMembersRole(channelID string, userIDs []string) *model.AppError { 3500 sql := fmt.Sprintf(` 3501 UPDATE 3502 ChannelMembers 3503 SET 3504 SchemeAdmin = CASE WHEN UserId IN ('%s') THEN 3505 TRUE 3506 ELSE 3507 FALSE 3508 END 3509 WHERE 3510 ChannelId = :ChannelId 3511 AND (SchemeGuest = false OR SchemeGuest IS NULL) 3512 `, strings.Join(userIDs, "', '")) 3513 3514 if _, err := s.GetMaster().Exec(sql, map[string]interface{}{"ChannelId": channelID}); err != nil { 3515 return model.NewAppError("SqlChannelStore.UpdateMembersRole", "store.update_error", nil, err.Error(), http.StatusInternalServerError) 3516 } 3517 3518 return nil 3519 } 3520 3521 func (s SqlChannelStore) GroupSyncedChannelCount() (int64, *model.AppError) { 3522 query := s.getQueryBuilder().Select("COUNT(*)").From("Channels").Where(sq.Eq{"GroupConstrained": true, "DeleteAt": 0}) 3523 3524 sql, args, err := query.ToSql() 3525 if err != nil { 3526 return 0, model.NewAppError("SqlChannelStore.GroupSyncedChannelCount", "store.sql_group.app_error", nil, err.Error(), http.StatusInternalServerError) 3527 } 3528 3529 count, err := s.GetReplica().SelectInt(sql, args...) 3530 if err != nil { 3531 return 0, model.NewAppError("SqlChannelStore.GroupSyncedChannelCount", "store.select_error", nil, err.Error(), http.StatusInternalServerError) 3532 } 3533 3534 return count, nil 3535 } 3536 3537 type sidebarCategoryForJoin struct { 3538 model.SidebarCategory 3539 ChannelId *string 3540 } 3541 3542 func (s SqlChannelStore) CreateSidebarCategory(userId, teamId string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) { 3543 transaction, err := s.GetMaster().Begin() 3544 if err != nil { 3545 return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3546 } 3547 3548 defer finalizeTransaction(transaction) 3549 3550 categoriesWithOrder, appErr := s.getSidebarCategoriesT(transaction, userId, teamId) 3551 if appErr != nil { 3552 return nil, appErr 3553 } 3554 if len(categoriesWithOrder.Categories) < 1 { 3555 return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError) 3556 } 3557 newOrder := categoriesWithOrder.Order 3558 newCategoryId := model.NewId() 3559 newCategorySortOrder := 0 3560 /* 3561 When a new category is created, it should be placed as follows: 3562 1. If the Favorites category is first, the new category should be placed after it 3563 2. Otherwise, the new category should be placed first. 3564 */ 3565 if categoriesWithOrder.Categories[0].Type == model.SidebarCategoryFavorites { 3566 newOrder = append([]string{newOrder[0], newCategoryId}, newOrder[1:]...) 3567 newCategorySortOrder = model.MinimalSidebarSortDistance 3568 } else { 3569 newOrder = append([]string{newCategoryId}, newOrder...) 3570 } 3571 3572 category := &model.SidebarCategory{ 3573 DisplayName: newCategory.DisplayName, 3574 Id: newCategoryId, 3575 UserId: userId, 3576 TeamId: teamId, 3577 Sorting: model.SidebarCategorySortDefault, 3578 SortOrder: int64(model.MinimalSidebarSortDistance * len(newOrder)), // first we place it at the end of the list 3579 Type: model.SidebarCategoryCustom, 3580 } 3581 if err = transaction.Insert(category); err != nil { 3582 return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3583 } 3584 3585 if len(newCategory.Channels) > 0 { 3586 channelIdsKeys, deleteParams := MapStringsToQueryParams(newCategory.Channels, "ChannelId") 3587 deleteParams["UserId"] = userId 3588 deleteParams["TeamId"] = teamId 3589 3590 // Remove any channels from their previous categories and add them to the new one 3591 var deleteQuery string 3592 if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 3593 deleteQuery = ` 3594 DELETE 3595 SidebarChannels 3596 FROM 3597 SidebarChannels 3598 JOIN 3599 SidebarCategories ON SidebarChannels.CategoryId = SidebarCategories.Id 3600 WHERE 3601 SidebarChannels.UserId = :UserId 3602 AND SidebarChannels.ChannelId IN ` + channelIdsKeys + ` 3603 AND SidebarCategories.TeamId = :TeamId` 3604 } else { 3605 deleteQuery = ` 3606 DELETE FROM 3607 SidebarChannels 3608 USING 3609 SidebarCategories 3610 WHERE 3611 SidebarChannels.CategoryId = SidebarCategories.Id 3612 AND SidebarChannels.UserId = :UserId 3613 AND SidebarChannels.ChannelId IN ` + channelIdsKeys + ` 3614 AND SidebarCategories.TeamId = :TeamId` 3615 } 3616 3617 _, err = transaction.Exec(deleteQuery, deleteParams) 3618 if err != nil { 3619 return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3620 } 3621 3622 var channels []interface{} 3623 for i, channelID := range newCategory.Channels { 3624 channels = append(channels, &model.SidebarChannel{ 3625 ChannelId: channelID, 3626 CategoryId: newCategoryId, 3627 SortOrder: int64(i * model.MinimalSidebarSortDistance), 3628 UserId: userId, 3629 }) 3630 } 3631 if err = transaction.Insert(channels...); err != nil { 3632 return nil, model.NewAppError("SqlPostStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3633 } 3634 } 3635 3636 // now we re-order the categories according to the new order 3637 if appErr := s.updateSidebarCategoryOrderT(transaction, userId, teamId, newOrder); appErr != nil { 3638 return nil, appErr 3639 } 3640 3641 if err = transaction.Commit(); err != nil { 3642 return nil, model.NewAppError("SqlChannelStore.CreateSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3643 } 3644 3645 // patch category to return proper sort order 3646 category.SortOrder = int64(newCategorySortOrder) 3647 result := &model.SidebarCategoryWithChannels{ 3648 SidebarCategory: *category, 3649 Channels: newCategory.Channels, 3650 } 3651 3652 return result, nil 3653 } 3654 3655 func (s SqlChannelStore) completePopulatingCategoryChannels(category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) { 3656 transaction, err := s.GetMaster().Begin() 3657 if err != nil { 3658 return nil, model.NewAppError("SqlChannelStore.completePopulatingCategoryChannels", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3659 } 3660 defer finalizeTransaction(transaction) 3661 3662 result, appErr := s.completePopulatingCategoryChannelsT(transaction, category) 3663 if appErr != nil { 3664 return nil, appErr 3665 } 3666 3667 if err = transaction.Commit(); err != nil { 3668 return nil, model.NewAppError("SqlChannelStore.completePopulatingCategoryChannels", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3669 } 3670 3671 return result, nil 3672 } 3673 3674 func (s SqlChannelStore) completePopulatingCategoryChannelsT(transation *gorp.Transaction, category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, *model.AppError) { 3675 if category.Type == model.SidebarCategoryCustom || category.Type == model.SidebarCategoryFavorites { 3676 return category, nil 3677 } 3678 3679 var channelTypeFilter sq.Sqlizer 3680 if category.Type == model.SidebarCategoryDirectMessages { 3681 // any DM/GM channels that aren't in any category should be returned as part of the Direct Messages category 3682 channelTypeFilter = sq.Eq{"Channels.Type": []string{model.CHANNEL_DIRECT, model.CHANNEL_GROUP}} 3683 } else if category.Type == model.SidebarCategoryChannels { 3684 // 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 3685 channelTypeFilter = sq.And{ 3686 sq.Eq{"Channels.Type": []string{model.CHANNEL_OPEN, model.CHANNEL_PRIVATE}}, 3687 sq.Eq{"Channels.TeamId": category.TeamId}, 3688 } 3689 } 3690 3691 // A subquery that is true if the channel does not have a SidebarChannel entry for the current user on the current team 3692 doesNotHaveSidebarChannel := sq.Select("1"). 3693 Prefix("NOT EXISTS ("). 3694 From("SidebarChannels"). 3695 Join("SidebarCategories on SidebarChannels.CategoryId=SidebarCategories.Id"). 3696 Where(sq.And{ 3697 sq.Expr("SidebarChannels.ChannelId = ChannelMembers.ChannelId"), 3698 sq.Eq{"SidebarCategories.UserId": category.UserId}, 3699 sq.Eq{"SidebarCategories.TeamId": category.TeamId}, 3700 }). 3701 Suffix(")") 3702 3703 var channels []string 3704 sql, args, _ := s.getQueryBuilder(). 3705 Select("Id"). 3706 From("ChannelMembers"). 3707 LeftJoin("Channels ON Channels.Id=ChannelMembers.ChannelId"). 3708 Where(sq.And{ 3709 sq.Eq{"ChannelMembers.UserId": category.UserId}, 3710 channelTypeFilter, 3711 sq.Eq{"Channels.DeleteAt": 0}, 3712 doesNotHaveSidebarChannel, 3713 }). 3714 OrderBy("DisplayName ASC").ToSql() 3715 3716 if _, err := transation.Select(&channels, sql, args...); err != nil { 3717 return nil, model.NewAppError("SqlPostStore.completePopulatingCategoryChannelsT", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound) 3718 } 3719 3720 category.Channels = append(channels, category.Channels...) 3721 return category, nil 3722 } 3723 3724 func (s SqlChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) { 3725 var categories []*sidebarCategoryForJoin 3726 sql, args, _ := s.getQueryBuilder(). 3727 Select("SidebarCategories.*", "SidebarChannels.ChannelId"). 3728 From("SidebarCategories"). 3729 LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=SidebarCategories.Id"). 3730 Where(sq.Eq{"SidebarCategories.Id": categoryId}). 3731 OrderBy("SidebarChannels.SortOrder ASC").ToSql() 3732 if _, err := s.GetReplica().Select(&categories, sql, args...); err != nil { 3733 return nil, model.NewAppError("SqlPostStore.GetSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound) 3734 } 3735 result := &model.SidebarCategoryWithChannels{ 3736 SidebarCategory: categories[0].SidebarCategory, 3737 Channels: make([]string, 0), 3738 } 3739 for _, category := range categories { 3740 if category.ChannelId != nil { 3741 result.Channels = append(result.Channels, *category.ChannelId) 3742 } 3743 } 3744 return s.completePopulatingCategoryChannels(result) 3745 } 3746 3747 func (s SqlChannelStore) getSidebarCategoriesT(transaction *gorp.Transaction, userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) { 3748 oc := model.OrderedSidebarCategories{ 3749 Categories: make(model.SidebarCategoriesWithChannels, 0), 3750 Order: make([]string, 0), 3751 } 3752 3753 var categories []*sidebarCategoryForJoin 3754 sql, args, _ := s.getQueryBuilder(). 3755 Select("SidebarCategories.*", "SidebarChannels.ChannelId"). 3756 From("SidebarCategories"). 3757 LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=Id"). 3758 Where(sq.And{ 3759 sq.Eq{"SidebarCategories.UserId": userId}, 3760 sq.Eq{"SidebarCategories.TeamId": teamId}, 3761 }). 3762 OrderBy("SidebarCategories.SortOrder ASC, SidebarChannels.SortOrder ASC").ToSql() 3763 3764 if _, err := transaction.Select(&categories, sql, args...); err != nil { 3765 return nil, model.NewAppError("SqlPostStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound) 3766 } 3767 for _, category := range categories { 3768 var prevCategory *model.SidebarCategoryWithChannels 3769 for _, existing := range oc.Categories { 3770 if existing.Id == category.Id { 3771 prevCategory = existing 3772 break 3773 } 3774 } 3775 if prevCategory == nil { 3776 prevCategory = &model.SidebarCategoryWithChannels{ 3777 SidebarCategory: category.SidebarCategory, 3778 Channels: make([]string, 0), 3779 } 3780 oc.Categories = append(oc.Categories, prevCategory) 3781 oc.Order = append(oc.Order, category.Id) 3782 } 3783 if category.ChannelId != nil { 3784 prevCategory.Channels = append(prevCategory.Channels, *category.ChannelId) 3785 } 3786 } 3787 for _, category := range oc.Categories { 3788 if _, err := s.completePopulatingCategoryChannelsT(transaction, category); err != nil { 3789 return nil, err 3790 } 3791 } 3792 3793 return &oc, nil 3794 } 3795 3796 func (s SqlChannelStore) GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, *model.AppError) { 3797 transaction, err := s.GetMaster().Begin() 3798 if err != nil { 3799 return nil, model.NewAppError("SqlChannelStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3800 } 3801 3802 defer finalizeTransaction(transaction) 3803 3804 oc, appErr := s.getSidebarCategoriesT(transaction, userId, teamId) 3805 if appErr != nil { 3806 return nil, appErr 3807 } 3808 3809 if err = transaction.Commit(); err != nil { 3810 return nil, model.NewAppError("SqlChannelStore.GetSidebarCategories", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3811 } 3812 3813 return oc, nil 3814 } 3815 3816 func (s SqlChannelStore) GetSidebarCategoryOrder(userId, teamId string) ([]string, *model.AppError) { 3817 var ids []string 3818 3819 sql, args, _ := s.getQueryBuilder(). 3820 Select("Id"). 3821 From("SidebarCategories"). 3822 Where(sq.And{ 3823 sq.Eq{"UserId": userId}, 3824 sq.Eq{"TeamId": teamId}, 3825 }). 3826 OrderBy("SidebarCategories.SortOrder ASC").ToSql() 3827 3828 if _, err := s.GetReplica().Select(&ids, sql, args...); err != nil { 3829 return nil, model.NewAppError("SqlPostStore.GetSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusNotFound) 3830 } 3831 return ids, nil 3832 } 3833 3834 func (s SqlChannelStore) updateSidebarCategoryOrderT(transaction *gorp.Transaction, userId, teamId string, categoryOrder []string) *model.AppError { 3835 var newOrder []interface{} 3836 runningOrder := 0 3837 for _, categoryId := range categoryOrder { 3838 newOrder = append(newOrder, &model.SidebarCategory{ 3839 Id: categoryId, 3840 SortOrder: int64(runningOrder), 3841 }) 3842 runningOrder += model.MinimalSidebarSortDistance 3843 } 3844 3845 // There's a bug in gorp where UpdateColumns messes up the stored query for any other attempt to use .Update or 3846 // .UpdateColumns on this table, so it's okay to use here as long as we don't use those methods for SidebarCategories 3847 // anywhere else. 3848 if _, err := transaction.UpdateColumns(func(col *gorp.ColumnMap) bool { 3849 return col.ColumnName == "SortOrder" 3850 }, newOrder...); err != nil { 3851 return model.NewAppError("SqlPostStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3852 } 3853 3854 return nil 3855 } 3856 3857 func (s SqlChannelStore) UpdateSidebarCategoryOrder(userId, teamId string, categoryOrder []string) *model.AppError { 3858 transaction, err := s.GetMaster().Begin() 3859 if err != nil { 3860 return model.NewAppError("SqlChannelStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3861 } 3862 3863 defer finalizeTransaction(transaction) 3864 3865 // Ensure no invalid categories are included and that no categories are left out 3866 existingOrder, appErr := s.GetSidebarCategoryOrder(userId, teamId) 3867 if appErr != nil { 3868 return appErr 3869 } 3870 if len(existingOrder) != len(categoryOrder) { 3871 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) 3872 } 3873 for _, originalCategoryId := range existingOrder { 3874 found := false 3875 for _, newCategoryId := range categoryOrder { 3876 if newCategoryId == originalCategoryId { 3877 found = true 3878 break 3879 } 3880 } 3881 if !found { 3882 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) 3883 } 3884 } 3885 3886 if appErr := s.updateSidebarCategoryOrderT(transaction, userId, teamId, categoryOrder); appErr != nil { 3887 return appErr 3888 } 3889 3890 if err = transaction.Commit(); err != nil { 3891 return model.NewAppError("SqlChannelStore.UpdateSidebarCategoryOrder", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3892 } 3893 3894 return nil 3895 } 3896 3897 func (s SqlChannelStore) UpdateSidebarCategories(userId, teamId string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) { 3898 transaction, err := s.GetMaster().Begin() 3899 if err != nil { 3900 return nil, model.NewAppError("SqlChannelStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 3901 } 3902 defer finalizeTransaction(transaction) 3903 3904 updatedCategories := []*model.SidebarCategoryWithChannels{} 3905 for _, category := range categories { 3906 originalCategory, appErr := s.GetSidebarCategory(category.Id) 3907 if appErr != nil { 3908 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, appErr.Error(), http.StatusInternalServerError) 3909 } 3910 3911 // Copy category to avoid modifying an argument 3912 updatedCategory := &model.SidebarCategoryWithChannels{ 3913 SidebarCategory: category.SidebarCategory, 3914 } 3915 3916 // Prevent any changes to read-only fields of SidebarCategories 3917 updatedCategory.UserId = originalCategory.UserId 3918 updatedCategory.TeamId = originalCategory.TeamId 3919 updatedCategory.SortOrder = originalCategory.SortOrder 3920 updatedCategory.Type = originalCategory.Type 3921 3922 if updatedCategory.Type != model.SidebarCategoryCustom { 3923 updatedCategory.DisplayName = originalCategory.DisplayName 3924 } 3925 3926 if category.Type != model.SidebarCategoryDirectMessages { 3927 updatedCategory.Channels = make([]string, len(category.Channels)) 3928 copy(updatedCategory.Channels, category.Channels) 3929 } 3930 3931 updateQuery, updateParams, _ := s.getQueryBuilder(). 3932 Update("SidebarCategories"). 3933 Set("DisplayName", updatedCategory.DisplayName). 3934 Set("Sorting", updatedCategory.Sorting). 3935 Where(sq.Eq{"Id": updatedCategory.Id}).ToSql() 3936 3937 if _, err = transaction.Exec(updateQuery, updateParams...); err != nil { 3938 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3939 } 3940 3941 // if we are updating DM category, it's order can't channel order cannot be changed. 3942 if category.Type != model.SidebarCategoryDirectMessages { 3943 // Remove any SidebarChannels entries that were either: 3944 // - previously in this category (and any ones that are still in the category will be recreated below) 3945 // - in another category and are being added to this category 3946 sql, args, _ := s.getQueryBuilder(). 3947 Delete("SidebarChannels"). 3948 Where( 3949 sq.And{ 3950 sq.Or{ 3951 sq.Eq{"ChannelId": originalCategory.Channels}, 3952 sq.Eq{"ChannelId": updatedCategory.Channels}, 3953 }, 3954 sq.Eq{"CategoryId": category.Id}, 3955 }, 3956 ).ToSql() 3957 3958 if _, err = transaction.Exec(sql, args...); err != nil { 3959 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3960 } 3961 3962 var channels []interface{} 3963 runningOrder := 0 3964 for _, channelID := range category.Channels { 3965 channels = append(channels, &model.SidebarChannel{ 3966 ChannelId: channelID, 3967 CategoryId: category.Id, 3968 SortOrder: int64(runningOrder), 3969 UserId: userId, 3970 }) 3971 runningOrder += model.MinimalSidebarSortDistance 3972 } 3973 3974 if err = transaction.Insert(channels...); err != nil { 3975 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3976 } 3977 } 3978 3979 // Update the favorites preferences based on channels moving into or out of the Favorites category for compatibility 3980 if category.Type == model.SidebarCategoryFavorites { 3981 // Remove any old favorites 3982 sql, args, _ := s.getQueryBuilder().Delete("Preferences").Where( 3983 sq.Eq{ 3984 "UserId": userId, 3985 "Name": originalCategory.Channels, 3986 "Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 3987 }, 3988 ).ToSql() 3989 3990 if _, err = transaction.Exec(sql, args...); err != nil { 3991 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 3992 } 3993 3994 // And then add the new ones 3995 for _, channelID := range category.Channels { 3996 // This breaks the PreferenceStore abstraction, but it should be safe to assume that everything is a SQL 3997 // store in this package. 3998 if err = s.Preference().(*SqlPreferenceStore).save(transaction, &model.Preference{ 3999 Name: channelID, 4000 UserId: userId, 4001 Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 4002 Value: "true", 4003 }); err != nil { 4004 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4005 } 4006 } 4007 } else { 4008 // Remove any old favorites that might have been in this category 4009 sql, args, _ := s.getQueryBuilder().Delete("Preferences").Where( 4010 sq.Eq{ 4011 "UserId": userId, 4012 "Name": category.Channels, 4013 "Category": model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 4014 }, 4015 ).ToSql() 4016 4017 if _, err = transaction.Exec(sql, args...); err != nil { 4018 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4019 } 4020 } 4021 4022 updatedCategories = append(updatedCategories, updatedCategory) 4023 } 4024 4025 // Ensure Channels are populated for Channels/Direct Messages category if they change 4026 for i, updatedCategory := range updatedCategories { 4027 populated, err := s.completePopulatingCategoryChannelsT(transaction, updatedCategory) 4028 if err != nil { 4029 return nil, model.NewAppError("SqlPostStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4030 } 4031 4032 updatedCategories[i] = populated 4033 } 4034 4035 if err = transaction.Commit(); err != nil { 4036 return nil, model.NewAppError("SqlChannelStore.UpdateSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 4037 } 4038 4039 return updatedCategories, nil 4040 } 4041 4042 // UpdateSidebarChannelsByPreferences is called when the Preference table is being updated to keep SidebarCategories in sync 4043 // At the moment, it's only handling Favorites and NOT DMs/GMs (those will be handled client side) 4044 func (s SqlChannelStore) UpdateSidebarChannelsByPreferences(preferences *model.Preferences) error { 4045 transaction, err := s.GetMaster().Begin() 4046 if err != nil { 4047 return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: begin_transaction") 4048 } 4049 defer finalizeTransaction(transaction) 4050 4051 for _, preference := range *preferences { 4052 preference := preference 4053 4054 if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL { 4055 continue 4056 } 4057 4058 // if new preference is false - remove the channel from the appropriate sidebar category 4059 if preference.Value == "false" { 4060 if err := s.removeSidebarEntriesForPreferenceT(transaction, &preference); err != nil { 4061 return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: removeSidebarEntriesForPreferenceT") 4062 } 4063 } else { 4064 if err := s.addChannelToFavoritesCategoryT(transaction, &preference); err != nil { 4065 return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: addChannelToFavoritesCategoryT") 4066 } 4067 } 4068 } 4069 4070 if err := transaction.Commit(); err != nil { 4071 return errors.Wrap(err, "UpdateSidebarChannelsByPreferences: commit_transaction") 4072 } 4073 4074 return nil 4075 } 4076 4077 func (s SqlChannelStore) removeSidebarEntriesForPreferenceT(transaction *gorp.Transaction, preference *model.Preference) error { 4078 if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL { 4079 return nil 4080 } 4081 4082 // Delete any corresponding SidebarChannels entries in a Favorites category corresponding to this preference. This 4083 // can't use the query builder because it uses DB-specific syntax 4084 params := map[string]interface{}{ 4085 "UserId": preference.UserId, 4086 "ChannelId": preference.Name, 4087 "CategoryType": model.SidebarCategoryFavorites, 4088 } 4089 var query string 4090 if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 4091 query = ` 4092 DELETE 4093 SidebarChannels 4094 FROM 4095 SidebarChannels 4096 JOIN 4097 SidebarCategories ON SidebarChannels.CategoryId = SidebarCategories.Id 4098 WHERE 4099 SidebarChannels.UserId = :UserId 4100 AND SidebarChannels.ChannelId = :ChannelId 4101 AND SidebarCategories.Type = :CategoryType` 4102 } else { 4103 query = ` 4104 DELETE FROM 4105 SidebarChannels 4106 USING 4107 SidebarCategories 4108 WHERE 4109 SidebarChannels.CategoryId = SidebarCategories.Id 4110 AND SidebarChannels.UserId = :UserId 4111 AND SidebarChannels.ChannelId = :ChannelId 4112 AND SidebarCategories.Type = :CategoryType` 4113 } 4114 4115 if _, err := transaction.Exec(query, params); err != nil { 4116 return errors.Wrap(err, "Failed to remove sidebar entries for preference") 4117 } 4118 4119 return nil 4120 } 4121 4122 func (s SqlChannelStore) addChannelToFavoritesCategoryT(transaction *gorp.Transaction, preference *model.Preference) error { 4123 if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL { 4124 return nil 4125 } 4126 4127 var channel *model.Channel 4128 if obj, err := transaction.Get(&model.Channel{}, preference.Name); err != nil { 4129 return errors.Wrapf(err, "Failed to get favorited channel with id=%s", preference.Name) 4130 } else { 4131 channel = obj.(*model.Channel) 4132 } 4133 4134 // Get the IDs of the Favorites category/categories that the channel needs to be added to 4135 builder := s.getQueryBuilder(). 4136 Select("SidebarCategories.Id"). 4137 From("SidebarCategories"). 4138 LeftJoin("SidebarChannels on SidebarCategories.Id = SidebarChannels.CategoryId and SidebarChannels.ChannelId = ?", preference.Name). 4139 Where(sq.Eq{ 4140 "SidebarCategories.UserId": preference.UserId, 4141 "Type": model.SidebarCategoryFavorites, 4142 }). 4143 Where("SidebarChannels.ChannelId is null") 4144 4145 if channel.TeamId != "" { 4146 builder = builder.Where(sq.Eq{"TeamId": channel.TeamId}) 4147 } 4148 4149 idsQuery, idsParams, _ := builder.ToSql() 4150 4151 var categoryIds []string 4152 if _, err := transaction.Select(&categoryIds, idsQuery, idsParams...); err != nil { 4153 return errors.Wrap(err, "Failed to get Favorites sidebar categories") 4154 } 4155 4156 if len(categoryIds) == 0 { 4157 // The channel is already in the Favorites category/categories 4158 return nil 4159 } 4160 4161 // For each category ID, insert a row into SidebarChannels with the given channel ID and a SortOrder that's less than 4162 // all existing SortOrders in the category so that the newly favorited channel comes first 4163 insertQuery, insertParams, _ := s.getQueryBuilder(). 4164 Insert("SidebarChannels"). 4165 Columns( 4166 "ChannelId", 4167 "CategoryId", 4168 "UserId", 4169 "SortOrder", 4170 ). 4171 Select( 4172 sq.Select(). 4173 Column("? as ChannelId", preference.Name). 4174 Column("SidebarCategories.Id as CategoryId"). 4175 Column("? as UserId", preference.UserId). 4176 Column("COALESCE(MIN(SidebarChannels.SortOrder) - 10, 0) as SortOrder"). 4177 From("SidebarCategories"). 4178 LeftJoin("SidebarChannels on SidebarCategories.Id = SidebarChannels.CategoryId"). 4179 Where(sq.Eq{ 4180 "SidebarCategories.Id": categoryIds, 4181 }). 4182 GroupBy("SidebarCategories.Id")).ToSql() 4183 4184 if _, err := transaction.Exec(insertQuery, insertParams...); err != nil { 4185 return errors.Wrap(err, "Failed to add sidebar entries for favorited channel") 4186 } 4187 4188 return nil 4189 } 4190 4191 // DeleteSidebarChannelsByPreferences is called when the Preference table is being updated to keep SidebarCategories in sync 4192 // At the moment, it's only handling Favorites and NOT DMs/GMs (those will be handled client side) 4193 func (s SqlChannelStore) DeleteSidebarChannelsByPreferences(preferences *model.Preferences) error { 4194 transaction, err := s.GetMaster().Begin() 4195 if err != nil { 4196 return errors.Wrap(err, "DeleteSidebarChannelsByPreferences: begin_transaction") 4197 } 4198 defer finalizeTransaction(transaction) 4199 4200 for _, preference := range *preferences { 4201 preference := preference 4202 4203 if preference.Category != model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL { 4204 continue 4205 } 4206 4207 if err := s.removeSidebarEntriesForPreferenceT(transaction, &preference); err != nil { 4208 return errors.Wrap(err, "DeleteSidebarChannelsByPreferences: removeSidebarEntriesForPreferenceT") 4209 } 4210 } 4211 4212 if err := transaction.Commit(); err != nil { 4213 return errors.Wrap(err, "DeleteSidebarChannelsByPreferences: commit_transaction") 4214 } 4215 4216 return nil 4217 } 4218 4219 func (s SqlChannelStore) UpdateSidebarChannelCategoryOnMove(channel *model.Channel, newTeamId string) *model.AppError { 4220 // if channel is being moved, remove it from the categories, since it's possible that there's no matching category in the new team 4221 if _, err := s.GetMaster().Exec("DELETE FROM SidebarChannels WHERE ChannelId=:ChannelId", map[string]interface{}{"ChannelId": channel.Id}); err != nil { 4222 return model.NewAppError("SqlChannelStore.UpdateSidebarChannelCategoryOnMove", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4223 } 4224 return nil 4225 } 4226 4227 func (s SqlChannelStore) ClearSidebarOnTeamLeave(userId, teamId string) *model.AppError { 4228 // if user leaves the team, clean his team related entries in sidebar channels and categories 4229 params := map[string]interface{}{ 4230 "UserId": userId, 4231 "TeamId": teamId, 4232 } 4233 4234 var deleteQuery string 4235 if s.DriverName() == model.DATABASE_DRIVER_MYSQL { 4236 deleteQuery = "DELETE SidebarChannels FROM SidebarChannels LEFT JOIN SidebarCategories ON SidebarCategories.Id = SidebarChannels.CategoryId WHERE SidebarCategories.TeamId=:TeamId AND SidebarCategories.UserId=:UserId" 4237 } else { 4238 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" 4239 } 4240 if _, err := s.GetMaster().Exec(deleteQuery, params); err != nil { 4241 return model.NewAppError("SqlChannelStore.ClearSidebarOnTeamLeave", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4242 } 4243 if _, err := s.GetMaster().Exec("DELETE FROM SidebarCategories WHERE SidebarCategories.TeamId = :TeamId AND SidebarCategories.UserId = :UserId", params); err != nil { 4244 return model.NewAppError("SqlChannelStore.ClearSidebarOnTeamLeave", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4245 } 4246 return nil 4247 } 4248 4249 // DeleteSidebarCategory removes a custom category and moves any channels into it into the Channels and Direct Messages 4250 // categories respectively. Assumes that the provided user ID and team ID match the given category ID. 4251 func (s SqlChannelStore) DeleteSidebarCategory(categoryId string) *model.AppError { 4252 transaction, err := s.GetMaster().Begin() 4253 if err != nil { 4254 return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 4255 } 4256 defer finalizeTransaction(transaction) 4257 4258 // Ensure that we're deleting a custom category 4259 var category *model.SidebarCategory 4260 if err = transaction.SelectOne(&category, "SELECT * FROM SidebarCategories WHERE Id = :Id", map[string]interface{}{"Id": categoryId}); err != nil { 4261 return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4262 } 4263 4264 if category.Type != model.SidebarCategoryCustom { 4265 return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.delete_invalid.app_error", nil, "", http.StatusBadRequest) 4266 } 4267 4268 // Delete the channels in the category 4269 sql, args, _ := s.getQueryBuilder(). 4270 Delete("SidebarChannels"). 4271 Where(sq.Eq{"CategoryId": categoryId}).ToSql() 4272 4273 if _, err := transaction.Exec(sql, args...); err != nil { 4274 return model.NewAppError("SqlPostStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4275 } 4276 4277 // Delete the category itself 4278 sql, args, _ = s.getQueryBuilder(). 4279 Delete("SidebarCategories"). 4280 Where(sq.Eq{"Id": categoryId}).ToSql() 4281 4282 if _, err := transaction.Exec(sql, args...); err != nil { 4283 return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) 4284 } 4285 4286 if err := transaction.Commit(); err != nil { 4287 return model.NewAppError("SqlChannelStore.DeleteSidebarCategory", "store.sql_channel.sidebar_categories.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 4288 } 4289 4290 return nil 4291 }