parent
81fd8c8fb6
commit
8a19c6b9a2
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"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,6 +325,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
|
||||||
|
@ -1026,18 +1027,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">
|
||||||
|
|
Reference in a new issue