2020-10-14 16:07:51 +03:00
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"crypto/md5"
"fmt"
"image/png"
"io"
"strconv"
"strings"
"code.gitea.io/gitea/modules/avatar"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
)
// CustomAvatarRelativePath returns user custom avatar relative path.
func ( u * User ) CustomAvatarRelativePath ( ) string {
return u . Avatar
}
// GenerateRandomAvatar generates a random avatar for user.
func ( u * User ) GenerateRandomAvatar ( ) error {
return u . generateRandomAvatar ( x )
}
func ( u * User ) generateRandomAvatar ( e Engine ) error {
seed := u . Email
if len ( seed ) == 0 {
seed = u . Name
}
img , err := avatar . RandomImage ( [ ] byte ( seed ) )
if err != nil {
return fmt . Errorf ( "RandomImage: %v" , err )
}
2020-10-23 20:55:10 +03:00
2021-01-02 21:01:09 +03:00
u . Avatar = HashEmail ( seed )
2020-10-14 16:07:51 +03:00
2021-01-02 21:01:09 +03:00
// Don't share the images so that we can delete them easily
2020-10-14 16:07:51 +03:00
if err := storage . SaveFrom ( storage . Avatars , u . CustomAvatarRelativePath ( ) , func ( w io . Writer ) error {
if err := png . Encode ( w , img ) ; err != nil {
log . Error ( "Encode: %v" , err )
}
return err
} ) ; err != nil {
return fmt . Errorf ( "Failed to create dir %s: %v" , u . CustomAvatarRelativePath ( ) , err )
}
if _ , err := e . ID ( u . ID ) . Cols ( "avatar" ) . Update ( u ) ; err != nil {
return err
}
log . Info ( "New random avatar created: %d" , u . ID )
return nil
}
// SizedRelAvatarLink returns a link to the user's avatar via
// the local explore page. Function returns immediately.
// When applicable, the link is for an avatar of the indicated size (in pixels).
func ( u * User ) SizedRelAvatarLink ( size int ) string {
2021-02-20 00:36:43 +03:00
return setting . AppSubURL + "/user/avatar/" + u . Name + "/" + strconv . Itoa ( size )
2020-10-14 16:07:51 +03:00
}
// RealSizedAvatarLink returns a link to the user's avatar. When
// applicable, the link is for an avatar of the indicated size (in pixels).
//
// This function make take time to return when federated avatars
// are in use, due to a DNS lookup need
//
func ( u * User ) RealSizedAvatarLink ( size int ) string {
if u . ID == - 1 {
2020-12-03 21:46:11 +03:00
return DefaultAvatarLink ( )
2020-10-14 16:07:51 +03:00
}
switch {
case u . UseCustomAvatar :
if u . Avatar == "" {
2020-12-03 21:46:11 +03:00
return DefaultAvatarLink ( )
2020-10-14 16:07:51 +03:00
}
2021-04-17 01:22:25 +03:00
if size > 0 {
return setting . AppSubURL + "/avatars/" + u . Avatar + "?size=" + strconv . Itoa ( size )
}
2020-10-14 16:07:51 +03:00
return setting . AppSubURL + "/avatars/" + u . Avatar
case setting . DisableGravatar , setting . OfflineMode :
if u . Avatar == "" {
if err := u . GenerateRandomAvatar ( ) ; err != nil {
log . Error ( "GenerateRandomAvatar: %v" , err )
}
}
2021-04-17 01:22:25 +03:00
if size > 0 {
return setting . AppSubURL + "/avatars/" + u . Avatar + "?size=" + strconv . Itoa ( size )
}
2020-10-14 16:07:51 +03:00
return setting . AppSubURL + "/avatars/" + u . Avatar
}
2020-12-03 21:46:11 +03:00
return SizedAvatarLink ( u . AvatarEmail , size )
2020-10-14 16:07:51 +03:00
}
// RelAvatarLink returns a relative link to the user's avatar. The link
// may either be a sub-URL to this site, or a full URL to an external avatar
// service.
func ( u * User ) RelAvatarLink ( ) string {
2020-12-03 21:46:11 +03:00
return u . SizedRelAvatarLink ( DefaultAvatarSize )
2020-10-14 16:07:51 +03:00
}
// AvatarLink returns user avatar absolute link.
func ( u * User ) AvatarLink ( ) string {
link := u . RelAvatarLink ( )
if link [ 0 ] == '/' && link [ 1 ] != '/' {
return setting . AppURL + strings . TrimPrefix ( link , setting . AppSubURL ) [ 1 : ]
}
return link
}
// UploadAvatar saves custom avatar for user.
// FIXME: split uploads to different subdirs in case we have massive users.
func ( u * User ) UploadAvatar ( data [ ] byte ) error {
m , err := avatar . Prepare ( data )
if err != nil {
return err
}
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
u . UseCustomAvatar = true
// Different users can upload same image as avatar
// If we prefix it with u.ID, it will be separated
// Otherwise, if any of the users delete his avatar
// Other users will lose their avatars too.
u . Avatar = fmt . Sprintf ( "%x" , md5 . Sum ( [ ] byte ( fmt . Sprintf ( "%d-%x" , u . ID , md5 . Sum ( data ) ) ) ) )
2021-01-02 21:01:09 +03:00
if err = updateUserCols ( sess , u , "use_custom_avatar" , "avatar" ) ; err != nil {
2020-10-14 16:07:51 +03:00
return fmt . Errorf ( "updateUser: %v" , err )
}
if err := storage . SaveFrom ( storage . Avatars , u . CustomAvatarRelativePath ( ) , func ( w io . Writer ) error {
if err := png . Encode ( w , * m ) ; err != nil {
log . Error ( "Encode: %v" , err )
}
return err
} ) ; err != nil {
return fmt . Errorf ( "Failed to create dir %s: %v" , u . CustomAvatarRelativePath ( ) , err )
}
return sess . Commit ( )
}
// DeleteAvatar deletes the user's custom avatar.
func ( u * User ) DeleteAvatar ( ) error {
aPath := u . CustomAvatarRelativePath ( )
log . Trace ( "DeleteAvatar[%d]: %s" , u . ID , aPath )
if len ( u . Avatar ) > 0 {
if err := storage . Avatars . Delete ( aPath ) ; err != nil {
return fmt . Errorf ( "Failed to remove %s: %v" , aPath , err )
}
}
u . UseCustomAvatar = false
u . Avatar = ""
if _ , err := x . ID ( u . ID ) . Cols ( "avatar, use_custom_avatar" ) . Update ( u ) ; err != nil {
return fmt . Errorf ( "UpdateUser: %v" , err )
}
return nil
}