code.gitea.io/gitea@v1.22.3/models/repo/avatar.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repo 5 6 import ( 7 "context" 8 "fmt" 9 "image/png" 10 "io" 11 "net/url" 12 13 "code.gitea.io/gitea/models/db" 14 "code.gitea.io/gitea/modules/avatar" 15 "code.gitea.io/gitea/modules/httplib" 16 "code.gitea.io/gitea/modules/log" 17 "code.gitea.io/gitea/modules/setting" 18 "code.gitea.io/gitea/modules/storage" 19 ) 20 21 // CustomAvatarRelativePath returns repository custom avatar file path. 22 func (repo *Repository) CustomAvatarRelativePath() string { 23 return repo.Avatar 24 } 25 26 // ExistsWithAvatarAtStoragePath returns true if there is a user with this Avatar 27 func ExistsWithAvatarAtStoragePath(ctx context.Context, storagePath string) (bool, error) { 28 // See func (repo *Repository) CustomAvatarRelativePath() 29 // repo.Avatar is used directly as the storage path - therefore we can check for existence directly using the path 30 return db.GetEngine(ctx).Where("`avatar`=?", storagePath).Exist(new(Repository)) 31 } 32 33 // RelAvatarLink returns a relative link to the repository's avatar. 34 func (repo *Repository) RelAvatarLink(ctx context.Context) string { 35 return repo.relAvatarLink(ctx) 36 } 37 38 // generateRandomAvatar generates a random avatar for repository. 39 func generateRandomAvatar(ctx context.Context, repo *Repository) error { 40 idToString := fmt.Sprintf("%d", repo.ID) 41 42 seed := idToString 43 img, err := avatar.RandomImage([]byte(seed)) 44 if err != nil { 45 return fmt.Errorf("RandomImage: %w", err) 46 } 47 48 repo.Avatar = idToString 49 50 if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error { 51 if err := png.Encode(w, img); err != nil { 52 log.Error("Encode: %v", err) 53 } 54 return err 55 }); err != nil { 56 return fmt.Errorf("Failed to create dir %s: %w", repo.CustomAvatarRelativePath(), err) 57 } 58 59 log.Info("New random avatar created for repository: %d", repo.ID) 60 61 if _, err := db.GetEngine(ctx).ID(repo.ID).Cols("avatar").NoAutoTime().Update(repo); err != nil { 62 return err 63 } 64 65 return nil 66 } 67 68 func (repo *Repository) relAvatarLink(ctx context.Context) string { 69 // If no avatar - path is empty 70 avatarPath := repo.CustomAvatarRelativePath() 71 if len(avatarPath) == 0 { 72 switch mode := setting.RepoAvatar.Fallback; mode { 73 case "image": 74 return setting.RepoAvatar.FallbackImage 75 case "random": 76 if err := generateRandomAvatar(ctx, repo); err != nil { 77 log.Error("generateRandomAvatar: %v", err) 78 } 79 default: 80 // default behaviour: do not display avatar 81 return "" 82 } 83 } 84 return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar) 85 } 86 87 // AvatarLink returns the full avatar url with http host or the empty string if the repo doesn't have an avatar. 88 // 89 // TODO: refactor it to a relative URL, but it is still used in API response at the moment 90 func (repo *Repository) AvatarLink(ctx context.Context) string { 91 relLink := repo.relAvatarLink(ctx) 92 if relLink != "" { 93 return httplib.MakeAbsoluteURL(ctx, relLink) 94 } 95 return "" 96 }