You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

user_avatar.go 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "crypto/md5"
  7. "fmt"
  8. "image/png"
  9. "io"
  10. "strconv"
  11. "strings"
  12. "code.gitea.io/gitea/modules/avatar"
  13. "code.gitea.io/gitea/modules/base"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/setting"
  16. "code.gitea.io/gitea/modules/storage"
  17. )
  18. // CustomAvatarRelativePath returns user custom avatar relative path.
  19. func (u *User) CustomAvatarRelativePath() string {
  20. return u.Avatar
  21. }
  22. // GenerateRandomAvatar generates a random avatar for user.
  23. func (u *User) GenerateRandomAvatar() error {
  24. return u.generateRandomAvatar(x)
  25. }
  26. func (u *User) generateRandomAvatar(e Engine) error {
  27. seed := u.Email
  28. if len(seed) == 0 {
  29. seed = u.Name
  30. }
  31. img, err := avatar.RandomImage([]byte(seed))
  32. if err != nil {
  33. return fmt.Errorf("RandomImage: %v", err)
  34. }
  35. // NOTICE for random avatar, it still uses id as avatar name, but custom avatar use md5
  36. // since random image is not a user's photo, there is no security for enumable
  37. if u.Avatar == "" {
  38. u.Avatar = fmt.Sprintf("%d", u.ID)
  39. }
  40. if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
  41. if err := png.Encode(w, img); err != nil {
  42. log.Error("Encode: %v", err)
  43. }
  44. return err
  45. }); err != nil {
  46. return fmt.Errorf("Failed to create dir %s: %v", u.CustomAvatarRelativePath(), err)
  47. }
  48. if _, err := e.ID(u.ID).Cols("avatar").Update(u); err != nil {
  49. return err
  50. }
  51. log.Info("New random avatar created: %d", u.ID)
  52. return nil
  53. }
  54. // SizedRelAvatarLink returns a link to the user's avatar via
  55. // the local explore page. Function returns immediately.
  56. // When applicable, the link is for an avatar of the indicated size (in pixels).
  57. func (u *User) SizedRelAvatarLink(size int) string {
  58. return strings.TrimSuffix(setting.AppSubURL, "/") + "/user/avatar/" + u.Name + "/" + strconv.Itoa(size)
  59. }
  60. // RealSizedAvatarLink returns a link to the user's avatar. When
  61. // applicable, the link is for an avatar of the indicated size (in pixels).
  62. //
  63. // This function make take time to return when federated avatars
  64. // are in use, due to a DNS lookup need
  65. //
  66. func (u *User) RealSizedAvatarLink(size int) string {
  67. if u.ID == -1 {
  68. return base.DefaultAvatarLink()
  69. }
  70. switch {
  71. case u.UseCustomAvatar:
  72. if u.Avatar == "" {
  73. return base.DefaultAvatarLink()
  74. }
  75. return setting.AppSubURL + "/avatars/" + u.Avatar
  76. case setting.DisableGravatar, setting.OfflineMode:
  77. if u.Avatar == "" {
  78. if err := u.GenerateRandomAvatar(); err != nil {
  79. log.Error("GenerateRandomAvatar: %v", err)
  80. }
  81. }
  82. return setting.AppSubURL + "/avatars/" + u.Avatar
  83. }
  84. return base.SizedAvatarLink(u.AvatarEmail, size)
  85. }
  86. // RelAvatarLink returns a relative link to the user's avatar. The link
  87. // may either be a sub-URL to this site, or a full URL to an external avatar
  88. // service.
  89. func (u *User) RelAvatarLink() string {
  90. return u.SizedRelAvatarLink(base.DefaultAvatarSize)
  91. }
  92. // AvatarLink returns user avatar absolute link.
  93. func (u *User) AvatarLink() string {
  94. link := u.RelAvatarLink()
  95. if link[0] == '/' && link[1] != '/' {
  96. return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL)[1:]
  97. }
  98. return link
  99. }
  100. // UploadAvatar saves custom avatar for user.
  101. // FIXME: split uploads to different subdirs in case we have massive users.
  102. func (u *User) UploadAvatar(data []byte) error {
  103. m, err := avatar.Prepare(data)
  104. if err != nil {
  105. return err
  106. }
  107. sess := x.NewSession()
  108. defer sess.Close()
  109. if err = sess.Begin(); err != nil {
  110. return err
  111. }
  112. u.UseCustomAvatar = true
  113. // Different users can upload same image as avatar
  114. // If we prefix it with u.ID, it will be separated
  115. // Otherwise, if any of the users delete his avatar
  116. // Other users will lose their avatars too.
  117. u.Avatar = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
  118. if err = updateUser(sess, u); err != nil {
  119. return fmt.Errorf("updateUser: %v", err)
  120. }
  121. if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
  122. if err := png.Encode(w, *m); err != nil {
  123. log.Error("Encode: %v", err)
  124. }
  125. return err
  126. }); err != nil {
  127. return fmt.Errorf("Failed to create dir %s: %v", u.CustomAvatarRelativePath(), err)
  128. }
  129. return sess.Commit()
  130. }
  131. // DeleteAvatar deletes the user's custom avatar.
  132. func (u *User) DeleteAvatar() error {
  133. aPath := u.CustomAvatarRelativePath()
  134. log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath)
  135. if len(u.Avatar) > 0 {
  136. if err := storage.Avatars.Delete(aPath); err != nil {
  137. return fmt.Errorf("Failed to remove %s: %v", aPath, err)
  138. }
  139. }
  140. u.UseCustomAvatar = false
  141. u.Avatar = ""
  142. if _, err := x.ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
  143. return fmt.Errorf("UpdateUser: %v", err)
  144. }
  145. return nil
  146. }