@@ -14,6 +14,7 @@ import (
1414 "path/filepath"
1515 "regexp"
1616 "strings"
17+ "sync"
1718 "time"
1819 "unicode"
1920
@@ -213,7 +214,7 @@ func (u *User) GetPlaceholderEmail() string {
213214 return fmt .Sprintf ("%s@%s" , u .LowerName , setting .Service .NoReplyAddress )
214215}
215216
216- // GetEmail returns an noreply email, if the user has set to keep his
217+ // GetEmail returns a noreply email, if the user has set to keep his
217218// email address private, otherwise the primary email address.
218219func (u * User ) GetEmail () string {
219220 if u .KeepEmailPrivate {
@@ -417,19 +418,9 @@ func (u *User) DisplayName() string {
417418 return u .Name
418419}
419420
420- var emailToReplacer = strings .NewReplacer (
421- "\n " , "" ,
422- "\r " , "" ,
423- "<" , "" ,
424- ">" , "" ,
425- "," , "" ,
426- ":" , "" ,
427- ";" , "" ,
428- )
429-
430421// EmailTo returns a string suitable to be put into a e-mail `To:` header.
431422func (u * User ) EmailTo () string {
432- sanitizedDisplayName := emailToReplacer .Replace (u .DisplayName ())
423+ sanitizedDisplayName := globalVars (). emailToReplacer .Replace (u .DisplayName ())
433424
434425 // should be an edge case but nice to have
435426 if sanitizedDisplayName == u .Email {
@@ -526,28 +517,58 @@ func GetUserSalt() (string, error) {
526517 if err != nil {
527518 return "" , err
528519 }
529- // Returns a 32 bytes long string.
520+ // Returns a 32-byte long string.
530521 return hex .EncodeToString (rBytes ), nil
531522}
532523
533- // Note: The set of characters here can safely expand without a breaking change,
534- // but characters removed from this set can cause user account linking to break
535- var (
536- customCharsReplacement = strings .NewReplacer ("Æ" , "AE" )
537- removeCharsRE = regexp .MustCompile ("['`´]" )
538- transformDiacritics = transform .Chain (norm .NFD , runes .Remove (runes .In (unicode .Mn )), norm .NFC )
539- replaceCharsHyphenRE = regexp .MustCompile (`[\s~+]` )
540- )
524+ type globalVarsStruct struct {
525+ customCharsReplacement * strings.Replacer
526+ removeCharsRE * regexp.Regexp
527+ transformDiacritics transform.Transformer
528+ replaceCharsHyphenRE * regexp.Regexp
529+ emailToReplacer * strings.Replacer
530+ emailRegexp * regexp.Regexp
531+ systemUserNewFuncs map [int64 ]func () * User
532+ }
533+
534+ var globalVars = sync .OnceValue (func () * globalVarsStruct {
535+ return & globalVarsStruct {
536+ // Note: The set of characters here can safely expand without a breaking change,
537+ // but characters removed from this set can cause user account linking to break
538+ customCharsReplacement : strings .NewReplacer ("Æ" , "AE" ),
539+
540+ removeCharsRE : regexp .MustCompile ("['`´]" ),
541+ transformDiacritics : transform .Chain (norm .NFD , runes .Remove (runes .In (unicode .Mn )), norm .NFC ),
542+ replaceCharsHyphenRE : regexp .MustCompile (`[\s~+]` ),
543+
544+ emailToReplacer : strings .NewReplacer (
545+ "\n " , "" ,
546+ "\r " , "" ,
547+ "<" , "" ,
548+ ">" , "" ,
549+ "," , "" ,
550+ ":" , "" ,
551+ ";" , "" ,
552+ ),
553+ emailRegexp : regexp .MustCompile ("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\ .[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" ),
554+
555+ systemUserNewFuncs : map [int64 ]func () * User {
556+ GhostUserID : NewGhostUser ,
557+ ActionsUserID : NewActionsUser ,
558+ },
559+ }
560+ })
541561
542562// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
543563// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
544564func NormalizeUserName (s string ) (string , error ) {
565+ vars := globalVars ()
545566 s , _ , _ = strings .Cut (s , "@" )
546- strDiacriticsRemoved , n , err := transform .String (transformDiacritics , customCharsReplacement .Replace (s ))
567+ strDiacriticsRemoved , n , err := transform .String (vars . transformDiacritics , vars . customCharsReplacement .Replace (s ))
547568 if err != nil {
548569 return "" , fmt .Errorf ("failed to normalize the string of provided username %q at position %d" , s , n )
549570 }
550- return replaceCharsHyphenRE .ReplaceAllLiteralString (removeCharsRE .ReplaceAllLiteralString (strDiacriticsRemoved , "" ), "-" ), nil
571+ return vars . replaceCharsHyphenRE .ReplaceAllLiteralString (vars . removeCharsRE .ReplaceAllLiteralString (strDiacriticsRemoved , "" ), "-" ), nil
551572}
552573
553574var (
@@ -963,30 +984,28 @@ func GetUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
963984 return users , err
964985}
965986
966- // GetPossibleUserByID returns the user if id > 0 or return system usrs if id < 0
987+ // GetPossibleUserByID returns the user if id > 0 or returns system user if id < 0
967988func GetPossibleUserByID (ctx context.Context , id int64 ) (* User , error ) {
968- switch id {
969- case GhostUserID :
970- return NewGhostUser (), nil
971- case ActionsUserID :
972- return NewActionsUser (), nil
973- case 0 :
989+ if id < 0 {
990+ if newFunc , ok := globalVars (). systemUserNewFuncs [ id ]; ok {
991+ return newFunc (), nil
992+ }
993+ return nil , ErrUserNotExist { UID : id }
994+ } else if id == 0 {
974995 return nil , ErrUserNotExist {}
975- default :
976- return GetUserByID (ctx , id )
977996 }
997+ return GetUserByID (ctx , id )
978998}
979999
980- // GetPossibleUserByIDs returns the users if id > 0 or return system users if id < 0
1000+ // GetPossibleUserByIDs returns the users if id > 0 or returns system users if id < 0
9811001func GetPossibleUserByIDs (ctx context.Context , ids []int64 ) ([]* User , error ) {
9821002 uniqueIDs := container .SetOf (ids ... )
9831003 users := make ([]* User , 0 , len (ids ))
9841004 _ = uniqueIDs .Remove (0 )
985- if uniqueIDs .Remove (GhostUserID ) {
986- users = append (users , NewGhostUser ())
987- }
988- if uniqueIDs .Remove (ActionsUserID ) {
989- users = append (users , NewActionsUser ())
1005+ for systemUID , newFunc := range globalVars ().systemUserNewFuncs {
1006+ if uniqueIDs .Remove (systemUID ) {
1007+ users = append (users , newFunc ())
1008+ }
9901009 }
9911010 res , err := GetUserByIDs (ctx , uniqueIDs .Values ())
9921011 if err != nil {
@@ -996,7 +1015,7 @@ func GetPossibleUserByIDs(ctx context.Context, ids []int64) ([]*User, error) {
9961015 return users , nil
9971016}
9981017
999- // GetUserByNameCtx returns user by given name.
1018+ // GetUserByName returns user by given name.
10001019func GetUserByName (ctx context.Context , name string ) (* User , error ) {
10011020 if len (name ) == 0 {
10021021 return nil , ErrUserNotExist {Name : name }
@@ -1027,8 +1046,8 @@ func GetUserEmailsByNames(ctx context.Context, names []string) []string {
10271046 return mails
10281047}
10291048
1030- // GetMaileableUsersByIDs gets users from ids, but only if they can receive mails
1031- func GetMaileableUsersByIDs (ctx context.Context , ids []int64 , isMention bool ) ([]* User , error ) {
1049+ // GetMailableUsersByIDs gets users from ids, but only if they can receive mails
1050+ func GetMailableUsersByIDs (ctx context.Context , ids []int64 , isMention bool ) ([]* User , error ) {
10321051 if len (ids ) == 0 {
10331052 return nil , nil
10341053 }
@@ -1053,17 +1072,6 @@ func GetMaileableUsersByIDs(ctx context.Context, ids []int64, isMention bool) ([
10531072 Find (& ous )
10541073}
10551074
1056- // GetUserNamesByIDs returns usernames for all resolved users from a list of Ids.
1057- func GetUserNamesByIDs (ctx context.Context , ids []int64 ) ([]string , error ) {
1058- unames := make ([]string , 0 , len (ids ))
1059- err := db .GetEngine (ctx ).In ("id" , ids ).
1060- Table ("user" ).
1061- Asc ("name" ).
1062- Cols ("name" ).
1063- Find (& unames )
1064- return unames , err
1065- }
1066-
10671075// GetUserNameByID returns username for the id
10681076func GetUserNameByID (ctx context.Context , id int64 ) (string , error ) {
10691077 var name string
0 commit comments