2017-04-12 08:27:57 -04:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2015-06-14 23:53:32 -08:00
// See License.txt for license information.
package store
import (
2016-03-11 10:48:43 -05:00
"database/sql"
2015-06-14 23:53:32 -08:00
"fmt"
2017-01-30 08:30:02 -05:00
"net/http"
2016-04-21 22:37:01 -07:00
"strconv"
2016-01-20 13:36:16 -06:00
"strings"
2016-04-21 22:37:01 -07:00
2016-12-19 10:16:22 -05:00
"github.com/mattermost/platform/einterfaces"
2016-04-21 22:37:01 -07:00
"github.com/mattermost/platform/model"
2016-06-14 12:12:46 -04:00
"github.com/mattermost/platform/utils"
2015-06-14 23:53:32 -08:00
)
2016-01-05 14:53:41 -06:00
const (
2016-12-02 12:24:22 -05:00
MISSING_ACCOUNT_ERROR = "store.sql_user.missing_account.const"
MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error"
2017-02-17 08:21:06 -05:00
PROFILES_IN_CHANNEL_CACHE_SIZE = model . CHANNEL_CACHE_SIZE
2016-12-02 12:24:22 -05:00
PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
2017-02-17 08:21:06 -05:00
PROFILE_BY_IDS_CACHE_SIZE = model . SESSION_CACHE_SIZE
2016-12-13 19:23:36 -08:00
PROFILE_BY_IDS_CACHE_SEC = 900 // 15 mins
2016-12-02 12:24:22 -05:00
USER_SEARCH_OPTION_NAMES_ONLY = "names_only"
USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME = "names_only_no_full_name"
USER_SEARCH_OPTION_ALL_NO_FULL_NAME = "all_no_full_name"
USER_SEARCH_OPTION_ALLOW_INACTIVE = "allow_inactive"
USER_SEARCH_TYPE_NAMES_NO_FULL_NAME = "Username, Nickname"
USER_SEARCH_TYPE_NAMES = "Username, FirstName, LastName, Nickname"
USER_SEARCH_TYPE_ALL_NO_FULL_NAME = "Username, Nickname, Email"
USER_SEARCH_TYPE_ALL = "Username, FirstName, LastName, Nickname, Email"
2016-01-05 14:53:41 -06:00
)
2015-06-14 23:53:32 -08:00
type SqlUserStore struct {
* SqlStore
}
2016-10-19 14:49:25 -04:00
var profilesInChannelCache * utils . Cache = utils . NewLru ( PROFILES_IN_CHANNEL_CACHE_SIZE )
2016-12-13 19:23:36 -08:00
var profileByIdsCache * utils . Cache = utils . NewLru ( PROFILE_BY_IDS_CACHE_SIZE )
2016-10-19 14:49:25 -04:00
2016-12-08 07:18:15 -08:00
func ClearUserCaches ( ) {
profilesInChannelCache . Purge ( )
2016-12-13 19:23:36 -08:00
profileByIdsCache . Purge ( )
}
func ( us SqlUserStore ) InvalidatProfileCacheForUser ( userId string ) {
profileByIdsCache . Remove ( userId )
2016-12-08 07:18:15 -08:00
}
2015-06-14 23:53:32 -08:00
func NewSqlUserStore ( sqlStore * SqlStore ) UserStore {
us := & SqlUserStore { sqlStore }
for _ , db := range sqlStore . GetAllConns ( ) {
table := db . AddTableWithName ( model . User { } , "Users" ) . SetKeys ( false , "Id" )
table . ColMap ( "Id" ) . SetMaxSize ( 26 )
2016-04-21 22:37:01 -07:00
table . ColMap ( "Username" ) . SetMaxSize ( 64 ) . SetUnique ( true )
2015-06-14 23:53:32 -08:00
table . ColMap ( "Password" ) . SetMaxSize ( 128 )
2016-05-11 11:04:30 -07:00
table . ColMap ( "AuthData" ) . SetMaxSize ( 128 ) . SetUnique ( true )
2015-07-22 15:05:20 -04:00
table . ColMap ( "AuthService" ) . SetMaxSize ( 32 )
2016-04-21 22:37:01 -07:00
table . ColMap ( "Email" ) . SetMaxSize ( 128 ) . SetUnique ( true )
2015-07-09 13:59:19 -04:00
table . ColMap ( "Nickname" ) . SetMaxSize ( 64 )
2015-07-09 17:06:04 -04:00
table . ColMap ( "FirstName" ) . SetMaxSize ( 64 )
table . ColMap ( "LastName" ) . SetMaxSize ( 64 )
2015-06-14 23:53:32 -08:00
table . ColMap ( "Roles" ) . SetMaxSize ( 64 )
table . ColMap ( "Props" ) . SetMaxSize ( 4000 )
table . ColMap ( "NotifyProps" ) . SetMaxSize ( 2000 )
2016-01-19 22:00:01 -06:00
table . ColMap ( "Locale" ) . SetMaxSize ( 5 )
2016-03-30 12:49:29 -04:00
table . ColMap ( "MfaSecret" ) . SetMaxSize ( 128 )
2016-12-14 11:11:51 +00:00
table . ColMap ( "Position" ) . SetMaxSize ( 64 )
2015-06-14 23:53:32 -08:00
}
return us
}
func ( us SqlUserStore ) CreateIndexesIfNotExists ( ) {
2015-07-12 14:56:44 -08:00
us . CreateIndexIfNotExists ( "idx_users_email" , "Users" , "Email" )
2016-10-24 17:05:27 -03:00
us . CreateIndexIfNotExists ( "idx_users_update_at" , "Users" , "UpdateAt" )
us . CreateIndexIfNotExists ( "idx_users_create_at" , "Users" , "CreateAt" )
us . CreateIndexIfNotExists ( "idx_users_delete_at" , "Users" , "DeleteAt" )
2016-10-19 14:49:25 -04:00
2016-11-03 11:24:45 -04:00
us . CreateFullTextIndexIfNotExists ( "idx_users_all_txt" , "Users" , USER_SEARCH_TYPE_ALL )
2016-12-02 12:24:22 -05:00
us . CreateFullTextIndexIfNotExists ( "idx_users_all_no_full_name_txt" , "Users" , USER_SEARCH_TYPE_ALL_NO_FULL_NAME )
2016-11-03 11:24:45 -04:00
us . CreateFullTextIndexIfNotExists ( "idx_users_names_txt" , "Users" , USER_SEARCH_TYPE_NAMES )
2016-12-02 12:24:22 -05:00
us . CreateFullTextIndexIfNotExists ( "idx_users_names_no_full_name_txt" , "Users" , USER_SEARCH_TYPE_NAMES_NO_FULL_NAME )
2015-06-14 23:53:32 -08:00
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) Save ( user * model . User ) StoreChannel {
2015-06-14 23:53:32 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
if len ( user . Id ) > 0 {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Save" , "store.sql_user.save.existing.app_error" , nil , "user_id=" + user . Id )
2015-06-14 23:53:32 -08:00
storeChannel <- result
close ( storeChannel )
return
}
user . PreSave ( )
if result . Err = user . IsValid ( ) ; result . Err != nil {
storeChannel <- result
close ( storeChannel )
return
}
if err := us . GetMaster ( ) . Insert ( user ) ; err != nil {
2016-05-17 12:52:10 -07:00
if IsUniqueConstraintError ( err . Error ( ) , [ ] string { "Email" , "users_email_key" , "idx_users_email_unique" } ) {
2017-01-30 08:30:02 -05:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "store.sql_user.save.email_exists.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusBadRequest )
2016-05-17 12:52:10 -07:00
} else if IsUniqueConstraintError ( err . Error ( ) , [ ] string { "Username" , "users_username_key" , "idx_users_username_unique" } ) {
2017-01-30 08:30:02 -05:00
result . Err = model . NewAppError ( "SqlUserStore.Save" , "store.sql_user.save.username_exists.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) , http . StatusBadRequest )
2015-06-14 23:53:32 -08:00
} else {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Save" , "store.sql_user.save.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) )
2015-06-14 23:53:32 -08:00
}
} else {
result . Data = user
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-05-03 15:27:00 -04:00
func ( us SqlUserStore ) Update ( user * model . User , trustedUpdateData bool ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
user . PreUpdate ( )
if result . Err = user . IsValid ( ) ; result . Err != nil {
storeChannel <- result
close ( storeChannel )
return
}
if oldUserResult , err := us . GetMaster ( ) . Get ( model . User { } , user . Id ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.finding.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) )
2015-06-14 23:53:32 -08:00
} else if oldUserResult == nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.find.app_error" , nil , "user_id=" + user . Id )
2015-06-14 23:53:32 -08:00
} else {
oldUser := oldUserResult . ( * model . User )
user . CreateAt = oldUser . CreateAt
user . AuthData = oldUser . AuthData
2015-10-22 10:53:39 -04:00
user . AuthService = oldUser . AuthService
2015-06-14 23:53:32 -08:00
user . Password = oldUser . Password
user . LastPasswordUpdate = oldUser . LastPasswordUpdate
2015-07-16 08:55:37 -07:00
user . LastPictureUpdate = oldUser . LastPictureUpdate
2015-06-14 23:53:32 -08:00
user . EmailVerified = oldUser . EmailVerified
2015-07-29 01:26:10 -08:00
user . FailedAttempts = oldUser . FailedAttempts
2016-03-30 12:49:29 -04:00
user . MfaSecret = oldUser . MfaSecret
user . MfaActive = oldUser . MfaActive
2015-06-14 23:53:32 -08:00
2016-05-03 15:27:00 -04:00
if ! trustedUpdateData {
2015-06-14 23:53:32 -08:00
user . Roles = oldUser . Roles
user . DeleteAt = oldUser . DeleteAt
}
2016-04-21 22:37:01 -07:00
if user . IsOAuthUser ( ) {
2017-02-27 00:18:01 +05:30
if ! trustedUpdateData {
user . Email = oldUser . Email
}
2016-05-03 15:27:00 -04:00
} else if user . IsLDAPUser ( ) && ! trustedUpdateData {
2016-05-02 08:07:58 -04:00
if user . Username != oldUser . Username ||
user . Email != oldUser . Email {
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.can_not_change_ldap.app_error" , nil , "user_id=" + user . Id )
storeChannel <- result
close ( storeChannel )
return
}
} else if user . Email != oldUser . Email {
2015-06-14 23:53:32 -08:00
user . EmailVerified = false
}
2015-08-20 14:29:40 -07:00
if user . Username != oldUser . Username {
2016-04-25 08:20:45 -04:00
user . UpdateMentionKeysFromUsername ( oldUser . Username )
2015-08-20 14:29:40 -07:00
}
2015-06-14 23:53:32 -08:00
if count , err := us . GetMaster ( ) . Update ( user ) ; err != nil {
2016-05-17 12:52:10 -07:00
if IsUniqueConstraintError ( err . Error ( ) , [ ] string { "Email" , "users_email_key" , "idx_users_email_unique" } ) {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.email_taken.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) )
2016-05-17 12:52:10 -07:00
} else if IsUniqueConstraintError ( err . Error ( ) , [ ] string { "Username" , "users_username_key" , "idx_users_username_unique" } ) {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.username_taken.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) )
2015-07-29 09:42:13 -07:00
} else {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.updating.app_error" , nil , "user_id=" + user . Id + ", " + err . Error ( ) )
2015-07-29 09:42:13 -07:00
}
2015-06-14 23:53:32 -08:00
} else if count != 1 {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Update" , "store.sql_user.update.app_error" , nil , fmt . Sprintf ( "user_id=%v, count=%v" , user . Id , count ) )
2015-06-14 23:53:32 -08:00
} else {
2017-04-19 15:38:35 -04:00
user . Sanitize ( map [ string ] bool { } )
oldUser . Sanitize ( map [ string ] bool { } )
2015-06-14 23:53:32 -08:00
result . Data = [ 2 ] * model . User { user , oldUser }
}
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) UpdateLastPictureUpdate ( userId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-07-09 16:37:03 -07:00
go func ( ) {
result := StoreResult { }
2015-07-16 08:55:37 -07:00
curTime := model . GetMillis ( )
2015-07-27 17:04:44 -04:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId" , map [ string ] interface { } { "Time" : curTime , "UserId" : userId } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateUpdateAt" , "store.sql_user.update_last_picture_update.app_error" , nil , "user_id=" + userId )
2015-07-09 16:37:03 -07:00
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-05-12 18:36:30 -07:00
func ( us SqlUserStore ) UpdateUpdateAt ( userId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-05-12 18:36:30 -07:00
go func ( ) {
result := StoreResult { }
curTime := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET UpdateAt = :Time WHERE Id = :UserId" , map [ string ] interface { } { "Time" : curTime , "UserId" : userId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateUpdateAt" , "store.sql_user.update_update.app_error" , nil , "user_id=" + userId )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) UpdatePassword ( userId , hashedPassword string ) StoreChannel {
2015-06-14 23:53:32 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
updateAt := model . GetMillis ( )
2016-05-11 11:04:30 -07:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET Password = :Password, LastPasswordUpdate = :LastPasswordUpdate, UpdateAt = :UpdateAt, AuthData = NULL, AuthService = '', EmailVerified = true, FailedAttempts = 0 WHERE Id = :UserId" , map [ string ] interface { } { "Password" : hashedPassword , "LastPasswordUpdate" : updateAt , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.UpdatePassword" , "store.sql_user.update_password.app_error" , nil , "id=" + userId + ", " + err . Error ( ) )
2015-06-14 23:53:32 -08:00
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) UpdateFailedPasswordAttempts ( userId string , attempts int ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-07-29 01:26:10 -08:00
go func ( ) {
result := StoreResult { }
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET FailedAttempts = :FailedAttempts WHERE Id = :UserId" , map [ string ] interface { } { "FailedAttempts" : attempts , "UserId" : userId } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateFailedPasswordAttempts" , "store.sql_user.update_failed_pwd_attempts.app_error" , nil , "user_id=" + userId )
2015-07-29 01:26:10 -08:00
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-12-12 08:16:10 -05:00
func ( us SqlUserStore ) UpdateAuthData ( userId string , service string , authData * string , email string , resetMfa bool ) StoreChannel {
2015-12-17 12:44:46 -05:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-12-17 12:44:46 -05:00
go func ( ) {
result := StoreResult { }
2016-04-13 14:31:27 -04:00
email = strings . ToLower ( email )
2015-12-17 12:44:46 -05:00
updateAt := model . GetMillis ( )
2016-02-03 10:32:37 -05:00
query := `
UPDATE
Users
SET
Password = ' ' ,
LastPasswordUpdate = : LastPasswordUpdate ,
UpdateAt = : UpdateAt ,
FailedAttempts = 0 ,
AuthService = : AuthService ,
AuthData = : AuthData `
if len ( email ) != 0 {
query += ", Email = :Email"
}
2016-12-12 08:16:10 -05:00
if resetMfa {
query += ", MfaActive = false, MfaSecret = ''"
}
2016-02-03 10:32:37 -05:00
query += " WHERE Id = :UserId"
if _ , err := us . GetMaster ( ) . Exec ( query , map [ string ] interface { } { "LastPasswordUpdate" : updateAt , "UpdateAt" : updateAt , "UserId" : userId , "AuthService" : service , "AuthData" : authData , "Email" : email } ) ; err != nil {
2016-06-15 08:02:07 -04:00
if IsUniqueConstraintError ( err . Error ( ) , [ ] string { "Email" , "users_email_key" , "idx_users_email_unique" } ) {
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateAuthData" , "store.sql_user.update_auth_data.email_exists.app_error" , map [ string ] interface { } { "Service" : service , "Email" : email } , "user_id=" + userId + ", " + err . Error ( ) )
} else {
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateAuthData" , "store.sql_user.update_auth_data.app_error" , nil , "id=" + userId + ", " + err . Error ( ) )
}
2015-12-17 12:44:46 -05:00
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-03-30 12:49:29 -04:00
func ( us SqlUserStore ) UpdateMfaSecret ( userId , secret string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-03-30 12:49:29 -04:00
go func ( ) {
result := StoreResult { }
updateAt := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET MfaSecret = :Secret, UpdateAt = :UpdateAt WHERE Id = :UserId" , map [ string ] interface { } { "Secret" : secret , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateMfaSecret" , "store.sql_user.update_mfa_secret.app_error" , nil , "id=" + userId + ", " + err . Error ( ) )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) UpdateMfaActive ( userId string , active bool ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-03-30 12:49:29 -04:00
go func ( ) {
result := StoreResult { }
updateAt := model . GetMillis ( )
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET MfaActive = :Active, UpdateAt = :UpdateAt WHERE Id = :UserId" , map [ string ] interface { } { "Active" : active , "UpdateAt" : updateAt , "UserId" : userId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.UpdateMfaActive" , "store.sql_user.update_mfa_active.app_error" , nil , "id=" + userId + ", " + err . Error ( ) )
} else {
result . Data = userId
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) Get ( id string ) StoreChannel {
2015-06-14 23:53:32 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
if obj , err := us . GetReplica ( ) . Get ( model . User { } , id ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.Get" , "store.sql_user.get.app_error" , nil , "user_id=" + id + ", " + err . Error ( ) )
2015-06-14 23:53:32 -08:00
} else if obj == nil {
2017-04-12 16:29:42 -04:00
result . Err = model . NewAppError ( "SqlUserStore.Get" , MISSING_ACCOUNT_ERROR , nil , "user_id=" + id , http . StatusNotFound )
2015-06-14 23:53:32 -08:00
} else {
result . Data = obj . ( * model . User )
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-04-21 22:37:01 -07:00
func ( us SqlUserStore ) GetAll ( ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-04-21 22:37:01 -07:00
go func ( ) {
result := StoreResult { }
var data [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & data , "SELECT * FROM Users" ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetAll" , "store.sql_user.get.app_error" , nil , err . Error ( ) )
}
result . Data = data
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-05-04 06:31:42 -07:00
func ( s SqlUserStore ) GetEtagForAllProfiles ( ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-05-04 06:31:42 -07:00
go func ( ) {
result := StoreResult { }
updateAt , err := s . GetReplica ( ) . SelectInt ( "SELECT UpdateAt FROM Users ORDER BY UpdateAt DESC LIMIT 1" )
2016-04-21 22:37:01 -07:00
if err != nil {
2016-06-14 12:12:46 -04:00
result . Data = fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , model . GetMillis ( ) , utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress )
2016-04-21 22:37:01 -07:00
} else {
2016-06-14 12:12:46 -04:00
result . Data = fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , updateAt , utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress )
2016-04-21 22:37:01 -07:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-10-19 14:49:25 -04:00
func ( us SqlUserStore ) GetAllProfiles ( offset int , limit int ) StoreChannel {
2016-05-04 06:31:42 -07:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-05-04 06:31:42 -07:00
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
2016-10-19 14:49:25 -04:00
if _ , err := us . GetReplica ( ) . Select ( & users , "SELECT * FROM Users ORDER BY Username ASC LIMIT :Limit OFFSET :Offset" , map [ string ] interface { } { "Offset" : offset , "Limit" : limit } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetAllProfiles" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
2016-05-04 06:31:42 -07:00
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-05-04 06:31:42 -07:00
}
2017-02-03 15:17:34 -05:00
result . Data = users
2016-05-04 06:31:42 -07:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( s SqlUserStore ) GetEtagForProfiles ( teamId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
2016-04-21 22:37:01 -07:00
updateAt , err := s . GetReplica ( ) . SelectInt ( "SELECT UpdateAt FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId ORDER BY UpdateAt DESC LIMIT 1" , map [ string ] interface { } { "TeamId" : teamId } )
2015-06-14 23:53:32 -08:00
if err != nil {
2016-06-14 12:12:46 -04:00
result . Data = fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , model . GetMillis ( ) , utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress )
2015-06-14 23:53:32 -08:00
} else {
2016-06-14 12:12:46 -04:00
result . Data = fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , updateAt , utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress )
2015-06-14 23:53:32 -08:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-10-19 14:49:25 -04:00
func ( us SqlUserStore ) GetProfiles ( teamId string , offset int , limit int ) StoreChannel {
2015-06-14 23:53:32 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
2016-10-19 14:49:25 -04:00
if _ , err := us . GetReplica ( ) . Select ( & users , "SELECT Users.* FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId AND TeamMembers.DeleteAt = 0 ORDER BY Users.Username ASC LIMIT :Limit OFFSET :Offset" , map [ string ] interface { } { "TeamId" : teamId , "Offset" : offset , "Limit" : limit } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfiles" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
2015-06-14 23:53:32 -08:00
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2015-06-14 23:53:32 -08:00
}
2017-02-03 15:17:34 -05:00
result . Data = users
2015-06-14 23:53:32 -08:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-11-10 06:25:23 -08:00
func ( us SqlUserStore ) InvalidateProfilesInChannelCacheByUser ( userId string ) {
keys := profilesInChannelCache . Keys ( )
for _ , key := range keys {
if cacheItem , ok := profilesInChannelCache . Get ( key ) ; ok {
userMap := cacheItem . ( map [ string ] * model . User )
if _ , userInCache := userMap [ userId ] ; userInCache {
profilesInChannelCache . Remove ( key )
}
}
}
}
2016-10-19 14:49:25 -04:00
func ( us SqlUserStore ) InvalidateProfilesInChannelCache ( channelId string ) {
profilesInChannelCache . Remove ( channelId )
}
2016-04-21 22:37:01 -07:00
2017-02-03 15:17:34 -05:00
func ( us SqlUserStore ) GetProfilesInChannel ( channelId string , offset int , limit int ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
query := "SELECT Users.* FROM Users, ChannelMembers WHERE ChannelMembers.ChannelId = :ChannelId AND Users.Id = ChannelMembers.UserId AND Users.DeleteAt = 0 ORDER BY Users.Username ASC LIMIT :Limit OFFSET :Offset"
if _ , err := us . GetReplica ( ) . Select ( & users , query , map [ string ] interface { } { "ChannelId" : channelId , "Offset" : offset , "Limit" : limit } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfilesInChannel" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2017-02-03 15:17:34 -05:00
}
result . Data = users
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) GetAllProfilesInChannel ( channelId string , allowFromCache bool ) StoreChannel {
2016-10-19 14:49:25 -04:00
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
2016-12-19 10:16:22 -05:00
metrics := einterfaces . GetMetricsInterface ( )
2016-10-19 14:49:25 -04:00
2017-02-03 15:17:34 -05:00
if allowFromCache {
2016-10-19 14:49:25 -04:00
if cacheItem , ok := profilesInChannelCache . Get ( channelId ) ; ok {
2016-12-19 10:16:22 -05:00
if metrics != nil {
metrics . IncrementMemCacheHitCounter ( "Profiles in Channel" )
}
2016-10-19 14:49:25 -04:00
result . Data = cacheItem . ( map [ string ] * model . User )
storeChannel <- result
close ( storeChannel )
return
2016-12-19 10:16:22 -05:00
} else {
if metrics != nil {
metrics . IncrementMemCacheMissCounter ( "Profiles in Channel" )
}
}
} else {
if metrics != nil {
metrics . IncrementMemCacheMissCounter ( "Profiles in Channel" )
2016-10-19 14:49:25 -04:00
}
}
var users [ ] * model . User
query := "SELECT Users.* FROM Users, ChannelMembers WHERE ChannelMembers.ChannelId = :ChannelId AND Users.Id = ChannelMembers.UserId AND Users.DeleteAt = 0"
2017-02-03 15:17:34 -05:00
if _ , err := us . GetReplica ( ) . Select ( & users , query , map [ string ] interface { } { "ChannelId" : channelId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetAllProfilesInChannel" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
2016-10-19 14:49:25 -04:00
} else {
userMap := make ( map [ string ] * model . User )
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-10-19 14:49:25 -04:00
userMap [ u . Id ] = u
}
result . Data = userMap
2017-02-03 15:17:34 -05:00
if allowFromCache {
2016-10-19 14:49:25 -04:00
profilesInChannelCache . AddWithExpiresInSecs ( channelId , userMap , PROFILES_IN_CHANNEL_CACHE_SEC )
}
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) GetProfilesNotInChannel ( teamId string , channelId string , offset int , limit int ) StoreChannel {
storeChannel := make ( StoreChannel )
2016-04-21 22:37:01 -07:00
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , `
2016-10-19 14:49:25 -04:00
SELECT
u . *
FROM Users u
INNER JOIN TeamMembers tm
ON tm . UserId = u . Id
AND tm . TeamId = : TeamId
2016-10-24 12:00:42 -04:00
AND tm . DeleteAt = 0
2016-10-19 14:49:25 -04:00
LEFT JOIN ChannelMembers cm
ON cm . UserId = u . Id
AND cm . ChannelId = : ChannelId
WHERE cm . UserId IS NULL
ORDER BY u . Username ASC
LIMIT : Limit OFFSET : Offset
` , map [ string ] interface { } { "TeamId" : teamId , "ChannelId" : channelId , "Offset" : offset , "Limit" : limit } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfilesNotInChannel" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
2016-04-21 22:37:01 -07:00
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2017-03-29 21:11:40 -04:00
}
result . Data = users
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) GetProfilesWithoutTeam ( offset int , limit int ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
query := `
SELECT
*
FROM
Users
WHERE
( SELECT
COUNT ( 0 )
FROM
TeamMembers
WHERE
TeamMembers . UserId = Users . Id
AND TeamMembers . DeleteAt = 0 ) = 0
ORDER BY
Username ASC
LIMIT
: Limit
OFFSET
: Offset `
if _ , err := us . GetReplica ( ) . Select ( & users , query , map [ string ] interface { } { "Offset" : offset , "Limit" : limit } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfilesWithoutTeam" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-04-21 22:37:01 -07:00
}
2017-02-03 15:17:34 -05:00
result . Data = users
2016-04-21 22:37:01 -07:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-10-19 14:49:25 -04:00
func ( us SqlUserStore ) GetProfilesByUsernames ( usernames [ ] string , teamId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
props := make ( map [ string ] interface { } )
idQuery := ""
for index , usernames := range usernames {
if len ( idQuery ) > 0 {
idQuery += ", "
}
props [ "username" + strconv . Itoa ( index ) ] = usernames
idQuery += ":username" + strconv . Itoa ( index )
}
props [ "TeamId" ] = teamId
if _ , err := us . GetReplica ( ) . Select ( & users , ` SELECT Users . * FROM Users INNER JOIN TeamMembers ON
Users . Id = TeamMembers . UserId AND Users . Username IN ( ` +idQuery+ ` ) AND TeamMembers . TeamId = : TeamId ` , props ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfilesByUsernames" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
} else {
userMap := make ( map [ string ] * model . User )
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-10-19 14:49:25 -04:00
userMap [ u . Id ] = u
}
result . Data = userMap
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
type UserWithLastActivityAt struct {
model . User
LastActivityAt int64
}
func ( us SqlUserStore ) GetRecentlyActiveUsersForTeam ( teamId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * UserWithLastActivityAt
if _ , err := us . GetReplica ( ) . Select ( & users , `
SELECT
u . * ,
s . LastActivityAt
FROM Users AS u
INNER JOIN TeamMembers AS t ON u . Id = t . UserId
INNER JOIN Status AS s ON s . UserId = t . UserId
WHERE t . TeamId = : TeamId
ORDER BY s . LastActivityAt DESC
LIMIT 100
` , map [ string ] interface { } { "TeamId" : teamId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetRecentlyActiveUsers" , "store.sql_user.get_recently_active_users.app_error" , nil , err . Error ( ) )
} else {
userMap := make ( map [ string ] * model . User )
for _ , userWithLastActivityAt := range users {
u := userWithLastActivityAt . User
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-10-19 14:49:25 -04:00
u . LastActivityAt = userWithLastActivityAt . LastActivityAt
userMap [ u . Id ] = & u
}
result . Data = userMap
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-12-13 19:23:36 -08:00
func ( us SqlUserStore ) GetProfileByIds ( userIds [ ] string , allowFromCache bool ) StoreChannel {
2016-04-21 22:37:01 -07:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-04-21 22:37:01 -07:00
go func ( ) {
result := StoreResult { }
2017-01-03 09:57:38 -05:00
metrics := einterfaces . GetMetricsInterface ( )
2016-04-21 22:37:01 -07:00
2017-02-03 09:30:57 -05:00
users := [ ] * model . User { }
2016-04-21 22:37:01 -07:00
props := make ( map [ string ] interface { } )
idQuery := ""
2016-12-13 19:23:36 -08:00
remainingUserIds := make ( [ ] string , 0 )
if allowFromCache {
for _ , userId := range userIds {
if cacheItem , ok := profileByIdsCache . Get ( userId ) ; ok {
u := cacheItem . ( * model . User )
2017-02-03 09:30:57 -05:00
users = append ( users , u )
2016-12-13 19:23:36 -08:00
} else {
remainingUserIds = append ( remainingUserIds , userId )
}
}
2017-01-03 09:57:38 -05:00
if metrics != nil {
2017-02-03 09:30:57 -05:00
metrics . AddMemCacheHitCounter ( "Profile By Ids" , float64 ( len ( users ) ) )
2017-01-03 09:57:38 -05:00
metrics . AddMemCacheMissCounter ( "Profile By Ids" , float64 ( len ( remainingUserIds ) ) )
}
2016-12-13 19:23:36 -08:00
} else {
remainingUserIds = userIds
2017-01-03 09:57:38 -05:00
if metrics != nil {
metrics . AddMemCacheMissCounter ( "Profile By Ids" , float64 ( len ( remainingUserIds ) ) )
}
2016-12-13 19:23:36 -08:00
}
2016-04-21 22:37:01 -07:00
2016-12-13 19:23:36 -08:00
// If everything came from the cache then just return
if len ( remainingUserIds ) == 0 {
2017-02-03 09:30:57 -05:00
result . Data = users
2016-12-13 19:23:36 -08:00
storeChannel <- result
close ( storeChannel )
return
}
for index , userId := range remainingUserIds {
2016-04-21 22:37:01 -07:00
if len ( idQuery ) > 0 {
idQuery += ", "
}
props [ "userId" + strconv . Itoa ( index ) ] = userId
idQuery += ":userId" + strconv . Itoa ( index )
}
if _ , err := us . GetReplica ( ) . Select ( & users , "SELECT * FROM Users WHERE Users.Id IN (" + idQuery + ")" , props ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfileByIds" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-12-13 19:23:36 -08:00
profileByIdsCache . AddWithExpiresInSecs ( u . Id , u , PROFILE_BY_IDS_CACHE_SEC )
2016-04-21 22:37:01 -07:00
}
2017-02-03 09:30:57 -05:00
result . Data = users
2016-04-21 22:37:01 -07:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) GetSystemAdminProfiles ( ) StoreChannel {
2015-10-05 17:33:45 -07:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-10-05 17:33:45 -07:00
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
2016-09-13 12:42:48 -04:00
if _ , err := us . GetReplica ( ) . Select ( & users , "SELECT * FROM Users WHERE Roles LIKE :Roles" , map [ string ] interface { } { "Roles" : "%system_admin%" } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetSystemAdminProfiles" , "store.sql_user.get_sysadmin_profiles.app_error" , nil , err . Error ( ) )
2015-10-05 17:33:45 -07:00
} else {
userMap := make ( map [ string ] * model . User )
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2015-10-05 17:33:45 -07:00
userMap [ u . Id ] = u
}
result . Data = userMap
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-04-21 22:37:01 -07:00
func ( us SqlUserStore ) GetByEmail ( email string ) StoreChannel {
2015-06-14 23:53:32 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
2016-04-13 14:31:27 -04:00
email = strings . ToLower ( email )
2015-06-14 23:53:32 -08:00
user := model . User { }
2016-04-21 22:37:01 -07:00
if err := us . GetReplica ( ) . SelectOne ( & user , "SELECT * FROM Users WHERE Email = :Email" , map [ string ] interface { } { "Email" : email } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetByEmail" , MISSING_ACCOUNT_ERROR , nil , "email=" + email + ", " + err . Error ( ) )
2015-06-14 23:53:32 -08:00
}
result . Data = & user
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-05-11 11:04:30 -07:00
func ( us SqlUserStore ) GetByAuth ( authData * string , authService string ) StoreChannel {
2015-07-15 12:48:50 -04:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-07-15 12:48:50 -04:00
go func ( ) {
result := StoreResult { }
2016-05-11 11:04:30 -07:00
if authData == nil || * authData == "" {
result . Err = model . NewLocAppError ( "SqlUserStore.GetByAuth" , MISSING_AUTH_ACCOUNT_ERROR , nil , "authData='', authService=" + authService )
storeChannel <- result
close ( storeChannel )
return
}
2015-07-15 12:48:50 -04:00
user := model . User { }
2016-04-21 22:37:01 -07:00
if err := us . GetReplica ( ) . SelectOne ( & user , "SELECT * FROM Users WHERE AuthData = :AuthData AND AuthService = :AuthService" , map [ string ] interface { } { "AuthData" : authData , "AuthService" : authService } ) ; err != nil {
2016-03-11 10:48:43 -05:00
if err == sql . ErrNoRows {
2016-05-11 11:04:30 -07:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetByAuth" , MISSING_AUTH_ACCOUNT_ERROR , nil , "authData=" + * authData + ", authService=" + authService + ", " + err . Error ( ) )
2016-03-11 10:48:43 -05:00
} else {
2016-05-11 11:04:30 -07:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetByAuth" , "store.sql_user.get_by_auth.other.app_error" , nil , "authData=" + * authData + ", authService=" + authService + ", " + err . Error ( ) )
2016-03-11 10:48:43 -05:00
}
2015-07-15 12:48:50 -04:00
}
result . Data = & user
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-06-03 09:33:59 -04:00
func ( us SqlUserStore ) GetAllUsingAuthService ( authService string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-06-03 09:33:59 -04:00
go func ( ) {
result := StoreResult { }
var data [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & data , "SELECT * FROM Users WHERE AuthService = :AuthService" , map [ string ] interface { } { "AuthService" : authService } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetByAuth" , "store.sql_user.get_by_auth.other.app_error" , nil , "authService=" + authService + ", " + err . Error ( ) )
}
result . Data = data
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-04-21 22:37:01 -07:00
func ( us SqlUserStore ) GetByUsername ( username string ) StoreChannel {
2015-06-14 23:53:32 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
user := model . User { }
2016-04-21 22:37:01 -07:00
if err := us . GetReplica ( ) . SelectOne ( & user , "SELECT * FROM Users WHERE Username = :Username" , map [ string ] interface { } { "Username" : username } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetByUsername" , "store.sql_user.get_by_username.app_error" ,
2016-04-21 22:37:01 -07:00
nil , err . Error ( ) )
2015-06-14 23:53:32 -08:00
}
result . Data = & user
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-05-03 14:10:36 -04:00
func ( us SqlUserStore ) GetForLogin ( loginId string , allowSignInWithUsername , allowSignInWithEmail , ldapEnabled bool ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-05-03 14:10:36 -04:00
go func ( ) {
result := StoreResult { }
params := map [ string ] interface { } {
"LoginId" : loginId ,
"AllowSignInWithUsername" : allowSignInWithUsername ,
"AllowSignInWithEmail" : allowSignInWithEmail ,
"LdapEnabled" : ldapEnabled ,
}
users := [ ] * model . User { }
if _ , err := us . GetReplica ( ) . Select (
& users ,
` SELECT
*
FROM
Users
WHERE
( : AllowSignInWithUsername AND Username = : LoginId )
OR ( : AllowSignInWithEmail AND Email = : LoginId )
OR ( : LdapEnabled AND AuthService = ' ` +model.USER_AUTH_SERVICE_LDAP+ ` ' AND AuthData = : LoginId ) ` ,
params ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.app_error" , nil , err . Error ( ) )
} else if len ( users ) == 1 {
result . Data = users [ 0 ]
} else if len ( users ) > 1 {
result . Err = model . NewLocAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.multiple_users" , nil , "" )
} else {
result . Err = model . NewLocAppError ( "SqlUserStore.GetForLogin" , "store.sql_user.get_for_login.app_error" , nil , "" )
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) VerifyEmail ( userId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-06-14 23:53:32 -08:00
go func ( ) {
result := StoreResult { }
2016-05-03 03:08:48 +09:00
if _ , err := us . GetMaster ( ) . Exec ( "UPDATE Users SET EmailVerified = true WHERE Id = :UserId" , map [ string ] interface { } { "UserId" : userId } ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.VerifyEmail" , "store.sql_user.verify_email.app_error" , nil , "userId=" + userId + ", " + err . Error ( ) )
2015-06-14 23:53:32 -08:00
}
result . Data = userId
storeChannel <- result
2015-08-26 12:49:07 -04:00
close ( storeChannel )
} ( )
return storeChannel
}
2015-09-23 15:52:59 -07:00
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) GetTotalUsersCount ( ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-09-23 15:52:59 -07:00
go func ( ) {
result := StoreResult { }
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT COUNT(Id) FROM Users" ) ; err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetTotalUsersCount" , "store.sql_user.get_total_users_count.app_error" , nil , err . Error ( ) )
2015-10-09 12:24:39 -07:00
} else {
result . Data = count
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2015-11-15 18:18:02 -08:00
2016-01-20 13:36:16 -06:00
func ( us SqlUserStore ) PermanentDelete ( userId string ) StoreChannel {
2015-11-15 18:18:02 -08:00
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2015-11-15 18:18:02 -08:00
go func ( ) {
result := StoreResult { }
if _ , err := us . GetMaster ( ) . Exec ( "DELETE FROM Users WHERE Id = :UserId" , map [ string ] interface { } { "UserId" : userId } ) ; err != nil {
2016-04-13 14:31:27 -04:00
result . Err = model . NewLocAppError ( "SqlUserStore.PermanentDelete" , "store.sql_user.permanent_delete.app_error" , nil , "userId=" + userId + ", " + err . Error ( ) )
2015-11-15 18:18:02 -08:00
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-01-21 12:14:17 -05:00
func ( us SqlUserStore ) AnalyticsUniqueUserCount ( teamId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-01-21 12:14:17 -05:00
go func ( ) {
result := StoreResult { }
2016-11-08 09:59:40 -05:00
query := ""
2016-01-21 12:14:17 -05:00
if len ( teamId ) > 0 {
2016-11-08 09:59:40 -05:00
query = "SELECT COUNT(DISTINCT Users.Email) From Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId AND TeamMembers.DeleteAt = 0 AND Users.DeleteAt = 0"
} else {
query = "SELECT COUNT(DISTINCT Email) FROM Users WHERE DeleteAt = 0"
2016-01-21 12:14:17 -05:00
}
v , err := us . GetReplica ( ) . SelectInt ( query , map [ string ] interface { } { "TeamId" : teamId } )
if err != nil {
2016-01-25 16:08:06 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.AnalyticsUniqueUserCount" , "store.sql_user.analytics_unique_user_count.app_error" , nil , err . Error ( ) )
2016-01-21 12:14:17 -05:00
} else {
result . Data = v
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-03-11 00:14:55 -03:00
2017-01-20 15:24:53 -05:00
func ( us SqlUserStore ) AnalyticsActiveCount ( timePeriod int64 ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
result := StoreResult { }
time := model . GetMillis ( ) - timePeriod
query := "SELECT COUNT(*) FROM Status WHERE LastActivityAt > :Time"
v , err := us . GetReplica ( ) . SelectInt ( query , map [ string ] interface { } { "Time" : time } )
if err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.AnalyticsDailyActiveUsers" , "store.sql_user.analytics_daily_active_users.app_error" , nil , err . Error ( ) )
} else {
result . Data = v
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-03-11 00:14:55 -03:00
func ( us SqlUserStore ) GetUnreadCount ( userId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-03-11 00:14:55 -03:00
go func ( ) {
result := StoreResult { }
2017-03-07 16:05:01 -05:00
if count , err := us . GetReplica ( ) . SelectInt ( `
SELECT SUM ( CASE WHEN c . Type = 'D' THEN ( c . TotalMsgCount - cm . MsgCount ) ELSE cm . MentionCount END )
FROM Channels c
INNER JOIN ChannelMembers cm
ON cm . ChannelId = c . Id
AND cm . UserId = : UserId
AND c . DeleteAt = 0 ` , map [ string ] interface { } { "UserId" : userId } ) ; err != nil {
2016-03-11 00:14:55 -03:00
result . Err = model . NewLocAppError ( "SqlUserStore.GetMentionCount" , "store.sql_user.get_unread_count.app_error" , nil , err . Error ( ) )
} else {
result . Data = count
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-08-31 12:52:14 -04:00
func ( us SqlUserStore ) GetUnreadCountForChannel ( userId string , channelId string ) StoreChannel {
2016-09-19 19:30:41 +07:00
storeChannel := make ( StoreChannel , 1 )
2016-08-31 12:52:14 -04:00
go func ( ) {
result := StoreResult { }
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = :ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId" , map [ string ] interface { } { "ChannelId" : channelId , "UserId" : userId } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetMentionCountForChannel" , "store.sql_user.get_unread_count_for_channel.app_error" , nil , err . Error ( ) )
} else {
result . Data = count
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2016-10-19 14:49:25 -04:00
2016-11-02 14:38:34 -04:00
func ( us SqlUserStore ) Search ( teamId string , term string , options map [ string ] bool ) StoreChannel {
2016-10-19 14:49:25 -04:00
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
searchQuery := ""
2016-11-02 14:38:34 -04:00
2016-10-19 14:49:25 -04:00
if teamId == "" {
2016-11-02 14:38:34 -04:00
// Id != '' is added because both SEARCH_CLAUSE and INACTIVE_CLAUSE start with an AND
2016-10-19 14:49:25 -04:00
searchQuery = `
SELECT
*
FROM
Users
WHERE
2016-11-02 14:38:34 -04:00
Id != ' '
2016-10-19 14:49:25 -04:00
SEARCH_CLAUSE
2016-11-02 14:38:34 -04:00
INACTIVE_CLAUSE
2016-10-19 14:49:25 -04:00
ORDER BY Username ASC
2016-11-24 09:35:09 -05:00
LIMIT 100 `
2016-10-19 14:49:25 -04:00
} else {
searchQuery = `
SELECT
Users . *
FROM
Users , TeamMembers
WHERE
TeamMembers . TeamId = : TeamId
AND Users . Id = TeamMembers . UserId
AND TeamMembers . DeleteAt = 0
SEARCH_CLAUSE
2016-11-02 14:38:34 -04:00
INACTIVE_CLAUSE
2016-10-19 14:49:25 -04:00
ORDER BY Users . Username ASC
LIMIT 100 `
}
2016-11-02 14:38:34 -04:00
storeChannel <- us . performSearch ( searchQuery , term , options , map [ string ] interface { } { "TeamId" : teamId } )
2016-10-19 14:49:25 -04:00
close ( storeChannel )
2017-03-23 06:34:22 -04:00
} ( )
return storeChannel
}
func ( us SqlUserStore ) SearchWithoutTeam ( term string , options map [ string ] bool ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
searchQuery := `
SELECT
*
FROM
Users
WHERE
( SELECT
COUNT ( 0 )
FROM
TeamMembers
WHERE
TeamMembers . UserId = Users . Id
AND TeamMembers . DeleteAt = 0 ) = 0
SEARCH_CLAUSE
INACTIVE_CLAUSE
ORDER BY Username ASC
LIMIT 100 `
storeChannel <- us . performSearch ( searchQuery , term , options , map [ string ] interface { } { } )
close ( storeChannel )
2017-04-03 18:11:12 +01:00
} ( )
return storeChannel
}
func ( us SqlUserStore ) SearchNotInTeam ( notInTeamId string , term string , options map [ string ] bool ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
searchQuery := `
SELECT
Users . *
FROM Users
LEFT JOIN TeamMembers tm
ON tm . UserId = Users . Id
AND tm . TeamId = : NotInTeamId
WHERE
2017-04-07 19:49:02 +01:00
( tm . UserId IS NULL OR tm . DeleteAt != 0 )
2017-04-03 18:11:12 +01:00
SEARCH_CLAUSE
INACTIVE_CLAUSE
ORDER BY Users . Username ASC
LIMIT 100 `
storeChannel <- us . performSearch ( searchQuery , term , options , map [ string ] interface { } { "NotInTeamId" : notInTeamId } )
close ( storeChannel )
2016-10-19 14:49:25 -04:00
} ( )
return storeChannel
}
2016-11-02 14:38:34 -04:00
func ( us SqlUserStore ) SearchNotInChannel ( teamId string , channelId string , term string , options map [ string ] bool ) StoreChannel {
2016-10-19 14:49:25 -04:00
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
searchQuery := ""
if teamId == "" {
searchQuery = `
SELECT
2016-11-02 14:38:34 -04:00
Users . *
FROM Users
2016-10-19 14:49:25 -04:00
LEFT JOIN ChannelMembers cm
2016-11-02 14:38:34 -04:00
ON cm . UserId = Users . Id
2016-10-19 14:49:25 -04:00
AND cm . ChannelId = : ChannelId
2016-11-02 14:38:34 -04:00
WHERE
cm . UserId IS NULL
SEARCH_CLAUSE
INACTIVE_CLAUSE
ORDER BY Users . Username ASC
2016-10-19 14:49:25 -04:00
LIMIT 100 `
} else {
searchQuery = `
SELECT
2016-11-02 14:38:34 -04:00
Users . *
FROM Users
2016-10-19 14:49:25 -04:00
INNER JOIN TeamMembers tm
2016-11-02 14:38:34 -04:00
ON tm . UserId = Users . Id
2016-10-19 14:49:25 -04:00
AND tm . TeamId = : TeamId
2016-10-24 12:00:42 -04:00
AND tm . DeleteAt = 0
2016-10-19 14:49:25 -04:00
LEFT JOIN ChannelMembers cm
2016-11-02 14:38:34 -04:00
ON cm . UserId = Users . Id
2016-10-19 14:49:25 -04:00
AND cm . ChannelId = : ChannelId
2016-11-02 14:38:34 -04:00
WHERE
cm . UserId IS NULL
SEARCH_CLAUSE
INACTIVE_CLAUSE
ORDER BY Users . Username ASC
2016-10-19 14:49:25 -04:00
LIMIT 100 `
}
2016-11-02 14:38:34 -04:00
storeChannel <- us . performSearch ( searchQuery , term , options , map [ string ] interface { } { "TeamId" : teamId , "ChannelId" : channelId } )
2016-10-19 14:49:25 -04:00
close ( storeChannel )
} ( )
return storeChannel
}
2016-11-02 14:38:34 -04:00
func ( us SqlUserStore ) SearchInChannel ( channelId string , term string , options map [ string ] bool ) StoreChannel {
2016-10-19 14:49:25 -04:00
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
searchQuery := `
SELECT
Users . *
FROM
Users , ChannelMembers
WHERE
ChannelMembers . ChannelId = : ChannelId
AND ChannelMembers . UserId = Users . Id
SEARCH_CLAUSE
2016-11-02 14:38:34 -04:00
INACTIVE_CLAUSE
2016-10-19 14:49:25 -04:00
ORDER BY Users . Username ASC
LIMIT 100 `
2016-11-02 14:38:34 -04:00
storeChannel <- us . performSearch ( searchQuery , term , options , map [ string ] interface { } { "ChannelId" : channelId } )
2016-10-19 14:49:25 -04:00
close ( storeChannel )
} ( )
return storeChannel
}
2016-11-21 12:17:53 -05:00
var specialUserSearchChar = [ ] string {
"<" ,
">" ,
"+" ,
"-" ,
"(" ,
")" ,
"~" ,
":" ,
"*" ,
2016-12-07 07:55:36 -05:00
"\"" ,
2017-01-03 17:11:44 -05:00
"!" ,
"@" ,
}
var postgresSearchChar = [ ] string {
"(" ,
")" ,
":" ,
"!" ,
2016-11-21 12:17:53 -05:00
}
2016-11-02 14:38:34 -04:00
func ( us SqlUserStore ) performSearch ( searchQuery string , term string , options map [ string ] bool , parameters map [ string ] interface { } ) StoreResult {
2016-10-19 14:49:25 -04:00
result := StoreResult { }
2017-01-03 17:11:44 -05:00
// Special handling for emails
originalTerm := term
postgresUseOriginalTerm := false
if strings . Contains ( term , "@" ) && strings . Contains ( term , "." ) {
if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
postgresUseOriginalTerm = true
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
lastIndex := strings . LastIndex ( term , "." )
term = term [ 0 : lastIndex ]
}
}
2016-10-31 09:10:20 -04:00
// these chars have special meaning and can be treated as spaces
2016-11-21 12:17:53 -05:00
for _ , c := range specialUserSearchChar {
2016-10-31 09:10:20 -04:00
term = strings . Replace ( term , c , " " , - 1 )
}
2016-11-02 14:38:34 -04:00
searchType := USER_SEARCH_TYPE_ALL
2016-11-03 11:24:45 -04:00
if ok := options [ USER_SEARCH_OPTION_NAMES_ONLY ] ; ok {
searchType = USER_SEARCH_TYPE_NAMES
2016-12-02 12:24:22 -05:00
} else if ok = options [ USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME ] ; ok {
searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
} else if ok = options [ USER_SEARCH_OPTION_ALL_NO_FULL_NAME ] ; ok {
searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
2016-11-02 14:38:34 -04:00
}
if ok := options [ USER_SEARCH_OPTION_ALLOW_INACTIVE ] ; ok {
searchQuery = strings . Replace ( searchQuery , "INACTIVE_CLAUSE" , "" , 1 )
} else {
searchQuery = strings . Replace ( searchQuery , "INACTIVE_CLAUSE" , "AND Users.DeleteAt = 0" , 1 )
}
2016-10-19 14:49:25 -04:00
if term == "" {
searchQuery = strings . Replace ( searchQuery , "SEARCH_CLAUSE" , "" , 1 )
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_POSTGRES {
2017-01-03 17:11:44 -05:00
if postgresUseOriginalTerm {
term = originalTerm
// these chars will break the query and must be removed
for _ , c := range postgresSearchChar {
term = strings . Replace ( term , c , "" , - 1 )
}
} else {
splitTerm := strings . Fields ( term )
for i , t := range strings . Fields ( term ) {
if i == len ( splitTerm ) - 1 {
splitTerm [ i ] = t + ":*"
} else {
splitTerm [ i ] = t + ":* &"
}
2016-10-31 09:10:20 -04:00
}
2017-01-03 17:11:44 -05:00
term = strings . Join ( splitTerm , " " )
}
2016-10-31 09:10:20 -04:00
2016-10-20 11:30:44 -04:00
searchType = convertMySQLFullTextColumnsToPostgres ( searchType )
2016-11-24 09:35:09 -05:00
searchClause := fmt . Sprintf ( "AND (%s) @@ to_tsquery('simple', :Term)" , searchType )
2016-10-19 14:49:25 -04:00
searchQuery = strings . Replace ( searchQuery , "SEARCH_CLAUSE" , searchClause , 1 )
} else if utils . Cfg . SqlSettings . DriverName == model . DATABASE_DRIVER_MYSQL {
2016-10-31 09:10:20 -04:00
splitTerm := strings . Fields ( term )
for i , t := range strings . Fields ( term ) {
splitTerm [ i ] = "+" + t + "*"
}
term = strings . Join ( splitTerm , " " )
2016-10-19 14:49:25 -04:00
searchClause := fmt . Sprintf ( "AND MATCH(%s) AGAINST (:Term IN BOOLEAN MODE)" , searchType )
searchQuery = strings . Replace ( searchQuery , "SEARCH_CLAUSE" , searchClause , 1 )
}
var users [ ] * model . User
parameters [ "Term" ] = term
if _ , err := us . GetReplica ( ) . Select ( & users , searchQuery , parameters ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.Search" , "store.sql_user.search.app_error" , nil , "term=" + term + ", " + "search_type=" + searchType + ", " + err . Error ( ) )
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2016-10-19 14:49:25 -04:00
}
result . Data = users
}
return result
}
2017-02-24 17:33:59 +00:00
func ( us SqlUserStore ) AnalyticsGetInactiveUsersCount ( ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
result := StoreResult { }
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT COUNT(Id) FROM Users WHERE DeleteAt > 0" ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.AnalyticsGetInactiveUsersCount" , "store.sql_user.analytics_get_inactive_users_count.app_error" , nil , err . Error ( ) )
} else {
result . Data = count
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) AnalyticsGetSystemAdminCount ( ) StoreChannel {
storeChannel := make ( StoreChannel , 1 )
go func ( ) {
result := StoreResult { }
if count , err := us . GetReplica ( ) . SelectInt ( "SELECT count(*) FROM Users WHERE Roles LIKE :Roles and DeleteAt = 0" , map [ string ] interface { } { "Roles" : "%system_admin%" } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.AnalyticsGetSystemAdminCount" , "store.sql_user.analytics_get_system_admin_count.app_error" , nil , err . Error ( ) )
} else {
result . Data = count
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
2017-03-30 02:10:51 +01:00
func ( us SqlUserStore ) GetProfilesNotInTeam ( teamId string , offset int , limit int ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
var users [ ] * model . User
if _ , err := us . GetReplica ( ) . Select ( & users , `
SELECT
u . *
FROM Users u
LEFT JOIN TeamMembers tm
ON tm . UserId = u . Id
AND tm . TeamId = : TeamId
AND tm . DeleteAt = 0
WHERE tm . UserId IS NULL
ORDER BY u . Username ASC
LIMIT : Limit OFFSET : Offset
` , map [ string ] interface { } { "TeamId" : teamId , "Offset" : offset , "Limit" : limit } ) ; err != nil {
result . Err = model . NewLocAppError ( "SqlUserStore.GetProfilesNotInTeam" , "store.sql_user.get_profiles.app_error" , nil , err . Error ( ) )
} else {
for _ , u := range users {
2017-04-19 15:38:35 -04:00
u . Sanitize ( map [ string ] bool { } )
2017-03-30 02:10:51 +01:00
}
result . Data = users
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}
func ( us SqlUserStore ) GetEtagForProfilesNotInTeam ( teamId string ) StoreChannel {
storeChannel := make ( StoreChannel )
go func ( ) {
result := StoreResult { }
updateAt , err := us . GetReplica ( ) . SelectInt ( `
SELECT
u . UpdateAt
FROM Users u
LEFT JOIN TeamMembers tm
ON tm . UserId = u . Id
AND tm . TeamId = : TeamId
AND tm . DeleteAt = 0
WHERE tm . UserId IS NULL
ORDER BY u . UpdateAt DESC
LIMIT 1
` , map [ string ] interface { } { "TeamId" : teamId } )
if err != nil {
result . Data = fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , model . GetMillis ( ) , utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress )
} else {
result . Data = fmt . Sprintf ( "%v.%v.%v.%v" , model . CurrentVersion , updateAt , utils . Cfg . PrivacySettings . ShowFullName , utils . Cfg . PrivacySettings . ShowEmailAddress )
}
storeChannel <- result
close ( storeChannel )
} ( )
return storeChannel
}