parent
							
								
									7bab3d2fb1
								
							
						
					
					
						commit
						ab62da283a
					
				
					 8 changed files with 111 additions and 37 deletions
				
			
		|  | @ -315,10 +315,9 @@ func (u *User) generateRandomAvatar(e Engine) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RelAvatarLink returns relative avatar link to the site domain,
 | // SizedRelAvatarLink returns a relative link to the user's avatar. When
 | ||||||
| // which includes app sub-url as prefix. However, it is possible
 | // applicable, the link is for an avatar of the indicated size (in pixels).
 | ||||||
| // to return full URL if user enables Gravatar-like service.
 | func (u *User) SizedRelAvatarLink(size int) string { | ||||||
| func (u *User) RelAvatarLink() string { |  | ||||||
| 	if u.ID == -1 { | 	if u.ID == -1 { | ||||||
| 		return base.DefaultAvatarLink() | 		return base.DefaultAvatarLink() | ||||||
| 	} | 	} | ||||||
|  | @ -338,7 +337,14 @@ func (u *User) RelAvatarLink() string { | ||||||
| 
 | 
 | ||||||
| 		return setting.AppSubURL + "/avatars/" + u.Avatar | 		return setting.AppSubURL + "/avatars/" + u.Avatar | ||||||
| 	} | 	} | ||||||
| 	return base.AvatarLink(u.AvatarEmail) | 	return base.SizedAvatarLink(u.AvatarEmail, size) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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 { | ||||||
|  | 	return u.SizedRelAvatarLink(base.DefaultAvatarSize) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // AvatarLink returns user avatar absolute link.
 | // AvatarLink returns user avatar absolute link.
 | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ import ( | ||||||
| 	"math" | 	"math" | ||||||
| 	"math/big" | 	"math/big" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
|  | 	"path" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -197,24 +199,59 @@ func DefaultAvatarLink() string { | ||||||
| 	return setting.AppSubURL + "/img/avatar_default.png" | 	return setting.AppSubURL + "/img/avatar_default.png" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // DefaultAvatarSize is a sentinel value for the default avatar size, as
 | ||||||
|  | // determined by the avatar-hosting service.
 | ||||||
|  | const DefaultAvatarSize = -1 | ||||||
|  | 
 | ||||||
|  | // libravatarURL returns the URL for the given email. This function should only
 | ||||||
|  | // be called if a federated avatar service is enabled.
 | ||||||
|  | func libravatarURL(email string) (*url.URL, error) { | ||||||
|  | 	urlStr, err := setting.LibravatarService.FromEmail(email) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "LibravatarService.FromEmail(email=%s): error %v", email, err) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	u, err := url.Parse(urlStr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "Failed to parse libravatar url(%s): error %v", urlStr, err) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return u, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SizedAvatarLink returns a sized link to the avatar for the given email
 | ||||||
|  | // address.
 | ||||||
|  | func SizedAvatarLink(email string, size int) string { | ||||||
|  | 	var avatarURL *url.URL | ||||||
|  | 	if setting.EnableFederatedAvatar && setting.LibravatarService != nil { | ||||||
|  | 		var err error | ||||||
|  | 		avatarURL, err = libravatarURL(email) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return DefaultAvatarLink() | ||||||
|  | 		} | ||||||
|  | 	} else if !setting.DisableGravatar { | ||||||
|  | 		// copy GravatarSourceURL, because we will modify its Path.
 | ||||||
|  | 		copyOfGravatarSourceURL := *setting.GravatarSourceURL | ||||||
|  | 		avatarURL = ©OfGravatarSourceURL | ||||||
|  | 		avatarURL.Path = path.Join(avatarURL.Path, HashEmail(email)) | ||||||
|  | 	} else { | ||||||
|  | 		return DefaultAvatarLink() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	vals := avatarURL.Query() | ||||||
|  | 	vals.Set("d", "identicon") | ||||||
|  | 	if size != DefaultAvatarSize { | ||||||
|  | 		vals.Set("s", strconv.Itoa(size)) | ||||||
|  | 	} | ||||||
|  | 	avatarURL.RawQuery = vals.Encode() | ||||||
|  | 	return avatarURL.String() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // AvatarLink returns relative avatar link to the site domain by given email,
 | // AvatarLink returns relative avatar link to the site domain by given email,
 | ||||||
| // which includes app sub-url as prefix. However, it is possible
 | // which includes app sub-url as prefix. However, it is possible
 | ||||||
| // to return full URL if user enables Gravatar-like service.
 | // to return full URL if user enables Gravatar-like service.
 | ||||||
| func AvatarLink(email string) string { | func AvatarLink(email string) string { | ||||||
| 	if setting.EnableFederatedAvatar && setting.LibravatarService != nil { | 	return SizedAvatarLink(email, DefaultAvatarSize) | ||||||
| 		url, err := setting.LibravatarService.FromEmail(email) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error(4, "LibravatarService.FromEmail(email=%s): error %v", email, err) |  | ||||||
| 			return DefaultAvatarLink() |  | ||||||
| 		} |  | ||||||
| 		return url |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if !setting.DisableGravatar { |  | ||||||
| 		return setting.GravatarSource + HashEmail(email) + "?d=identicon" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return DefaultAvatarLink() |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Seconds-based time units
 | // Seconds-based time units
 | ||||||
|  |  | ||||||
|  | @ -1,11 +1,13 @@ | ||||||
| package base | package base | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 
 | ||||||
| 	"github.com/Unknwon/i18n" | 	"github.com/Unknwon/i18n" | ||||||
| 	macaroni18n "github.com/go-macaron/i18n" | 	macaroni18n "github.com/go-macaron/i18n" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
|  | @ -126,16 +128,40 @@ func TestHashEmail(t *testing.T) { | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestAvatarLink(t *testing.T) { | const gravatarSource = "https://secure.gravatar.com/avatar/" | ||||||
|  | 
 | ||||||
|  | func disableGravatar() { | ||||||
| 	setting.EnableFederatedAvatar = false | 	setting.EnableFederatedAvatar = false | ||||||
| 	setting.LibravatarService = nil | 	setting.LibravatarService = nil | ||||||
| 	setting.DisableGravatar = true | 	setting.DisableGravatar = true | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	assert.Equal(t, "/img/avatar_default.png", AvatarLink("")) | func enableGravatar(t *testing.T) { | ||||||
| 
 |  | ||||||
| 	setting.DisableGravatar = false | 	setting.DisableGravatar = false | ||||||
|  | 	var err error | ||||||
|  | 	setting.GravatarSourceURL, err = url.Parse(gravatarSource) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestSizedAvatarLink(t *testing.T) { | ||||||
|  | 	disableGravatar() | ||||||
|  | 	assert.Equal(t, "/img/avatar_default.png", | ||||||
|  | 		SizedAvatarLink("gitea@example.com", 100)) | ||||||
|  | 
 | ||||||
|  | 	enableGravatar(t) | ||||||
| 	assert.Equal(t, | 	assert.Equal(t, | ||||||
| 		"353cbad9b58e69c96154ad99f92bedc7?d=identicon", | 		"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100", | ||||||
|  | 		SizedAvatarLink("gitea@example.com", 100), | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestAvatarLink(t *testing.T) { | ||||||
|  | 	disableGravatar() | ||||||
|  | 	assert.Equal(t, "/img/avatar_default.png", AvatarLink("gitea@example.com")) | ||||||
|  | 
 | ||||||
|  | 	enableGravatar(t) | ||||||
|  | 	assert.Equal(t, | ||||||
|  | 		"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon", | ||||||
| 		AvatarLink("gitea@example.com"), | 		AvatarLink("gitea@example.com"), | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -326,6 +326,7 @@ var ( | ||||||
| 	// Picture settings
 | 	// Picture settings
 | ||||||
| 	AvatarUploadPath      string | 	AvatarUploadPath      string | ||||||
| 	GravatarSource        string | 	GravatarSource        string | ||||||
|  | 	GravatarSourceURL     *url.URL | ||||||
| 	DisableGravatar       bool | 	DisableGravatar       bool | ||||||
| 	EnableFederatedAvatar bool | 	EnableFederatedAvatar bool | ||||||
| 	LibravatarService     *libravatar.Libravatar | 	LibravatarService     *libravatar.Libravatar | ||||||
|  | @ -1027,18 +1028,22 @@ func NewContext() { | ||||||
| 	if DisableGravatar { | 	if DisableGravatar { | ||||||
| 		EnableFederatedAvatar = false | 		EnableFederatedAvatar = false | ||||||
| 	} | 	} | ||||||
|  | 	if EnableFederatedAvatar || !DisableGravatar { | ||||||
|  | 		GravatarSourceURL, err = url.Parse(GravatarSource) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Fatal(4, "Failed to parse Gravatar URL(%s): %v", | ||||||
|  | 				GravatarSource, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if EnableFederatedAvatar { | 	if EnableFederatedAvatar { | ||||||
| 		LibravatarService = libravatar.New() | 		LibravatarService = libravatar.New() | ||||||
| 		parts := strings.Split(GravatarSource, "/") | 		if GravatarSourceURL.Scheme == "https" { | ||||||
| 		if len(parts) >= 3 { | 			LibravatarService.SetUseHTTPS(true) | ||||||
| 			if parts[0] == "https:" { | 			LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host) | ||||||
| 				LibravatarService.SetUseHTTPS(true) | 		} else { | ||||||
| 				LibravatarService.SetSecureFallbackHost(parts[2]) | 			LibravatarService.SetUseHTTPS(false) | ||||||
| 			} else { | 			LibravatarService.SetFallbackHost(GravatarSourceURL.Host) | ||||||
| 				LibravatarService.SetUseHTTPS(false) |  | ||||||
| 				LibravatarService.SetFallbackHost(parts[2]) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 		<div class="ui vertically grid head"> | 		<div class="ui vertically grid head"> | ||||||
| 			<div class="column"> | 			<div class="column"> | ||||||
| 				<div class="ui header"> | 				<div class="ui header"> | ||||||
| 					<img class="ui image" src="{{.RelAvatarLink}}?s=100"> | 					<img class="ui image" src="{{.SizedRelAvatarLink 100}}"> | ||||||
| 					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> | 					<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> | ||||||
| 
 | 
 | ||||||
| 					<div class="ui right"> | 					<div class="ui right"> | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 	<div class="ui container"> | 	<div class="ui container"> | ||||||
| 		<div class="ui grid"> | 		<div class="ui grid"> | ||||||
| 			<div class="ui sixteen wide column"> | 			<div class="ui sixteen wide column"> | ||||||
| 				<img class="ui left" id="org-avatar" src="{{.Org.RelAvatarLink}}?s=140"/> | 				<img class="ui left" id="org-avatar" src="{{.Org.SizedRelAvatarLink 140}}"/> | ||||||
| 				<div id="org-info"> | 				<div id="org-info"> | ||||||
| 					<div class="ui header"> | 					<div class="ui header"> | ||||||
| 						{{.Org.DisplayName}} | 						{{.Org.DisplayName}} | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| 			{{range .Members}} | 			{{range .Members}} | ||||||
| 				<div class="item ui grid"> | 				<div class="item ui grid"> | ||||||
| 					<div class="ui one wide column"> | 					<div class="ui one wide column"> | ||||||
| 						<img class="ui avatar" src="{{.RelAvatarLink}}?s=48"> | 						<img class="ui avatar" src="{{.SizedRelAvatarLink 48}}"> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="ui three wide column"> | 					<div class="ui three wide column"> | ||||||
| 						<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div> | 						<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div> | ||||||
|  |  | ||||||
|  | @ -6,11 +6,11 @@ | ||||||
| 				<div class="ui card"> | 				<div class="ui card"> | ||||||
| 					{{if eq .SignedUserName .Owner.Name}} | 					{{if eq .SignedUserName .Owner.Name}} | ||||||
| 						<a class="image poping up" href="{{AppSubUrl}}/user/settings/avatar" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center"> | 						<a class="image poping up" href="{{AppSubUrl}}/user/settings/avatar" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center"> | ||||||
| 							<img src="{{.Owner.RelAvatarLink}}?s=290" title="{{.Owner.Name}}"/> | 							<img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}"/> | ||||||
| 						</a> | 						</a> | ||||||
| 					{{else}} | 					{{else}} | ||||||
| 						<span class="image"> | 						<span class="image"> | ||||||
| 							<img src="{{.Owner.RelAvatarLink}}?s=290" title="{{.Owner.Name}}"/> | 							<img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}"/> | ||||||
| 						</span> | 						</span> | ||||||
| 					{{end}} | 					{{end}} | ||||||
| 					<div class="content"> | 					<div class="content"> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue