A fork of Gitea (see branch `mj`) adding Majority Judgment Polls 𐄷 over Issues and Merge Requests. https://git.mieuxvoter.fr
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.

171 lines
4.8 KiB

  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 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. if size > 0 {
  72. return setting.AppSubURL + "/avatars/" + u.Avatar + "?size=" + strconv.Itoa(size)
  73. }
  74. return setting.AppSubURL + "/avatars/" + u.Avatar
  75. case setting.DisableGravatar, setting.OfflineMode:
  76. if u.Avatar == "" {
  77. if err := u.GenerateRandomAvatar(); err != nil {
  78. log.Error("GenerateRandomAvatar: %v", err)
  79. }
  80. }
  81. if size > 0 {
  82. return setting.AppSubURL + "/avatars/" + u.Avatar + "?size=" + strconv.Itoa(size)
  83. }
  84. return setting.AppSubURL + "/avatars/" + u.Avatar
  85. }
  86. return SizedAvatarLink(u.AvatarEmail, size)
  87. }
  88. // RelAvatarLink returns a relative link to the user's avatar. The link
  89. // may either be a sub-URL to this site, or a full URL to an external avatar
  90. // service.
  91. func (u *User) RelAvatarLink() string {
  92. return u.SizedRelAvatarLink(DefaultAvatarSize)
  93. }
  94. // AvatarLink returns user avatar absolute link.
  95. func (u *User) AvatarLink() string {
  96. link := u.RelAvatarLink()
  97. if link[0] == '/' && link[1] != '/' {
  98. return setting.AppURL + strings.TrimPrefix(link, setting.AppSubURL)[1:]
  99. }
  100. return link
  101. }
  102. // UploadAvatar saves custom avatar for user.
  103. // FIXME: split uploads to different subdirs in case we have massive users.
  104. func (u *User) UploadAvatar(data []byte) error {
  105. m, err := avatar.Prepare(data)
  106. if err != nil {
  107. return err
  108. }
  109. sess := x.NewSession()
  110. defer sess.Close()
  111. if err = sess.Begin(); err != nil {
  112. return err
  113. }
  114. u.UseCustomAvatar = true
  115. // Different users can upload same image as avatar
  116. // If we prefix it with u.ID, it will be separated
  117. // Otherwise, if any of the users delete his avatar
  118. // Other users will lose their avatars too.
  119. u.Avatar = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
  120. if err = updateUserCols(sess, u, "use_custom_avatar", "avatar"); err != nil {
  121. return fmt.Errorf("updateUser: %v", err)
  122. }
  123. if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
  124. if err := png.Encode(w, *m); err != nil {
  125. log.Error("Encode: %v", err)
  126. }
  127. return err
  128. }); err != nil {
  129. return fmt.Errorf("Failed to create dir %s: %v", u.CustomAvatarRelativePath(), err)
  130. }
  131. return sess.Commit()
  132. }
  133. // DeleteAvatar deletes the user's custom avatar.
  134. func (u *User) DeleteAvatar() error {
  135. aPath := u.CustomAvatarRelativePath()
  136. log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath)
  137. if len(u.Avatar) > 0 {
  138. if err := storage.Avatars.Delete(aPath); err != nil {
  139. return fmt.Errorf("Failed to remove %s: %v", aPath, err)
  140. }
  141. }
  142. u.UseCustomAvatar = false
  143. u.Avatar = ""
  144. if _, err := x.ID(u.ID).Cols("avatar, use_custom_avatar").Update(u); err != nil {
  145. return fmt.Errorf("UpdateUser: %v", err)
  146. }
  147. return nil
  148. }