github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/store/sqlstore/class_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 "strconv" 11 "strings" 12 13 "github.com/mattermost/gorp" 14 15 sq "github.com/Masterminds/squirrel" 16 "github.com/vnforks/kid/v5/einterfaces" 17 "github.com/vnforks/kid/v5/mlog" 18 "github.com/vnforks/kid/v5/model" 19 "github.com/vnforks/kid/v5/services/cache/lru" 20 "github.com/vnforks/kid/v5/store" 21 ) 22 23 const ( 24 ALL_CLASS_MEMBERS_FOR_USER_CACHE_SIZE = model.SESSION_CACHE_SIZE 25 ALL_CLASS_MEMBERS_FOR_USER_CACHE_SEC = 900 // 15 mins 26 27 ALL_CLASS_MEMBERS_NOTIFY_PROPS_FOR_CLASS_CACHE_SIZE = model.SESSION_CACHE_SIZE 28 ALL_CLASS_MEMBERS_NOTIFY_PROPS_FOR_CLASS_CACHE_SEC = 1800 // 30 mins 29 30 CLASS_CACHE_SEC = 900 // 15 mins 31 ) 32 33 type SqlClassStore struct { 34 SqlStore 35 metrics einterfaces.MetricsInterface 36 } 37 38 type classMember struct { 39 ClassId string 40 UserId string 41 Roles string 42 NotifyProps model.StringMap 43 LastUpdateAt int64 44 SchemeUser sql.NullBool 45 SchemeAdmin sql.NullBool 46 } 47 48 func NewClassMemberFromModel(cm *model.ClassMember) *classMember { 49 return &classMember{ 50 ClassId: cm.ClassId, 51 UserId: cm.UserId, 52 Roles: cm.ExplicitRoles, 53 NotifyProps: cm.NotifyProps, 54 LastUpdateAt: cm.LastUpdateAt, 55 SchemeUser: sql.NullBool{Valid: true, Bool: cm.SchemeUser}, 56 SchemeAdmin: sql.NullBool{Valid: true, Bool: cm.SchemeAdmin}, 57 } 58 } 59 60 type classMemberWithSchemeRoles struct { 61 ClassId string 62 UserId string 63 Roles string 64 NotifyProps model.StringMap 65 LastUpdateAt int64 66 SchemeUser sql.NullBool 67 SchemeAdmin sql.NullBool 68 BranchSchemeDefaultUserRole sql.NullString 69 BranchSchemeDefaultAdminRole sql.NullString 70 ClassSchemeDefaultUserRole sql.NullString 71 ClassSchemeDefaultAdminRole sql.NullString 72 } 73 74 func classMemberSliceColumns() []string { 75 return []string{"ClassId", "UserId", "Roles", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin"} 76 } 77 78 func classMemberToSlice(member *model.ClassMember) []interface{} { 79 resultSlice := []interface{}{} 80 resultSlice = append(resultSlice, member.ClassId) 81 resultSlice = append(resultSlice, member.UserId) 82 resultSlice = append(resultSlice, member.ExplicitRoles) 83 resultSlice = append(resultSlice, model.MapToJson(member.NotifyProps)) 84 resultSlice = append(resultSlice, member.LastUpdateAt) 85 resultSlice = append(resultSlice, member.SchemeUser) 86 resultSlice = append(resultSlice, member.SchemeAdmin) 87 return resultSlice 88 } 89 90 type classMemberWithSchemeRolesList []classMemberWithSchemeRoles 91 92 func getClassRoles(schemeUser, schemeAdmin bool, defaultBranchUserRole, defaultBranchAdminRole, defaultClassUserRole, defaultClassAdminRole string, roles []string) rolesInfo { 93 result := rolesInfo{ 94 roles: []string{}, 95 explicitRoles: []string{}, 96 schemeUser: schemeUser, 97 schemeAdmin: schemeAdmin, 98 } 99 100 // Identify any scheme derived roles that are in "Roles" field due to not yet being migrated, and exclude 101 // them from ExplicitRoles field. 102 for _, role := range roles { 103 switch role { 104 case model.CLASS_USER_ROLE_ID: 105 result.schemeUser = true 106 case model.CLASS_ADMIN_ROLE_ID: 107 result.schemeAdmin = true 108 default: 109 result.explicitRoles = append(result.explicitRoles, role) 110 result.roles = append(result.roles, role) 111 } 112 } 113 114 // Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add 115 // them to the Roles field for backwards compatibility reasons. 116 var schemeImpliedRoles []string 117 118 if result.schemeUser { 119 if defaultClassUserRole != "" { 120 schemeImpliedRoles = append(schemeImpliedRoles, defaultClassUserRole) 121 } else { 122 schemeImpliedRoles = append(schemeImpliedRoles, model.CLASS_USER_ROLE_ID) 123 } 124 } 125 if result.schemeAdmin { 126 if defaultClassAdminRole != "" { 127 schemeImpliedRoles = append(schemeImpliedRoles, defaultClassAdminRole) 128 } else { 129 schemeImpliedRoles = append(schemeImpliedRoles, model.CLASS_ADMIN_ROLE_ID) 130 } 131 } 132 for _, impliedRole := range schemeImpliedRoles { 133 alreadyThere := false 134 for _, role := range result.roles { 135 if role == impliedRole { 136 alreadyThere = true 137 break 138 } 139 } 140 if !alreadyThere { 141 result.roles = append(result.roles, impliedRole) 142 } 143 } 144 return result 145 } 146 147 func (db classMemberWithSchemeRoles) ToModel() *model.ClassMember { 148 // Identify any system-wide scheme derived roles that are in "Roles" field due to not yet being migrated, 149 // and exclude them from ExplicitRoles field. 150 schemeUser := db.SchemeUser.Valid && db.SchemeUser.Bool 151 schemeAdmin := db.SchemeAdmin.Valid && db.SchemeAdmin.Bool 152 153 defaultBranchUserRole := "" 154 if db.BranchSchemeDefaultUserRole.Valid { 155 defaultBranchUserRole = db.BranchSchemeDefaultUserRole.String 156 } 157 158 defaultBranchAdminRole := "" 159 if db.BranchSchemeDefaultAdminRole.Valid { 160 defaultBranchAdminRole = db.BranchSchemeDefaultAdminRole.String 161 } 162 163 defaultClassUserRole := "" 164 if db.ClassSchemeDefaultUserRole.Valid { 165 defaultClassUserRole = db.ClassSchemeDefaultUserRole.String 166 } 167 168 defaultClassAdminRole := "" 169 if db.ClassSchemeDefaultAdminRole.Valid { 170 defaultClassAdminRole = db.ClassSchemeDefaultAdminRole.String 171 } 172 173 rolesResult := getClassRoles( 174 schemeUser, schemeAdmin, 175 defaultBranchUserRole, defaultBranchAdminRole, 176 defaultClassUserRole, defaultClassAdminRole, 177 strings.Fields(db.Roles), 178 ) 179 return &model.ClassMember{ 180 ClassId: db.ClassId, 181 UserId: db.UserId, 182 Roles: strings.Join(rolesResult.roles, " "), 183 NotifyProps: db.NotifyProps, 184 LastUpdateAt: db.LastUpdateAt, 185 SchemeAdmin: rolesResult.schemeAdmin, 186 SchemeUser: rolesResult.schemeUser, 187 ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "), 188 } 189 } 190 191 func (db classMemberWithSchemeRolesList) ToModel() *model.ClassMembers { 192 cms := model.ClassMembers{} 193 194 for _, cm := range db { 195 cms = append(cms, *cm.ToModel()) 196 } 197 198 return &cms 199 } 200 201 type allClassMember struct { 202 ClassId string 203 Roles string 204 SchemeUser sql.NullBool 205 SchemeAdmin sql.NullBool 206 BranchSchemeDefaultUserRole sql.NullString 207 BranchSchemeDefaultAdminRole sql.NullString 208 ClassSchemeDefaultUserRole sql.NullString 209 ClassSchemeDefaultAdminRole sql.NullString 210 } 211 212 type allClassMembers []allClassMember 213 214 func (db allClassMember) Process() (string, string) { 215 roles := strings.Fields(db.Roles) 216 217 // Add any scheme derived roles that are not in the Roles field due to being Implicit from the Scheme, and add 218 // them to the Roles field for backwards compatibility reasons. 219 var schemeImpliedRoles []string 220 221 if db.SchemeUser.Valid && db.SchemeUser.Bool { 222 if db.ClassSchemeDefaultUserRole.Valid && db.ClassSchemeDefaultUserRole.String != "" { 223 schemeImpliedRoles = append(schemeImpliedRoles, db.ClassSchemeDefaultUserRole.String) 224 } else { 225 schemeImpliedRoles = append(schemeImpliedRoles, model.CLASS_USER_ROLE_ID) 226 } 227 } 228 if db.SchemeAdmin.Valid && db.SchemeAdmin.Bool { 229 if db.ClassSchemeDefaultAdminRole.Valid && db.ClassSchemeDefaultAdminRole.String != "" { 230 schemeImpliedRoles = append(schemeImpliedRoles, db.ClassSchemeDefaultAdminRole.String) 231 } else { 232 schemeImpliedRoles = append(schemeImpliedRoles, model.CLASS_ADMIN_ROLE_ID) 233 } 234 } 235 for _, impliedRole := range schemeImpliedRoles { 236 alreadyThere := false 237 for _, role := range roles { 238 if role == impliedRole { 239 alreadyThere = true 240 } 241 } 242 if !alreadyThere { 243 roles = append(roles, impliedRole) 244 } 245 } 246 247 return db.ClassId, strings.Join(roles, " ") 248 } 249 250 func (db allClassMembers) ToMapStringString() map[string]string { 251 result := make(map[string]string) 252 253 for _, item := range db { 254 key, value := item.Process() 255 result[key] = value 256 } 257 258 return result 259 } 260 261 var allClassMembersForUserCache = lru.New(ALL_CLASS_MEMBERS_FOR_USER_CACHE_SIZE) 262 var allClassMembersNotifyPropsForClassCache = lru.New(ALL_CLASS_MEMBERS_NOTIFY_PROPS_FOR_CLASS_CACHE_SIZE) 263 var classByNameCache = lru.New(model.CLASS_CACHE_SIZE) 264 265 func (s SqlClassStore) ClearCaches() { 266 allClassMembersForUserCache.Purge() 267 allClassMembersNotifyPropsForClassCache.Purge() 268 classByNameCache.Purge() 269 270 if s.metrics != nil { 271 s.metrics.IncrementMemCacheInvalidationCounter("All Class Members for User - Purge") 272 s.metrics.IncrementMemCacheInvalidationCounter("All Class Members Notify Props for Class - Purge") 273 s.metrics.IncrementMemCacheInvalidationCounter("Class By Name - Purge") 274 } 275 } 276 277 func newSqlClassStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.ClassStore { 278 s := &SqlClassStore{ 279 SqlStore: sqlStore, 280 metrics: metrics, 281 } 282 283 for _, db := range sqlStore.GetAllConns() { 284 table := db.AddTableWithName(model.Class{}, "Classes").SetKeys(false, "Id") 285 table.ColMap("Id").SetMaxSize(26) 286 table.ColMap("BranchId").SetMaxSize(26) 287 table.ColMap("DisplayName").SetMaxSize(64) 288 table.ColMap("Name").SetMaxSize(64) 289 table.SetUniqueTogether("Name", "BranchId") 290 table.ColMap("Header").SetMaxSize(1024) 291 table.ColMap("Purpose").SetMaxSize(250) 292 table.ColMap("CreatorId").SetMaxSize(26) 293 table.ColMap("SchemeId").SetMaxSize(26) 294 295 tablem := db.AddTableWithName(classMember{}, "ClassMembers").SetKeys(false, "ClassId", "UserId") 296 tablem.ColMap("ClassId").SetMaxSize(26) 297 tablem.ColMap("UserId").SetMaxSize(26) 298 tablem.ColMap("Roles").SetMaxSize(64) 299 tablem.ColMap("NotifyProps").SetMaxSize(2000) 300 301 } 302 303 return s 304 } 305 306 func (s SqlClassStore) createIndexesIfNotExists() { 307 s.CreateIndexIfNotExists("idx_classes_branch_id", "Classes", "BranchId") 308 s.CreateIndexIfNotExists("idx_classes_name", "Classes", "Name") 309 s.CreateIndexIfNotExists("idx_classes_update_at", "Classes", "UpdateAt") 310 s.CreateIndexIfNotExists("idx_classes_create_at", "Classes", "CreateAt") 311 s.CreateIndexIfNotExists("idx_classes_delete_at", "Classes", "DeleteAt") 312 313 if s.DriverName() == model.DATABASE_DRIVER_POSTGRES { 314 s.CreateIndexIfNotExists("idx_classes_name_lower", "Classes", "lower(Name)") 315 s.CreateIndexIfNotExists("idx_classes_displayname_lower", "Classes", "lower(DisplayName)") 316 } 317 318 s.CreateIndexIfNotExists("idx_classmembers_class_id", "ClassMembers", "ClassId") 319 s.CreateIndexIfNotExists("idx_classmembers_user_id", "ClassMembers", "UserId") 320 321 s.CreateFullTextIndexIfNotExists("idx_class_search_txt", "Classes", "Name, DisplayName, Purpose") 322 323 s.CreateIndexIfNotExists("idx_classes_scheme_id", "Classes", "SchemeId") 324 } 325 326 // Save writes the (non-direct) class class to the database. 327 func (s SqlClassStore) Save(class *model.Class, maxClassesPerBranch int64) (*model.Class, *model.AppError) { 328 329 if class.DeleteAt != 0 { 330 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save.archived_class.app_error", nil, "", http.StatusBadRequest) 331 } 332 333 transaction, err := s.GetMaster().Begin() 334 if err != nil { 335 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 336 } 337 defer finalizeTransaction(transaction) 338 339 newClass, appErr := s.saveClassT(transaction, class, maxClassesPerBranch) 340 if appErr != nil { 341 return newClass, appErr 342 } 343 344 if err := transaction.Commit(); err != nil { 345 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 346 } 347 348 return newClass, nil 349 } 350 351 func (s SqlClassStore) CreateDirectClass(user *model.User, otherUser *model.User) (*model.Class, *model.AppError) { 352 class := new(model.Class) 353 354 class.DisplayName = "" 355 class.Name = model.GetDMNameFromIds(otherUser.Id, user.Id) 356 357 class.Header = "" 358 359 cm1 := &model.ClassMember{ 360 UserId: user.Id, 361 NotifyProps: model.GetDefaultClassNotifyProps(), 362 } 363 cm2 := &model.ClassMember{ 364 UserId: otherUser.Id, 365 NotifyProps: model.GetDefaultClassNotifyProps(), 366 } 367 368 return s.SaveDirectClass(class, cm1, cm2) 369 } 370 371 func (s SqlClassStore) SaveDirectClass(directclass *model.Class, member1 *model.ClassMember, member2 *model.ClassMember) (*model.Class, *model.AppError) { 372 if directclass.DeleteAt != 0 { 373 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save.archived_class.app_error", nil, "", http.StatusBadRequest) 374 } 375 transaction, err := s.GetMaster().Begin() 376 if err != nil { 377 return nil, model.NewAppError("SqlClassStore.SaveDirectClass", "store.sql_class.save_direct_class.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 378 } 379 defer finalizeTransaction(transaction) 380 381 directclass.BranchId = "" 382 newClass, appErr := s.saveClassT(transaction, directclass, 0) 383 if appErr != nil { 384 return newClass, appErr 385 } 386 387 // Members need new class ID 388 member1.ClassId = newClass.Id 389 member2.ClassId = newClass.Id 390 391 var memberSaveErr *model.AppError 392 if member1.UserId != member2.UserId { 393 _, memberSaveErr = s.saveMultipleMembersT(transaction, []*model.ClassMember{member1, member2}) 394 } else { 395 _, memberSaveErr = s.saveMemberT(transaction, member2) 396 } 397 398 if memberSaveErr != nil { 399 return nil, model.NewAppError("SqlClassStore.SaveDirectClass", "store.sql_class.save_direct_class.add_members.app_error", nil, memberSaveErr.Error(), http.StatusInternalServerError) 400 } 401 402 if err := transaction.Commit(); err != nil { 403 return nil, model.NewAppError("SqlClassStore.SaveDirectClass", "store.sql_class.save_direct_class.commit.app_error", nil, err.Error(), http.StatusInternalServerError) 404 } 405 406 return newClass, nil 407 408 } 409 410 func (s SqlClassStore) saveClassT(transaction *gorp.Transaction, class *model.Class, maxClassesPerBranch int64) (*model.Class, *model.AppError) { 411 if len(class.Id) > 0 { 412 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save_class.existing.app_error", nil, "id="+class.Id, http.StatusBadRequest) 413 } 414 415 class.PreSave() 416 if err := class.IsValid(); err != nil { 417 return nil, err 418 } 419 420 if maxClassesPerBranch >= 0 { 421 if count, err := transaction.SelectInt("SELECT COUNT(0) FROM Classes WHERE BranchId = :BranchId AND DeleteAt = 0 ", map[string]interface{}{"BranchId": class.BranchId}); err != nil { 422 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save_class.current_count.app_error", nil, "branchId="+class.BranchId+", "+err.Error(), http.StatusInternalServerError) 423 } else if count >= maxClassesPerBranch { 424 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save_class.limit.app_error", nil, "branchId="+class.BranchId, http.StatusBadRequest) 425 } 426 } 427 428 if err := transaction.Insert(class); err != nil { 429 if IsUniqueConstraintError(err, []string{"Name", "classes_name_branchid_key"}) { 430 dupClass := model.Class{} 431 s.GetMaster().SelectOne(&dupClass, "SELECT * FROM Classes WHERE BranchId = :BranchId AND Name = :Name", map[string]interface{}{"BranchId": class.BranchId, "Name": class.Name}) 432 return &dupClass, model.NewAppError("SqlClassStore.Save", store.CLASS_EXISTS_ERROR, nil, "id="+class.Id+", "+err.Error(), http.StatusBadRequest) 433 } 434 return nil, model.NewAppError("SqlClassStore.Save", "store.sql_class.save_class.save.app_error", nil, "id="+class.Id+", "+err.Error(), http.StatusInternalServerError) 435 } 436 return class, nil 437 } 438 439 // Update writes the updated class to the database. 440 func (s SqlClassStore) Update(class *model.Class) (*model.Class, *model.AppError) { 441 transaction, err := s.GetMaster().Begin() 442 if err != nil { 443 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 444 } 445 defer finalizeTransaction(transaction) 446 447 updatedClass, appErr := s.updateClassT(transaction, class) 448 if appErr != nil { 449 return nil, appErr 450 } 451 452 if err := transaction.Commit(); err != nil { 453 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 454 } 455 return updatedClass, nil 456 } 457 458 func (s SqlClassStore) updateClassT(transaction *gorp.Transaction, class *model.Class) (*model.Class, *model.AppError) { 459 class.PreUpdate() 460 461 if class.DeleteAt != 0 { 462 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.archived_class.app_error", nil, "", http.StatusBadRequest) 463 } 464 465 if err := class.IsValid(); err != nil { 466 return nil, err 467 } 468 469 count, err := transaction.Update(class) 470 if err != nil { 471 if IsUniqueConstraintError(err, []string{"Name", "classes_name_branchid_key"}) { 472 dupClass := model.Class{} 473 s.GetReplica().SelectOne(&dupClass, "SELECT * FROM Classes WHERE BranchId = :BranchId AND Name= :Name AND DeleteAt > 0", map[string]interface{}{"BranchId": class.BranchId, "Name": class.Name}) 474 if dupClass.DeleteAt > 0 { 475 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.previously.app_error", nil, "id="+class.Id+", "+err.Error(), http.StatusBadRequest) 476 } 477 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.exists.app_error", nil, "id="+class.Id+", "+err.Error(), http.StatusBadRequest) 478 } 479 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.updating.app_error", nil, "id="+class.Id+", "+err.Error(), http.StatusInternalServerError) 480 } 481 482 if count != 1 { 483 return nil, model.NewAppError("SqlClassStore.Update", "store.sql_class.update.app_error", nil, "id="+class.Id, http.StatusInternalServerError) 484 } 485 486 return class, nil 487 } 488 489 func (s SqlClassStore) InvalidateClass(id string) { 490 } 491 492 func (s SqlClassStore) InvalidateClassByName(branchId, name string) { 493 classByNameCache.Remove(branchId + name) 494 if s.metrics != nil { 495 s.metrics.IncrementMemCacheInvalidationCounter("Class by Name - Remove by BranchId and Name") 496 } 497 } 498 499 func (s SqlClassStore) Get(id string, allowFromCache bool) (*model.Class, *model.AppError) { 500 return s.get(id, false, allowFromCache) 501 } 502 func (s SqlClassStore) GetFromMaster(id string) (*model.Class, *model.AppError) { 503 return s.get(id, true, false) 504 } 505 506 func (s SqlClassStore) get(id string, master bool, allowFromCache bool) (*model.Class, *model.AppError) { 507 var db *gorp.DbMap 508 509 if master { 510 db = s.GetMaster() 511 } else { 512 db = s.GetReplica() 513 } 514 515 obj, err := db.Get(model.Class{}, id) 516 if err != nil { 517 return nil, model.NewAppError("SqlClassStore.Get", "store.sql_class.get.find.app_error", nil, "id="+id+", "+err.Error(), http.StatusInternalServerError) 518 } 519 520 if obj == nil { 521 return nil, model.NewAppError("SqlClassStore.Get", "store.sql_class.get.existing.app_error", nil, "id="+id, http.StatusNotFound) 522 } 523 524 ch := obj.(*model.Class) 525 return ch, nil 526 } 527 528 // Delete records the given deleted timestamp to the class in question. 529 func (s SqlClassStore) Delete(classId string, time int64) *model.AppError { 530 return s.SetDeleteAt(classId, time, time) 531 } 532 533 // Restore reverts a previous deleted timestamp from the class in question. 534 func (s SqlClassStore) Restore(classId string, time int64) *model.AppError { 535 return s.SetDeleteAt(classId, 0, time) 536 } 537 538 // SetDeleteAt records the given deleted and updated timestamp to the class in question. 539 func (s SqlClassStore) SetDeleteAt(classId string, deleteAt, updateAt int64) *model.AppError { 540 defer s.InvalidateClass(classId) 541 542 transaction, err := s.GetMaster().Begin() 543 if err != nil { 544 return model.NewAppError("SqlClassStore.SetDeleteAt", "store.sql_class.set_delete_at.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 545 } 546 defer finalizeTransaction(transaction) 547 548 appErr := s.setDeleteAtT(transaction, classId, deleteAt, updateAt) 549 if appErr != nil { 550 return appErr 551 } 552 553 if err := transaction.Commit(); err != nil { 554 return model.NewAppError("SqlClassStore.SetDeleteAt", "store.sql_class.set_delete_at.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 555 } 556 557 return nil 558 } 559 560 func (s SqlClassStore) setDeleteAtT(transaction *gorp.Transaction, classId string, deleteAt, updateAt int64) *model.AppError { 561 _, err := transaction.Exec("Update Classes SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :ClassId", map[string]interface{}{"DeleteAt": deleteAt, "UpdateAt": updateAt, "ClassId": classId}) 562 if err != nil { 563 return model.NewAppError("SqlClassStore.Delete", "store.sql_class.delete.class.app_error", nil, "id="+classId+", err="+err.Error(), http.StatusInternalServerError) 564 } 565 566 return nil 567 } 568 569 // PermanentDeleteByBranch removes all classes for the given branch from the database. 570 func (s SqlClassStore) PermanentDeleteByBranch(branchId string) *model.AppError { 571 transaction, err := s.GetMaster().Begin() 572 if err != nil { 573 return model.NewAppError("SqlClassStore.PermanentDeleteByBranch", "store.sql_class.permanent_delete_by_branch.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 574 } 575 defer finalizeTransaction(transaction) 576 577 if err := s.permanentDeleteByBranchtT(transaction, branchId); err != nil { 578 return err 579 } 580 581 if err := transaction.Commit(); err != nil { 582 return model.NewAppError("SqlClassStore.PermanentDeleteByBranch", "store.sql_class.permanent_delete_by_branch.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 583 } 584 585 return nil 586 } 587 588 func (s SqlClassStore) permanentDeleteByBranchtT(transaction *gorp.Transaction, branchId string) *model.AppError { 589 if _, err := transaction.Exec("DELETE FROM Classes WHERE BranchId = :BranchId", map[string]interface{}{"BranchId": branchId}); err != nil { 590 return model.NewAppError("SqlClassStore.PermanentDeleteByBranch", "store.sql_class.permanent_delete_by_branch.app_error", nil, "branchId="+branchId+", "+err.Error(), http.StatusInternalServerError) 591 } 592 593 return nil 594 } 595 596 // PermanentDelete removes the given class from the database. 597 func (s SqlClassStore) PermanentDelete(classId string) *model.AppError { 598 transaction, err := s.GetMaster().Begin() 599 if err != nil { 600 return model.NewAppError("SqlClassStore.PermanentDelete", "store.sql_class.permanent_delete.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 601 } 602 defer finalizeTransaction(transaction) 603 604 if err := s.permanentDeleteT(transaction, classId); err != nil { 605 return err 606 } 607 608 if err := transaction.Commit(); err != nil { 609 return model.NewAppError("SqlClassStore.PermanentDelete", "store.sql_class.permanent_delete.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 610 } 611 612 return nil 613 } 614 615 func (s SqlClassStore) permanentDeleteT(transaction *gorp.Transaction, classId string) *model.AppError { 616 if _, err := transaction.Exec("DELETE FROM Classes WHERE Id = :ClassId", map[string]interface{}{"ClassId": classId}); err != nil { 617 return model.NewAppError("SqlClassStore.PermanentDelete", "store.sql_class.permanent_delete.app_error", nil, "class_id="+classId+", "+err.Error(), http.StatusInternalServerError) 618 } 619 620 return nil 621 } 622 623 func (s SqlClassStore) PermanentDeleteMembersByClass(classId string) *model.AppError { 624 _, err := s.GetMaster().Exec("DELETE FROM ClassMembers WHERE ClassId = :ClassId", map[string]interface{}{"ClassId": classId}) 625 if err != nil { 626 return model.NewAppError("SqlClassStore.RemoveAllMembersByClass", "store.sql_class.remove_member.app_error", nil, "class_id="+classId+", "+err.Error(), http.StatusInternalServerError) 627 } 628 629 return nil 630 } 631 632 func (s SqlClassStore) GetClasses(branchId string, userId string, includeDeleted bool) (*model.ClassList, *model.AppError) { 633 query := "SELECT Classes.* FROM Classes, ClassMembers WHERE Id = ClassId AND UserId = :UserId AND DeleteAt = 0 AND (BranchId = :BranchId OR BranchId = '') ORDER BY DisplayName" 634 if includeDeleted { 635 query = "SELECT Classes.* FROM Classes, ClassMembers WHERE Id = ClassId AND UserId = :UserId AND (BranchId = :BranchId OR BranchId = '') ORDER BY DisplayName" 636 } 637 classes := &model.ClassList{} 638 _, err := s.GetReplica().Select(classes, query, map[string]interface{}{"BranchId": branchId, "UserId": userId}) 639 640 if err != nil { 641 return nil, model.NewAppError("SqlClassStore.GetClasses", "store.sql_class.get_classes.get.app_error", nil, "branchId="+branchId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 642 } 643 644 if len(*classes) == 0 { 645 return nil, model.NewAppError("SqlClassStore.GetClasses", "store.sql_class.get_classes.not_found.app_error", nil, "branchId="+branchId+", userId="+userId, http.StatusBadRequest) 646 } 647 648 return classes, nil 649 } 650 651 func (s SqlClassStore) GetAllClasses(offset, limit int, opts store.ClassSearchOpts) (*model.ClassListWithBranchData, *model.AppError) { 652 query := s.getAllClassesQuery(opts, false) 653 654 query = query.OrderBy("c.DisplayName, Branches.DisplayName").Limit(uint64(limit)).Offset(uint64(offset)) 655 656 queryString, args, err := query.ToSql() 657 if err != nil { 658 return nil, model.NewAppError("SqlClassStore.GetAllClasses", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError) 659 } 660 661 data := &model.ClassListWithBranchData{} 662 _, err = s.GetReplica().Select(data, queryString, args...) 663 664 if err != nil { 665 return nil, model.NewAppError("SqlClassStore.GetAllClasses", "store.sql_class.get_all_classes.get.app_error", nil, err.Error(), http.StatusInternalServerError) 666 } 667 668 return data, nil 669 } 670 671 func (s SqlClassStore) GetAllClassesCount(opts store.ClassSearchOpts) (int64, *model.AppError) { 672 query := s.getAllClassesQuery(opts, true) 673 674 queryString, args, err := query.ToSql() 675 if err != nil { 676 return 0, model.NewAppError("SqlClassStore.GetAllClassesCount", "store.sql.build_query.app_error", nil, err.Error(), http.StatusInternalServerError) 677 } 678 679 count, err := s.GetReplica().SelectInt(queryString, args...) 680 if err != nil { 681 return 0, model.NewAppError("SqlClassStore.GetAllClassesCount", "store.sql_class.get_all_classes.get.app_error", nil, err.Error(), http.StatusInternalServerError) 682 } 683 684 return count, nil 685 } 686 687 func (s SqlClassStore) getAllClassesQuery(opts store.ClassSearchOpts, forCount bool) sq.SelectBuilder { 688 var selectStr string 689 if forCount { 690 selectStr = "count(c.Id)" 691 } else { 692 selectStr = "c.*, Branches.DisplayName AS BranchDisplayName, Branches.Name AS BranchName, Branches.UpdateAt AS BranchUpdateAt" 693 } 694 695 query := s.getQueryBuilder(). 696 Select(selectStr). 697 From("Classes AS c") 698 699 if !forCount { 700 query = query.Join("Branches ON Branches.Id = c.BranchId") 701 } 702 703 if !opts.IncludeDeleted { 704 query = query.Where(sq.Eq{"c.DeleteAt": int(0)}) 705 } 706 707 if len(opts.ExcludeClassNames) > 0 { 708 query = query.Where(sq.NotEq{"c.Name": opts.ExcludeClassNames}) 709 } 710 711 return query 712 } 713 714 func (s SqlClassStore) GetMoreClasses(branchId string, userId string, offset int, limit int) (*model.ClassList, *model.AppError) { 715 classes := &model.ClassList{} 716 _, err := s.GetReplica().Select(classes, ` 717 SELECT 718 c.* 719 FROM 720 Classes c 721 WHERE 722 c.BranchId = :BranchId 723 AND c.DeleteAt = 0 724 ORDER BY 725 c.DisplayName 726 LIMIT :Limit 727 OFFSET :Offset 728 `, map[string]interface{}{ 729 "BranchId": branchId, 730 "UserId": userId, 731 "Limit": limit, 732 "Offset": offset, 733 }) 734 735 if err != nil { 736 return nil, model.NewAppError("SqlClassStore.GetMoreClasses", "store.sql_class.get_more_classes.get.app_error", nil, "branchId="+branchId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 737 } 738 739 return classes, nil 740 } 741 742 func (s SqlClassStore) GetPublicClassesForBranch(branchId string, offset int, limit int) (*model.ClassList, *model.AppError) { 743 classes := &model.ClassList{} 744 _, err := s.GetReplica().Select(classes, ` 745 SELECT 746 c.* 747 FROM 748 Classes c 749 WHERE 750 c.BranchId = :BranchId 751 AND c.DeleteAt = 0 752 ORDER BY c.DisplayName 753 LIMIT :Limit 754 OFFSET :Offset 755 `, map[string]interface{}{ 756 "BranchId": branchId, 757 "Limit": limit, 758 "Offset": offset, 759 }) 760 761 if err != nil { 762 return nil, model.NewAppError("SqlClassStore.GetPublicClassesForBranch", "store.sql_class.get_public_classes.get.app_error", nil, "branchId="+branchId+", err="+err.Error(), http.StatusInternalServerError) 763 } 764 765 return classes, nil 766 } 767 768 type classIdWithCountAndUpdateAt struct { 769 Id string 770 TotalMsgCount int64 771 UpdateAt int64 772 } 773 774 func (s SqlClassStore) GetBranchClasses(branchId string) (*model.ClassList, *model.AppError) { 775 data := &model.ClassList{} 776 _, err := s.GetReplica().Select(data, "SELECT * FROM Classes WHERE BranchId = :BranchId ORDER BY DisplayName", map[string]interface{}{"BranchId": branchId}) 777 778 if err != nil { 779 return nil, model.NewAppError("SqlClassStore.GetBranchClasses", "store.sql_class.get_classes.get.app_error", nil, "branchId="+branchId+", err="+err.Error(), http.StatusInternalServerError) 780 } 781 782 if len(*data) == 0 { 783 return nil, model.NewAppError("SqlClassStore.GetBranchClasses", "store.sql_class.get_classes.not_found.app_error", nil, "branchId="+branchId, http.StatusNotFound) 784 } 785 786 return data, nil 787 } 788 789 func (s SqlClassStore) GetByName(branchId string, name string, allowFromCache bool) (*model.Class, *model.AppError) { 790 return s.getByName(branchId, name, false, allowFromCache) 791 } 792 793 func (s SqlClassStore) GetByNames(branchId string, names []string, allowFromCache bool) ([]*model.Class, *model.AppError) { 794 var classes []*model.Class 795 796 if allowFromCache { 797 var misses []string 798 visited := make(map[string]struct{}) 799 for _, name := range names { 800 if _, ok := visited[name]; ok { 801 continue 802 } 803 visited[name] = struct{}{} 804 if cacheItem, ok := classByNameCache.Get(branchId + name); ok { 805 classes = append(classes, cacheItem.(*model.Class)) 806 } else { 807 misses = append(misses, name) 808 } 809 } 810 names = misses 811 } 812 813 if len(names) > 0 { 814 props := map[string]interface{}{} 815 var namePlaceholders []string 816 for _, name := range names { 817 key := fmt.Sprintf("Name%v", len(namePlaceholders)) 818 props[key] = name 819 namePlaceholders = append(namePlaceholders, ":"+key) 820 } 821 822 var query string 823 if branchId == "" { 824 query = `SELECT * FROM Classes WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND DeleteAt = 0` 825 } else { 826 props["BranchId"] = branchId 827 query = `SELECT * FROM Classes WHERE Name IN (` + strings.Join(namePlaceholders, ", ") + `) AND BranchId = :BranchId AND DeleteAt = 0` 828 } 829 830 var dbClasses []*model.Class 831 if _, err := s.GetReplica().Select(&dbClasses, query, props); err != nil && err != sql.ErrNoRows { 832 return nil, model.NewAppError("SqlClassStore.GetByName", "store.sql_class.get_by_name.existing.app_error", nil, "branchId="+branchId+", "+err.Error(), http.StatusInternalServerError) 833 } 834 for _, class := range dbClasses { 835 classByNameCache.AddWithExpiresInSecs(branchId+class.Name, class, CLASS_CACHE_SEC) 836 classes = append(classes, class) 837 } 838 // Not all classes are in cache. Increment aggregate miss counter. 839 if s.metrics != nil { 840 s.metrics.IncrementMemCacheMissCounter("Class By Name - Aggregate") 841 } 842 } else { 843 // All of the class names are in cache. Increment aggregate hit counter. 844 if s.metrics != nil { 845 s.metrics.IncrementMemCacheHitCounter("Class By Name - Aggregate") 846 } 847 } 848 849 return classes, nil 850 } 851 852 func (s SqlClassStore) GetByNameIncludeDeleted(branchId string, name string, allowFromCache bool) (*model.Class, *model.AppError) { 853 return s.getByName(branchId, name, true, allowFromCache) 854 } 855 856 func (s SqlClassStore) getByName(branchId string, name string, includeDeleted bool, allowFromCache bool) (*model.Class, *model.AppError) { 857 var query string 858 if includeDeleted { 859 query = "SELECT * FROM Classes WHERE (BranchId = :BranchId OR BranchId = '') AND Name = :Name" 860 } else { 861 query = "SELECT * FROM Classes WHERE (BranchId = :BranchId OR BranchId = '') AND Name = :Name AND DeleteAt = 0" 862 } 863 class := model.Class{} 864 865 if allowFromCache { 866 if cacheItem, ok := classByNameCache.Get(branchId + name); ok { 867 if s.metrics != nil { 868 s.metrics.IncrementMemCacheHitCounter("Class By Name") 869 } 870 return cacheItem.(*model.Class), nil 871 } 872 if s.metrics != nil { 873 s.metrics.IncrementMemCacheMissCounter("Class By Name") 874 } 875 } 876 877 if err := s.GetReplica().SelectOne(&class, query, map[string]interface{}{"BranchId": branchId, "Name": name}); err != nil { 878 if err == sql.ErrNoRows { 879 return nil, model.NewAppError("SqlClassStore.GetByName", store.MISSING_CLASS_ERROR, nil, "branchId="+branchId+", "+"name="+name+"", http.StatusNotFound) 880 } 881 return nil, model.NewAppError("SqlClassStore.GetByName", "store.sql_class.get_by_name.existing.app_error", nil, "branchId="+branchId+", "+"name="+name+", "+err.Error(), http.StatusInternalServerError) 882 } 883 884 classByNameCache.AddWithExpiresInSecs(branchId+name, &class, CLASS_CACHE_SEC) 885 return &class, nil 886 } 887 888 func (s SqlClassStore) GetDeletedByName(branchId string, name string) (*model.Class, *model.AppError) { 889 class := model.Class{} 890 891 if err := s.GetReplica().SelectOne(&class, "SELECT * FROM Classes WHERE (BranchId = :BranchId OR BranchId = '') AND Name = :Name AND DeleteAt != 0", map[string]interface{}{"BranchId": branchId, "Name": name}); err != nil { 892 if err == sql.ErrNoRows { 893 return nil, model.NewAppError("SqlClassStore.GetDeletedByName", "store.sql_class.get_deleted_by_name.missing.app_error", nil, "branchId="+branchId+", "+"name="+name+", "+err.Error(), http.StatusNotFound) 894 } 895 return nil, model.NewAppError("SqlClassStore.GetDeletedByName", "store.sql_class.get_deleted_by_name.existing.app_error", nil, "branchId="+branchId+", "+"name="+name+", "+err.Error(), http.StatusInternalServerError) 896 } 897 898 return &class, nil 899 } 900 901 func (s SqlClassStore) GetDeleted(branchId string, offset int, limit int, userId string) (*model.ClassList, *model.AppError) { 902 classes := &model.ClassList{} 903 904 query := ` 905 SELECT * FROM Classes 906 WHERE (BranchId = :BranchId OR BranchId = '') 907 AND DeleteAt != 0 908 UNION 909 SELECT * FROM Classes 910 WHERE (BranchId = :BranchId OR BranchId = '') 911 AND DeleteAt != 0 912 AND Id IN (SELECT ClassId FROM ClassMembers WHERE UserId = :UserId) 913 ORDER BY DisplayName LIMIT :Limit OFFSET :Offset 914 ` 915 916 if _, err := s.GetReplica().Select(classes, query, map[string]interface{}{"BranchId": branchId, "Limit": limit, "Offset": offset, "UserId": userId}); err != nil { 917 if err == sql.ErrNoRows { 918 return nil, model.NewAppError("SqlClassStore.GetDeleted", "store.sql_class.get_deleted.missing.app_error", nil, "branchId="+branchId+", "+err.Error(), http.StatusNotFound) 919 } 920 return nil, model.NewAppError("SqlClassStore.GetDeleted", "store.sql_class.get_deleted.existing.app_error", nil, "branchId="+branchId+", "+err.Error(), http.StatusInternalServerError) 921 } 922 923 return classes, nil 924 } 925 926 var CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY = ` 927 SELECT 928 ClassMembers.*, 929 BranchScheme.DefaultClassUserRole BranchSchemeDefaultUserRole, 930 BranchScheme.DefaultClassAdminRole BranchSchemeDefaultAdminRole, 931 ClassScheme.DefaultClassUserRole ClassSchemeDefaultUserRole, 932 ClassScheme.DefaultClassAdminRole ClassSchemeDefaultAdminRole 933 FROM 934 ClassMembers 935 INNER JOIN 936 Classes ON ClassMembers.ClassId = Classes.Id 937 LEFT JOIN 938 Schemes ClassScheme ON Classes.SchemeId = ClassScheme.Id 939 LEFT JOIN 940 Branches ON Classes.BranchId = Branches.Id 941 LEFT JOIN 942 Schemes BranchScheme ON Branches.SchemeId = BranchScheme.Id 943 ` 944 945 func (s SqlClassStore) SaveMultipleMembers(members []*model.ClassMember) ([]*model.ClassMember, *model.AppError) { 946 for _, member := range members { 947 defer s.InvalidateAllClassMembersForUser(member.UserId) 948 } 949 950 transaction, err := s.GetMaster().Begin() 951 if err != nil { 952 return nil, model.NewAppError("SqlClassStore.SaveMember", "store.sql_class.save_member.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 953 } 954 defer finalizeTransaction(transaction) 955 956 newMembers, appErr := s.saveMultipleMembersT(transaction, members) 957 if appErr != nil { 958 return nil, appErr 959 } 960 961 if err := transaction.Commit(); err != nil { 962 return nil, model.NewAppError("SqlClassStore.SaveMember", "store.sql_class.save_member.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 963 } 964 965 return newMembers, nil 966 } 967 968 func (s SqlClassStore) SaveMember(member *model.ClassMember) (*model.ClassMember, *model.AppError) { 969 newMembers, appErr := s.SaveMultipleMembers([]*model.ClassMember{member}) 970 if appErr != nil { 971 return nil, appErr 972 } 973 return newMembers[0], nil 974 } 975 976 func (s SqlClassStore) saveMultipleMembersT(transaction *gorp.Transaction, members []*model.ClassMember) ([]*model.ClassMember, *model.AppError) { 977 newClassMembers := map[string]int{} 978 users := map[string]bool{} 979 for _, member := range members { 980 if val, ok := newClassMembers[member.ClassId]; val < 1 || !ok { 981 newClassMembers[member.ClassId] = 1 982 } else { 983 newClassMembers[member.ClassId]++ 984 } 985 users[member.UserId] = true 986 987 member.PreSave() 988 if err := member.IsValid(); err != nil { 989 return nil, err 990 } 991 } 992 993 classes := []string{} 994 for class := range newClassMembers { 995 classes = append(classes, class) 996 } 997 998 defaultClassRolesByClass := map[string]struct { 999 Id string 1000 User sql.NullString 1001 Admin sql.NullString 1002 }{} 1003 1004 classRolesQuery := s.getQueryBuilder(). 1005 Select( 1006 "Classes.Id as Id", 1007 "ClassScheme.DefaultClassUserRole as User", 1008 "ClassScheme.DefaultClassAdminRole as Admin", 1009 ). 1010 From("Classes"). 1011 LeftJoin("Schemes ClassScheme ON Classes.SchemeId = ClassScheme.Id"). 1012 Where(sq.Eq{"Classes.Id": classes}) 1013 1014 classRolesSql, classRolesArgs, err := classRolesQuery.ToSql() 1015 if err != nil { 1016 return nil, model.NewAppError("SqlClassStore.SaveMultipleMembers", "store.sql_class.save_multimple_members.class_roles.app_error", nil, err.Error(), http.StatusInternalServerError) 1017 } 1018 1019 var defaultClassesRoles []struct { 1020 Id string 1021 User sql.NullString 1022 Admin sql.NullString 1023 } 1024 _, err = s.GetMaster().Select(&defaultClassesRoles, classRolesSql, classRolesArgs...) 1025 if err != nil { 1026 return nil, model.NewAppError("SqlClassStore.SaveMultipleMembers", "store.sql_class.save_multimple_members.class_roles_query.app_error", nil, err.Error(), http.StatusInternalServerError) 1027 } 1028 1029 for _, defaultRoles := range defaultClassesRoles { 1030 defaultClassRolesByClass[defaultRoles.Id] = defaultRoles 1031 } 1032 1033 defaultBranchRolesByClass := map[string]struct { 1034 Id string 1035 User sql.NullString 1036 Admin sql.NullString 1037 }{} 1038 1039 branchRolesQuery := s.getQueryBuilder(). 1040 Select( 1041 "Classes.Id as Id", 1042 "BranchScheme.DefaultClassUserRole as User", 1043 "BranchScheme.DefaultClassAdminRole as Admin", 1044 ). 1045 From("Classes"). 1046 LeftJoin("Branches ON Branches.Id = Classes.BranchId"). 1047 LeftJoin("Schemes BranchScheme ON Branches.SchemeId = BranchScheme.Id"). 1048 Where(sq.Eq{"Classes.Id": classes}) 1049 1050 branchRolesSql, branchRolesArgs, err := branchRolesQuery.ToSql() 1051 if err != nil { 1052 return nil, model.NewAppError("SqlClassStore.SaveMultipleMembers", "store.sql_class.save_multimple_members.branch_roles.app_error", nil, err.Error(), http.StatusInternalServerError) 1053 } 1054 1055 var defaultBranchesRoles []struct { 1056 Id string 1057 User sql.NullString 1058 Admin sql.NullString 1059 } 1060 _, err = s.GetMaster().Select(&defaultBranchesRoles, branchRolesSql, branchRolesArgs...) 1061 if err != nil { 1062 return nil, model.NewAppError("SqlClassStore.SaveMultipleMembers", "store.sql_class.save_multimple_members.branch_roles_query.app_error", nil, err.Error(), http.StatusInternalServerError) 1063 } 1064 1065 for _, defaultRoles := range defaultBranchesRoles { 1066 defaultBranchRolesByClass[defaultRoles.Id] = defaultRoles 1067 } 1068 1069 query := s.getQueryBuilder().Insert("ClassMembers").Columns(classMemberSliceColumns()...) 1070 for _, member := range members { 1071 query = query.Values(classMemberToSlice(member)...) 1072 } 1073 1074 sql, args, err := query.ToSql() 1075 if err != nil { 1076 return nil, model.NewAppError("SqlBranchStore.SaveMember", "store.sql_class.save_member.save.app_error", nil, err.Error(), http.StatusInternalServerError) 1077 } 1078 1079 if _, err := s.GetMaster().Exec(sql, args...); err != nil { 1080 if IsUniqueConstraintError(err, []string{"ClassId", "classmembers_pkey", "PRIMARY"}) { 1081 return nil, model.NewAppError("SqlBranchStore.SaveMember", "store.sql_class.save_member.exists.app_error", nil, err.Error(), http.StatusBadRequest) 1082 } 1083 return nil, model.NewAppError("SqlBranchStore.SaveMember", "store.sql_class.save_member.save.app_error", nil, err.Error(), http.StatusInternalServerError) 1084 } 1085 1086 newMembers := []*model.ClassMember{} 1087 for _, member := range members { 1088 defaultBranchUserRole := defaultBranchRolesByClass[member.ClassId].User.String 1089 defaultBranchAdminRole := defaultBranchRolesByClass[member.ClassId].Admin.String 1090 defaultClassUserRole := defaultClassRolesByClass[member.ClassId].User.String 1091 defaultClassAdminRole := defaultClassRolesByClass[member.ClassId].Admin.String 1092 rolesResult := getClassRoles( 1093 member.SchemeUser, member.SchemeAdmin, 1094 defaultBranchUserRole, defaultBranchAdminRole, 1095 defaultClassUserRole, defaultClassAdminRole, 1096 strings.Fields(member.ExplicitRoles), 1097 ) 1098 newMember := *member 1099 newMember.SchemeUser = rolesResult.schemeUser 1100 newMember.SchemeAdmin = rolesResult.schemeAdmin 1101 newMember.Roles = strings.Join(rolesResult.roles, " ") 1102 newMember.ExplicitRoles = strings.Join(rolesResult.explicitRoles, " ") 1103 newMembers = append(newMembers, &newMember) 1104 } 1105 return newMembers, nil 1106 } 1107 1108 func (s SqlClassStore) saveMemberT(transaction *gorp.Transaction, member *model.ClassMember) (*model.ClassMember, *model.AppError) { 1109 members, err := s.saveMultipleMembersT(transaction, []*model.ClassMember{member}) 1110 if err != nil { 1111 return nil, err 1112 } 1113 return members[0], nil 1114 } 1115 1116 func (s SqlClassStore) UpdateMultipleMembers(members []*model.ClassMember) ([]*model.ClassMember, *model.AppError) { 1117 for _, member := range members { 1118 member.PreUpdate() 1119 1120 if err := member.IsValid(); err != nil { 1121 return nil, err 1122 } 1123 } 1124 1125 var transaction *gorp.Transaction 1126 var err error 1127 1128 if transaction, err = s.GetMaster().Begin(); err != nil { 1129 return nil, model.NewAppError("SqlClassStore.MigrateClassMembers", "store.sql_class.migrate_class_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1130 } 1131 defer finalizeTransaction(transaction) 1132 1133 updatedMembers := []*model.ClassMember{} 1134 for _, member := range members { 1135 if _, err := transaction.Update(NewClassMemberFromModel(member)); err != nil { 1136 return nil, model.NewAppError("SqlClassStore.UpdateMember", "store.sql_class.update_member.app_error", nil, "class_id="+member.ClassId+", "+"user_id="+member.UserId+", "+err.Error(), http.StatusInternalServerError) 1137 } 1138 1139 // TODO: Get this out of the transaction when is possible 1140 var dbMember classMemberWithSchemeRoles 1141 if err := transaction.SelectOne(&dbMember, CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ClassMembers.ClassId = :ClassId AND ClassMembers.UserId = :UserId", map[string]interface{}{"ClassId": member.ClassId, "UserId": member.UserId}); err != nil { 1142 if err == sql.ErrNoRows { 1143 return nil, model.NewAppError("SqlClassStore.GetMember", store.MISSING_CLASS_MEMBER_ERROR, nil, "class_id="+member.ClassId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound) 1144 } 1145 return nil, model.NewAppError("SqlClassStore.GetMember", "store.sql_class.get_member.app_error", nil, "class_id="+member.ClassId+"user_id="+member.UserId+","+err.Error(), http.StatusInternalServerError) 1146 } 1147 updatedMembers = append(updatedMembers, dbMember.ToModel()) 1148 } 1149 1150 if err := transaction.Commit(); err != nil { 1151 return nil, model.NewAppError("SqlClassStore.MigrateClassMembers", "store.sql_class.migrate_class_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1152 } 1153 return updatedMembers, nil 1154 } 1155 1156 func (s SqlClassStore) UpdateMember(member *model.ClassMember) (*model.ClassMember, *model.AppError) { 1157 updatedMembers, err := s.UpdateMultipleMembers([]*model.ClassMember{member}) 1158 if err != nil { 1159 return nil, err 1160 } 1161 return updatedMembers[0], nil 1162 } 1163 1164 func (s SqlClassStore) GetMembers(classId string, offset, limit int) (*model.ClassMembers, *model.AppError) { 1165 var dbMembers classMemberWithSchemeRolesList 1166 _, err := s.GetReplica().Select(&dbMembers, CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ClassId = :ClassId LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ClassId": classId, "Limit": limit, "Offset": offset}) 1167 if err != nil { 1168 return nil, model.NewAppError("SqlClassStore.GetMembers", "store.sql_class.get_members.app_error", nil, "class_id="+classId+","+err.Error(), http.StatusInternalServerError) 1169 } 1170 1171 return dbMembers.ToModel(), nil 1172 } 1173 1174 func (s SqlClassStore) GetClassMembersTimezones(classId string) ([]model.StringMap, *model.AppError) { 1175 var dbMembersTimezone []model.StringMap 1176 _, err := s.GetReplica().Select(&dbMembersTimezone, ` 1177 SELECT 1178 Users.Timezone 1179 FROM 1180 ClassMembers 1181 LEFT JOIN 1182 Users ON ClassMembers.UserId = Id 1183 WHERE ClassId = :ClassId 1184 `, map[string]interface{}{"ClassId": classId}) 1185 1186 if err != nil { 1187 return nil, model.NewAppError("SqlClassStore.GetClassMembersTimezones", "store.sql_class.get_members.app_error", nil, "class_id="+classId+","+err.Error(), http.StatusInternalServerError) 1188 } 1189 1190 return dbMembersTimezone, nil 1191 } 1192 1193 func (s SqlClassStore) GetMember(classId string, userId string) (*model.ClassMember, *model.AppError) { 1194 var dbMember classMemberWithSchemeRoles 1195 1196 if err := s.GetReplica().SelectOne(&dbMember, CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ClassMembers.ClassId = :ClassId AND ClassMembers.UserId = :UserId", map[string]interface{}{"ClassId": classId, "UserId": userId}); err != nil { 1197 if err == sql.ErrNoRows { 1198 return nil, model.NewAppError("SqlClassStore.GetMember", store.MISSING_CLASS_MEMBER_ERROR, nil, "class_id="+classId+"user_id="+userId+","+err.Error(), http.StatusNotFound) 1199 } 1200 return nil, model.NewAppError("SqlClassStore.GetMember", "store.sql_class.get_member.app_error", nil, "class_id="+classId+"user_id="+userId+","+err.Error(), http.StatusInternalServerError) 1201 } 1202 1203 return dbMember.ToModel(), nil 1204 } 1205 1206 func (s SqlClassStore) InvalidateAllClassMembersForUser(userId string) { 1207 allClassMembersForUserCache.Remove(userId) 1208 allClassMembersForUserCache.Remove(userId + "_deleted") 1209 if s.metrics != nil { 1210 s.metrics.IncrementMemCacheInvalidationCounter("All Class Members for User - Remove by UserId") 1211 } 1212 } 1213 1214 func (s SqlClassStore) IsUserInClassUseCache(userId string, classId string) bool { 1215 if cacheItem, ok := allClassMembersForUserCache.Get(userId); ok { 1216 if s.metrics != nil { 1217 s.metrics.IncrementMemCacheHitCounter("All Class Members for User") 1218 } 1219 ids := cacheItem.(map[string]string) 1220 if _, ok := ids[classId]; ok { 1221 return true 1222 } 1223 return false 1224 } 1225 1226 if s.metrics != nil { 1227 s.metrics.IncrementMemCacheMissCounter("All Class Members for User") 1228 } 1229 1230 ids, err := s.GetAllClassMembersForUser(userId, true, false) 1231 if err != nil { 1232 mlog.Error("Error getting all class members for user", mlog.Err(err)) 1233 return false 1234 } 1235 1236 if _, ok := ids[classId]; ok { 1237 return true 1238 } 1239 1240 return false 1241 } 1242 1243 func (s SqlClassStore) GetMemberForPost(postId string, userId string) (*model.ClassMember, *model.AppError) { 1244 var dbMember classMemberWithSchemeRoles 1245 query := ` 1246 SELECT 1247 ClassMembers.*, 1248 BranchScheme.DefaultClassUserRole BranchSchemeDefaultUserRole, 1249 BranchScheme.DefaultClassAdminRole BranchSchemeDefaultAdminRole, 1250 ClassScheme.DefaultClassUserRole ClassSchemeDefaultUserRole, 1251 ClassScheme.DefaultClassAdminRole ClassSchemeDefaultAdminRole 1252 FROM 1253 ClassMembers 1254 INNER JOIN 1255 Posts ON ClassMembers.ClassId = Posts.ClassId 1256 INNER JOIN 1257 Classes ON ClassMembers.ClassId = Classes.Id 1258 LEFT JOIN 1259 Schemes ClassScheme ON Classes.SchemeId = ClassScheme.Id 1260 LEFT JOIN 1261 Branches ON Classes.BranchId = Branches.Id 1262 LEFT JOIN 1263 Schemes BranchScheme ON Branches.SchemeId = BranchScheme.Id 1264 WHERE 1265 ClassMembers.UserId = :UserId 1266 AND 1267 Posts.Id = :PostId` 1268 if err := s.GetReplica().SelectOne(&dbMember, query, map[string]interface{}{"UserId": userId, "PostId": postId}); err != nil { 1269 return nil, model.NewAppError("SqlClassStore.GetMemberForPost", "store.sql_class.get_member_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) 1270 } 1271 return dbMember.ToModel(), nil 1272 } 1273 1274 func (s SqlClassStore) GetForPost(postId string) (*model.Class, *model.AppError) { 1275 class := &model.Class{} 1276 if err := s.GetReplica().SelectOne( 1277 class, 1278 `SELECT 1279 Classes.* 1280 FROM 1281 Classes, 1282 Posts 1283 WHERE 1284 Classes.Id = Posts.ClassId 1285 AND Posts.Id = :PostId`, map[string]interface{}{"PostId": postId}); err != nil { 1286 return nil, model.NewAppError("SqlClassStore.GetForPost", "store.sql_class.get_for_post.app_error", nil, "postId="+postId+", err="+err.Error(), http.StatusInternalServerError) 1287 1288 } 1289 return class, nil 1290 } 1291 1292 func (s SqlClassStore) GetAllClassMembersForUser(userId string, allowFromCache bool, includeDeleted bool) (map[string]string, *model.AppError) { 1293 cache_key := userId 1294 if includeDeleted { 1295 cache_key += "_deleted" 1296 } 1297 if allowFromCache { 1298 if cacheItem, ok := allClassMembersForUserCache.Get(cache_key); ok { 1299 if s.metrics != nil { 1300 s.metrics.IncrementMemCacheHitCounter("All Class Members for User") 1301 } 1302 ids := cacheItem.(map[string]string) 1303 return ids, nil 1304 } 1305 } 1306 1307 if s.metrics != nil { 1308 s.metrics.IncrementMemCacheMissCounter("All Class Members for User") 1309 } 1310 1311 var deletedClause string 1312 if !includeDeleted { 1313 deletedClause = "Classes.DeleteAt = 0 AND" 1314 } 1315 1316 var data allClassMembers 1317 _, err := s.GetReplica().Select(&data, ` 1318 SELECT 1319 ClassMembers.ClassId, ClassMembers.Roles, 1320 ClassMembers.SchemeUser, ClassMembers.SchemeAdmin, 1321 BranchScheme.DefaultClassUserRole BranchSchemeDefaultUserRole, 1322 BranchScheme.DefaultClassAdminRole BranchSchemeDefaultAdminRole, 1323 ClassScheme.DefaultClassUserRole ClassSchemeDefaultUserRole, 1324 ClassScheme.DefaultClassAdminRole ClassSchemeDefaultAdminRole 1325 FROM 1326 ClassMembers 1327 INNER JOIN 1328 Classes ON ClassMembers.ClassId = Classes.Id 1329 LEFT JOIN 1330 Schemes ClassScheme ON Classes.SchemeId = ClassScheme.Id 1331 LEFT JOIN 1332 Branches ON Classes.BranchId = Branches.Id 1333 LEFT JOIN 1334 Schemes BranchScheme ON Branches.SchemeId = BranchScheme.Id 1335 WHERE 1336 `+deletedClause+` 1337 ClassMembers.UserId = :UserId`, map[string]interface{}{"UserId": userId}) 1338 1339 if err != nil { 1340 return nil, model.NewAppError("SqlClassStore.GetAllClassMembersForUser", "store.sql_class.get_classes.get.app_error", nil, "userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 1341 } 1342 1343 ids := data.ToMapStringString() 1344 1345 if allowFromCache { 1346 allClassMembersForUserCache.AddWithExpiresInSecs(cache_key, ids, ALL_CLASS_MEMBERS_FOR_USER_CACHE_SEC) 1347 } 1348 return ids, nil 1349 } 1350 1351 func (s SqlClassStore) InvalidateCacheForClassMembersNotifyProps(classId string) { 1352 allClassMembersNotifyPropsForClassCache.Remove(classId) 1353 if s.metrics != nil { 1354 s.metrics.IncrementMemCacheInvalidationCounter("All Class Members Notify Props for Class - Remove by ClassId") 1355 } 1356 } 1357 1358 type allClassMemberNotifyProps struct { 1359 UserId string 1360 NotifyProps model.StringMap 1361 } 1362 1363 func (s SqlClassStore) GetAllClassMembersNotifyPropsForClass(classId string, allowFromCache bool) (map[string]model.StringMap, *model.AppError) { 1364 if allowFromCache { 1365 if cacheItem, ok := allClassMembersNotifyPropsForClassCache.Get(classId); ok { 1366 if s.metrics != nil { 1367 s.metrics.IncrementMemCacheHitCounter("All Class Members Notify Props for Class") 1368 } 1369 return cacheItem.(map[string]model.StringMap), nil 1370 } 1371 } 1372 1373 if s.metrics != nil { 1374 s.metrics.IncrementMemCacheMissCounter("All Class Members Notify Props for Class") 1375 } 1376 1377 var data []allClassMemberNotifyProps 1378 _, err := s.GetReplica().Select(&data, ` 1379 SELECT UserId, NotifyProps 1380 FROM ClassMembers 1381 WHERE ClassId = :ClassId`, map[string]interface{}{"ClassId": classId}) 1382 1383 if err != nil { 1384 return nil, model.NewAppError("SqlClassStore.GetAllClassMembersPropsForClass", "store.sql_class.get_members.app_error", nil, "classId="+classId+", err="+err.Error(), http.StatusInternalServerError) 1385 } 1386 1387 props := make(map[string]model.StringMap) 1388 for i := range data { 1389 props[data[i].UserId] = data[i].NotifyProps 1390 } 1391 1392 allClassMembersNotifyPropsForClassCache.AddWithExpiresInSecs(classId, props, ALL_CLASS_MEMBERS_NOTIFY_PROPS_FOR_CLASS_CACHE_SEC) 1393 1394 return props, nil 1395 } 1396 1397 func (s SqlClassStore) InvalidateMemberCount(classId string) { 1398 } 1399 1400 func (s SqlClassStore) GetMemberCountFromCache(classId string) int64 { 1401 count, _ := s.GetMemberCount(classId, true) 1402 return count 1403 } 1404 1405 func (s SqlClassStore) GetMemberCount(classId string, allowFromCache bool) (int64, *model.AppError) { 1406 count, err := s.GetReplica().SelectInt(` 1407 SELECT 1408 count(*) 1409 FROM 1410 ClassMembers, 1411 Users 1412 WHERE 1413 ClassMembers.UserId = Users.Id 1414 AND ClassMembers.ClassId = :ClassId 1415 AND Users.DeleteAt = 0`, map[string]interface{}{"ClassId": classId}) 1416 if err != nil { 1417 return 0, model.NewAppError("SqlClassStore.GetMemberCount", "store.sql_class.get_member_count.app_error", nil, "class_id="+classId+", "+err.Error(), http.StatusInternalServerError) 1418 } 1419 1420 return count, nil 1421 } 1422 1423 func (s SqlClassStore) InvalidatePinnedPostCount(classId string) { 1424 } 1425 func (s SqlClassStore) InvalidateGuestCount(classId string) { 1426 } 1427 1428 func (s SqlClassStore) RemoveMembers(classId string, userIds []string) *model.AppError { 1429 query := s.getQueryBuilder(). 1430 Delete("ClassMembers"). 1431 Where(sq.Eq{"ClassId": classId}). 1432 Where(sq.Eq{"UserId": userIds}) 1433 sql, args, err := query.ToSql() 1434 if err != nil { 1435 return model.NewAppError("SqlClassStore.RemoveMember", "store.sql_class.remove_member.app_error", nil, "class_id="+classId+", "+err.Error(), http.StatusInternalServerError) 1436 } 1437 _, err = s.GetMaster().Exec(sql, args...) 1438 if err != nil { 1439 return model.NewAppError("SqlClassStore.RemoveMember", "store.sql_class.remove_member.app_error", nil, "class_id="+classId+", "+err.Error(), http.StatusInternalServerError) 1440 } 1441 return nil 1442 } 1443 1444 func (s SqlClassStore) RemoveMember(classId string, userId string) *model.AppError { 1445 return s.RemoveMembers(classId, []string{userId}) 1446 } 1447 1448 func (s SqlClassStore) RemoveAllDeactivatedMembers(classId string) *model.AppError { 1449 query := ` 1450 DELETE 1451 FROM 1452 ClassMembers 1453 WHERE 1454 UserId IN ( 1455 SELECT 1456 Id 1457 FROM 1458 Users 1459 WHERE 1460 Users.DeleteAt != 0 1461 ) 1462 AND 1463 ClassMembers.ClassId = :ClassId 1464 ` 1465 1466 _, err := s.GetMaster().Exec(query, map[string]interface{}{"ClassId": classId}) 1467 if err != nil { 1468 return model.NewAppError("SqlClassStore.RemoveAllDeactivatedMembers", "store.sql_class.remove_all_deactivated_members.app_error", nil, "class_id="+classId+", "+err.Error(), http.StatusInternalServerError) 1469 } 1470 return nil 1471 } 1472 1473 func (s SqlClassStore) PermanentDeleteMembersByUser(userId string) *model.AppError { 1474 if _, err := s.GetMaster().Exec("DELETE FROM ClassMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { 1475 return model.NewAppError("SqlClassStore.ClassPermanentDeleteMembersByUser", "store.sql_class.permanent_delete_members_by_user.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError) 1476 } 1477 return nil 1478 } 1479 1480 func (s SqlClassStore) GetAll(branchId string) ([]*model.Class, *model.AppError) { 1481 var data []*model.Class 1482 _, err := s.GetReplica().Select(&data, "SELECT * FROM Classes WHERE BranchId = :BranchId ORDER BY Name", map[string]interface{}{"BranchId": branchId}) 1483 1484 if err != nil { 1485 return nil, model.NewAppError("SqlClassStore.GetAll", "store.sql_class.get_all.app_error", nil, "branchId="+branchId+", err="+err.Error(), http.StatusInternalServerError) 1486 } 1487 1488 return data, nil 1489 } 1490 1491 func (s SqlClassStore) GetClassesByIds(classIds []string, includeDeleted bool) ([]*model.Class, *model.AppError) { 1492 keys, params := MapStringsToQueryParams(classIds, "Class") 1493 query := `SELECT * FROM Classes WHERE Id IN ` + keys + ` ORDER BY Name` 1494 if !includeDeleted { 1495 query = `SELECT * FROM Classes WHERE DeleteAt=0 AND Id IN ` + keys + ` ORDER BY Name` 1496 } 1497 1498 var classes []*model.Class 1499 _, err := s.GetReplica().Select(&classes, query, params) 1500 1501 if err != nil { 1502 mlog.Error("Query error getting classes by ids", mlog.Err(err)) 1503 return nil, model.NewAppError("SqlClassStore.GetClassesByIds", "store.sql_class.get_classes_by_ids.app_error", nil, "", http.StatusInternalServerError) 1504 } 1505 return classes, nil 1506 } 1507 1508 func (s SqlClassStore) GetMembersForUser(branchId string, userId string) (*model.ClassMembers, *model.AppError) { 1509 var dbMembers classMemberWithSchemeRolesList 1510 _, err := s.GetReplica().Select(&dbMembers, CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ClassMembers.UserId = :UserId AND (Branches.Id = :BranchId OR Branches.Id = '' OR Branches.Id IS NULL)", map[string]interface{}{"BranchId": branchId, "UserId": userId}) 1511 if err != nil { 1512 return nil, model.NewAppError("SqlClassStore.GetMembersForUser", "store.sql_class.get_members.app_error", nil, "branchId="+branchId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 1513 } 1514 1515 return dbMembers.ToModel(), nil 1516 } 1517 1518 func (s SqlClassStore) GetMembersForUserWithPagination(branchId, userId string, page, perPage int) (*model.ClassMembers, *model.AppError) { 1519 var dbMembers classMemberWithSchemeRolesList 1520 offset := page * perPage 1521 _, err := s.GetReplica().Select(&dbMembers, CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ClassMembers.UserId = :UserId Limit :Limit Offset :Offset", map[string]interface{}{"BranchId": branchId, "UserId": userId, "Limit": perPage, "Offset": offset}) 1522 1523 if err != nil { 1524 return nil, model.NewAppError("SqlClassStore.GetMembersForUserWithPagination", "store.sql_class.get_members.app_error", nil, "branchId="+branchId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 1525 } 1526 1527 return dbMembers.ToModel(), nil 1528 } 1529 1530 func (s SqlClassStore) GetMembersByIds(classId string, userIds []string) (*model.ClassMembers, *model.AppError) { 1531 var dbMembers classMemberWithSchemeRolesList 1532 props := make(map[string]interface{}) 1533 idQuery := "" 1534 1535 for index, userId := range userIds { 1536 if len(idQuery) > 0 { 1537 idQuery += ", " 1538 } 1539 1540 props["userId"+strconv.Itoa(index)] = userId 1541 idQuery += ":userId" + strconv.Itoa(index) 1542 } 1543 1544 props["ClassId"] = classId 1545 1546 if _, err := s.GetReplica().Select(&dbMembers, CLASS_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE ClassMembers.ClassId = :ClassId AND ClassMembers.UserId IN ("+idQuery+")", props); err != nil { 1547 return nil, model.NewAppError("SqlClassStore.GetMembersByIds", "store.sql_class.get_members_by_ids.app_error", nil, "classId="+classId+" "+err.Error(), http.StatusInternalServerError) 1548 } 1549 1550 return dbMembers.ToModel(), nil 1551 } 1552 1553 func (s SqlClassStore) GetClassesByScheme(schemeId string, offset int, limit int) (model.ClassList, *model.AppError) { 1554 var classes model.ClassList 1555 _, err := s.GetReplica().Select(&classes, "SELECT * FROM Classes WHERE SchemeId = :SchemeId ORDER BY DisplayName LIMIT :Limit OFFSET :Offset", map[string]interface{}{"SchemeId": schemeId, "Offset": offset, "Limit": limit}) 1556 if err != nil { 1557 return nil, model.NewAppError("SqlClassStore.GetClassesByScheme", "store.sql_class.get_by_scheme.app_error", nil, "schemeId="+schemeId+" "+err.Error(), http.StatusInternalServerError) 1558 } 1559 return classes, nil 1560 } 1561 1562 // This function does the Advanced Permissions Phase 2 migration for ClassMember objects. It performs the migration 1563 // in batches as a single transaction per batch to ensure consistency but to also minimise execution time to avoid 1564 // causing unnecessary table locks. **THIS FUNCTION SHOULD NOT BE USED FOR ANY OTHER PURPOSE.** Executing this function 1565 // *after* the new Schemes functionality has been used on an installation will have unintended consequences. 1566 func (s SqlClassStore) MigrateClassMembers(fromClassId string, fromUserId string) (map[string]string, *model.AppError) { 1567 var transaction *gorp.Transaction 1568 var err error 1569 1570 if transaction, err = s.GetMaster().Begin(); err != nil { 1571 return nil, model.NewAppError("SqlClassStore.MigrateClassMembers", "store.sql_class.migrate_class_members.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1572 } 1573 defer finalizeTransaction(transaction) 1574 1575 var classMembers []classMember 1576 if _, err := transaction.Select(&classMembers, "SELECT * from ClassMembers WHERE (ClassId, UserId) > (:FromClassId, :FromUserId) ORDER BY ClassId, UserId LIMIT 100", map[string]interface{}{"FromClassId": fromClassId, "FromUserId": fromUserId}); err != nil { 1577 return nil, model.NewAppError("SqlClassStore.MigrateClassMembers", "store.sql_class.migrate_class_members.select.app_error", nil, err.Error(), http.StatusInternalServerError) 1578 } 1579 1580 if len(classMembers) == 0 { 1581 // No more class members in query result means that the migration has finished. 1582 return nil, nil 1583 } 1584 1585 for i := range classMembers { 1586 member := classMembers[i] 1587 roles := strings.Fields(member.Roles) 1588 var newRoles []string 1589 if !member.SchemeAdmin.Valid { 1590 member.SchemeAdmin = sql.NullBool{Bool: false, Valid: true} 1591 } 1592 if !member.SchemeUser.Valid { 1593 member.SchemeUser = sql.NullBool{Bool: false, Valid: true} 1594 } 1595 for _, role := range roles { 1596 if role == model.CLASS_ADMIN_ROLE_ID { 1597 member.SchemeAdmin = sql.NullBool{Bool: true, Valid: true} 1598 } else if role == model.CLASS_USER_ROLE_ID { 1599 member.SchemeUser = sql.NullBool{Bool: true, Valid: true} 1600 } else { 1601 newRoles = append(newRoles, role) 1602 } 1603 } 1604 member.Roles = strings.Join(newRoles, " ") 1605 1606 if _, err := transaction.Update(&member); err != nil { 1607 return nil, model.NewAppError("SqlClassStore.MigrateClassMembers", "store.sql_class.migrate_class_members.update.app_error", nil, err.Error(), http.StatusInternalServerError) 1608 } 1609 1610 } 1611 1612 if err := transaction.Commit(); err != nil { 1613 return nil, model.NewAppError("SqlClassStore.MigrateClassMembers", "store.sql_class.migrate_class_members.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1614 } 1615 1616 data := make(map[string]string) 1617 data["ClassId"] = classMembers[len(classMembers)-1].ClassId 1618 data["UserId"] = classMembers[len(classMembers)-1].UserId 1619 return data, nil 1620 } 1621 1622 func (s SqlClassStore) ResetAllClassSchemes() *model.AppError { 1623 transaction, err := s.GetMaster().Begin() 1624 if err != nil { 1625 return model.NewAppError("SqlClassStore.ResetAllClassSchemes", "store.sql_class.reset_all_class_schemes.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1626 } 1627 defer finalizeTransaction(transaction) 1628 1629 resetErr := s.resetAllClassSchemesT(transaction) 1630 if resetErr != nil { 1631 return resetErr 1632 } 1633 1634 if err := transaction.Commit(); err != nil { 1635 return model.NewAppError("SqlClassStore.ResetAllClassSchemes", "store.sql_class.reset_all_class_schemes.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1636 } 1637 1638 return nil 1639 } 1640 1641 func (s SqlClassStore) resetAllClassSchemesT(transaction *gorp.Transaction) *model.AppError { 1642 if _, err := transaction.Exec("UPDATE Classes SET SchemeId=''"); err != nil { 1643 return model.NewAppError("SqlClassStore.ResetAllClassSchemes", "store.sql_class.reset_all_class_schemes.app_error", nil, err.Error(), http.StatusInternalServerError) 1644 } 1645 1646 return nil 1647 } 1648 1649 func (s SqlClassStore) ClearAllCustomRoleAssignments() *model.AppError { 1650 builtInRoles := model.MakeDefaultRoles() 1651 lastUserId := strings.Repeat("0", 26) 1652 lastClassId := strings.Repeat("0", 26) 1653 1654 for { 1655 var transaction *gorp.Transaction 1656 var err error 1657 1658 if transaction, err = s.GetMaster().Begin(); err != nil { 1659 return model.NewAppError("SqlClassStore.ClearAllCustomRoleAssignments", "store.sql_class.clear_all_custom_role_assignments.open_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1660 } 1661 1662 var classMembers []*classMember 1663 if _, err := transaction.Select(&classMembers, "SELECT * from ClassMembers WHERE (ClassId, UserId) > (:ClassId, :UserId) ORDER BY ClassId, UserId LIMIT 1000", map[string]interface{}{"ClassId": lastClassId, "UserId": lastUserId}); err != nil { 1664 finalizeTransaction(transaction) 1665 return model.NewAppError("SqlClassStore.ClearAllCustomRoleAssignments", "store.sql_class.clear_all_custom_role_assignments.select.app_error", nil, err.Error(), http.StatusInternalServerError) 1666 } 1667 1668 if len(classMembers) == 0 { 1669 finalizeTransaction(transaction) 1670 break 1671 } 1672 1673 for _, member := range classMembers { 1674 lastUserId = member.UserId 1675 lastClassId = member.ClassId 1676 1677 var newRoles []string 1678 1679 for _, role := range strings.Fields(member.Roles) { 1680 for name := range builtInRoles { 1681 if name == role { 1682 newRoles = append(newRoles, role) 1683 break 1684 } 1685 } 1686 } 1687 1688 newRolesString := strings.Join(newRoles, " ") 1689 if newRolesString != member.Roles { 1690 if _, err := transaction.Exec("UPDATE ClassMembers SET Roles = :Roles WHERE UserId = :UserId AND ClassId = :ClassId", map[string]interface{}{"Roles": newRolesString, "ClassId": member.ClassId, "UserId": member.UserId}); err != nil { 1691 finalizeTransaction(transaction) 1692 return model.NewAppError("SqlClassStore.ClearAllCustomRoleAssignments", "store.sql_class.clear_all_custom_role_assignments.update.app_error", nil, err.Error(), http.StatusInternalServerError) 1693 } 1694 } 1695 } 1696 1697 if err := transaction.Commit(); err != nil { 1698 finalizeTransaction(transaction) 1699 return model.NewAppError("SqlClassStore.ClearAllCustomRoleAssignments", "store.sql_class.clear_all_custom_role_assignments.commit_transaction.app_error", nil, err.Error(), http.StatusInternalServerError) 1700 } 1701 } 1702 1703 return nil 1704 } 1705 1706 func (s SqlClassStore) GetAllClassesForExportAfter(limit int, afterId string) ([]*model.ClassForExport, *model.AppError) { 1707 var classes []*model.ClassForExport 1708 if _, err := s.GetReplica().Select(&classes, ` 1709 SELECT 1710 Classes.*, 1711 Branches.Name as BranchName, 1712 Schemes.Name as SchemeName 1713 FROM Classes 1714 INNER JOIN 1715 Branches ON Classes.BranchId = Branches.Id 1716 LEFT JOIN 1717 Schemes ON Classes.SchemeId = Schemes.Id 1718 WHERE 1719 Classes.Id > :AfterId 1720 ORDER BY 1721 Id 1722 LIMIT :Limit`, 1723 map[string]interface{}{"AfterId": afterId, "Limit": limit}); err != nil { 1724 return nil, model.NewAppError("SqlClassStore.GetAllClassesForExportAfter", "store.sql_class.get_all.app_error", nil, err.Error(), http.StatusInternalServerError) 1725 } 1726 1727 return classes, nil 1728 } 1729 1730 func (s SqlClassStore) GetClassMembersForExport(userId string, branchId string) ([]*model.ClassMemberForExport, *model.AppError) { 1731 var members []*model.ClassMemberForExport 1732 _, err := s.GetReplica().Select(&members, ` 1733 SELECT 1734 ClassMembers.ClassId, 1735 ClassMembers.UserId, 1736 ClassMembers.Roles, 1737 ClassMembers.NotifyProps, 1738 ClassMembers.LastUpdateAt, 1739 ClassMembers.SchemeUser, 1740 ClassMembers.SchemeAdmin, 1741 Classes.Name as ClassName 1742 FROM 1743 ClassMembers 1744 INNER JOIN 1745 Classes ON ClassMembers.ClassId = Classes.Id 1746 WHERE 1747 ClassMembers.UserId = :UserId 1748 AND Classes.BranchId = :BranchId 1749 AND Classes.DeleteAt = 0`, 1750 map[string]interface{}{"BranchId": branchId, "UserId": userId}) 1751 1752 if err != nil { 1753 return nil, model.NewAppError("SqlClassStore.GetClassMembersForExport", "store.sql_class.get_members.app_error", nil, "branchId="+branchId+", userId="+userId+", err="+err.Error(), http.StatusInternalServerError) 1754 } 1755 1756 return members, nil 1757 } 1758 1759 func (s SqlClassStore) GetClassesBatchForIndexing(startTime, endTime int64, limit int) ([]*model.Class, *model.AppError) { 1760 query := 1761 `SELECT 1762 * 1763 FROM 1764 Classes 1765 WHERE 1766 CreateAt >= :StartTime 1767 AND 1768 CreateAt < :EndTime 1769 ORDER BY 1770 CreateAt 1771 LIMIT 1772 :NumClasses` 1773 1774 var classes []*model.Class 1775 _, err := s.GetSearchReplica().Select(&classes, query, map[string]interface{}{"StartTime": startTime, "EndTime": endTime, "NumClasses": limit}) 1776 if err != nil { 1777 return nil, model.NewAppError("SqlClassStore.GetClassesBatchForIndexing", "store.sql_class.get_classes_batch_for_indexing.get.app_error", nil, err.Error(), http.StatusInternalServerError) 1778 } 1779 1780 return classes, nil 1781 } 1782 1783 func (s SqlClassStore) UserBelongsToClasses(userId string, classIds []string) (bool, *model.AppError) { 1784 query := s.getQueryBuilder(). 1785 Select("Count(*)"). 1786 From("ClassMembers"). 1787 Where(sq.And{ 1788 sq.Eq{"UserId": userId}, 1789 sq.Eq{"ClassId": classIds}, 1790 }) 1791 1792 queryString, args, err := query.ToSql() 1793 if err != nil { 1794 return false, model.NewAppError("SqlClassStore.UserBelongsToClasses", "store.sql_class.user_belongs_to_classes.app_error", nil, err.Error(), http.StatusInternalServerError) 1795 } 1796 c, err := s.GetReplica().SelectInt(queryString, args...) 1797 if err != nil { 1798 return false, model.NewAppError("SqlClassStore.UserBelongsToClasses", "store.sql_class.user_belongs_to_classes.app_error", nil, err.Error(), http.StatusInternalServerError) 1799 } 1800 return c > 0, nil 1801 } 1802 1803 func (s SqlClassStore) UpdateMembersRole(classID string, userIDs []string) *model.AppError { 1804 sql := fmt.Sprintf(` 1805 UPDATE 1806 ClassMembers 1807 SET 1808 SchemeAdmin = CASE WHEN UserId IN ('%s') THEN 1809 TRUE 1810 ELSE 1811 FALSE 1812 END 1813 WHERE 1814 ClassId = :ClassId 1815 `, strings.Join(userIDs, "', '")) 1816 1817 if _, err := s.GetMaster().Exec(sql, map[string]interface{}{"ClassId": classID}); err != nil { 1818 return model.NewAppError("SqlClassStore.UpdateMembersRole", "store.update_error", nil, err.Error(), http.StatusInternalServerError) 1819 } 1820 1821 return nil 1822 }