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  }