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  }