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.6KB

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