]> source.dussan.org Git - gitea.git/commitdiff
Direct avatar rendering (#13649)
authorsilverwind <me@silverwind.io>
Thu, 3 Dec 2020 18:46:11 +0000 (19:46 +0100)
committerGitHub <noreply@github.com>
Thu, 3 Dec 2020 18:46:11 +0000 (19:46 +0100)
* Direct avatar rendering

This adds new template helpers for avatar rendering which output image
elements with direct links to avatars which makes them cacheable by the
browsers.

This should be a major performance improvment for pages with many avatars.

* fix avatars of other user's profile pages

* fix top border on user avatar name

* uncircle avatars

* remove old incomplete avatar selector

* use title attribute for name and add it back on blame

* minor refactor

* tweak comments

* fix url path join and adjust test to new result

* dedupe functions

62 files changed:
models/action.go
models/avatar.go
models/avatar_test.go [new file with mode: 0644]
models/user_avatar.go
modules/auth/sso/sspi_windows.go
modules/base/tool.go
modules/base/tool_test.go
modules/repository/commits.go
modules/templates/helper.go
routers/repo/blame.go
routers/user/avatar.go
templates/base/head_navbar.tmpl
templates/explore/organizations.tmpl
templates/explore/repo_list.tmpl
templates/explore/users.tmpl
templates/org/header.tmpl
templates/org/home.tmpl
templates/org/member/members.tmpl
templates/org/team/members.tmpl
templates/org/team/teams.tmpl
templates/repo/commit_page.tmpl
templates/repo/commits_list.tmpl
templates/repo/commits_list_small.tmpl
templates/repo/create.tmpl
templates/repo/diff/comments.tmpl
templates/repo/editor/commit_form.tmpl
templates/repo/forks.tmpl
templates/repo/graph/commits.tmpl
templates/repo/header.tmpl
templates/repo/header_icon.tmpl
templates/repo/issue/list.tmpl
templates/repo/issue/milestone_issues.tmpl
templates/repo/issue/new_form.tmpl
templates/repo/issue/view_content.tmpl
templates/repo/issue/view_content/comments.tmpl
templates/repo/issue/view_content/pull.tmpl
templates/repo/issue/view_content/sidebar.tmpl
templates/repo/migrate/git.tmpl
templates/repo/migrate/gitea.tmpl
templates/repo/migrate/github.tmpl
templates/repo/migrate/gitlab.tmpl
templates/repo/projects/view.tmpl
templates/repo/pulls/fork.tmpl
templates/repo/release/list.tmpl
templates/repo/settings/collaboration.tmpl
templates/repo/settings/lfs_locks.tmpl
templates/repo/settings/protected_branch.tmpl
templates/repo/shabox_badge.tmpl
templates/repo/user_cards.tmpl
templates/repo/view_list.tmpl
templates/shared/issuelist.tmpl
templates/user/dashboard/feeds.tmpl
templates/user/dashboard/navbar.tmpl
templates/user/profile.tmpl
templates/user/project.tmpl
templates/user/settings/organization.tmpl
web_src/less/_base.less
web_src/less/_dashboard.less
web_src/less/_organization.less
web_src/less/_repository.less
web_src/less/_user.less
web_src/less/helpers.less

index f4d163afa3f53aec1ce538da69fb199d653bcd68..ca186033a6bcc838c9ec0fcc3e9f59c9b59bb29b 100644 (file)
@@ -140,12 +140,6 @@ func (a *Action) GetDisplayNameTitle() string {
        return a.GetActFullName()
 }
 
-// GetActAvatar the action's user's avatar link
-func (a *Action) GetActAvatar() string {
-       a.loadActUser()
-       return a.ActUser.RelAvatarLink()
-}
-
 // GetRepoUserName returns the name of the action repository owner.
 func (a *Action) GetRepoUserName() string {
        a.loadRepo()
index c9ba2961ef7b9ffe992d67f218c484fcb7f2dfe7..ac260fbd935fa0267c15a9ee65048b226480b6d9 100644 (file)
@@ -8,9 +8,13 @@ import (
        "crypto/md5"
        "fmt"
        "net/url"
+       "path"
+       "strconv"
        "strings"
 
+       "code.gitea.io/gitea/modules/base"
        "code.gitea.io/gitea/modules/cache"
+       "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
 )
 
@@ -20,6 +24,28 @@ type EmailHash struct {
        Email string `xorm:"UNIQUE NOT NULL"`
 }
 
+// DefaultAvatarLink the default avatar link
+func DefaultAvatarLink() string {
+       u, err := url.Parse(setting.AppSubURL)
+       if err != nil {
+               log.Error("GetUserByEmail: %v", err)
+               return ""
+       }
+
+       u.Path = path.Join(u.Path, "/img/avatar_default.png")
+       return u.String()
+}
+
+// DefaultAvatarSize is a sentinel value for the default avatar size, as
+// determined by the avatar-hosting service.
+const DefaultAvatarSize = -1
+
+// HashEmail hashes email address to MD5 string.
+// https://en.gravatar.com/site/implement/hash/
+func HashEmail(email string) string {
+       return base.EncodeMD5(strings.ToLower(strings.TrimSpace(email)))
+}
+
 // GetEmailForHash converts a provided md5sum to the email
 func GetEmailForHash(md5Sum string) (string, error) {
        return cache.GetString("Avatar:"+md5Sum, func() (string, error) {
@@ -32,8 +58,24 @@ func GetEmailForHash(md5Sum string) (string, error) {
        })
 }
 
-// AvatarLink returns an avatar link for a provided email
-func AvatarLink(email string) string {
+// 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("LibravatarService.FromEmail(email=%s): error %v", email, err)
+               return nil, err
+       }
+       u, err := url.Parse(urlStr)
+       if err != nil {
+               log.Error("Failed to parse libravatar url(%s): error %v", urlStr, err)
+               return nil, err
+       }
+       return u, nil
+}
+
+// HashedAvatarLink returns an avatar link for a provided email
+func HashedAvatarLink(email string) string {
        lowerEmail := strings.ToLower(strings.TrimSpace(email))
        sum := fmt.Sprintf("%x", md5.Sum([]byte(lowerEmail)))
        _, _ = cache.GetString("Avatar:"+sum, func() (string, error) {
@@ -57,3 +99,34 @@ func AvatarLink(email string) string {
        })
        return setting.AppSubURL + "/avatar/" + url.PathEscape(sum)
 }
+
+// MakeFinalAvatarURL constructs the final avatar URL string
+func MakeFinalAvatarURL(u *url.URL, size int) string {
+       vals := u.Query()
+       vals.Set("d", "identicon")
+       if size != DefaultAvatarSize {
+               vals.Set("s", strconv.Itoa(size))
+       }
+       u.RawQuery = vals.Encode()
+       return u.String()
+}
+
+// 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 {
+               // This is the slow path that would need to call LibravatarURL() which
+               // does DNS lookups. Avoid it by issuing a redirect so we don't block
+               // the template render with network requests.
+               return HashedAvatarLink(email)
+       } else if !setting.DisableGravatar {
+               // copy GravatarSourceURL, because we will modify its Path.
+               copyOfGravatarSourceURL := *setting.GravatarSourceURL
+               avatarURL = &copyOfGravatarSourceURL
+               avatarURL.Path = path.Join(avatarURL.Path, HashEmail(email))
+       } else {
+               return DefaultAvatarLink()
+       }
+
+       return MakeFinalAvatarURL(avatarURL, size)
+}
diff --git a/models/avatar_test.go b/models/avatar_test.go
new file mode 100644 (file)
index 0000000..8954070
--- /dev/null
@@ -0,0 +1,52 @@
+// 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 (
+       "net/url"
+       "testing"
+
+       "code.gitea.io/gitea/modules/setting"
+
+       "github.com/stretchr/testify/assert"
+)
+
+const gravatarSource = "https://secure.gravatar.com/avatar/"
+
+func disableGravatar() {
+       setting.EnableFederatedAvatar = false
+       setting.LibravatarService = nil
+       setting.DisableGravatar = true
+}
+
+func enableGravatar(t *testing.T) {
+       setting.DisableGravatar = false
+       var err error
+       setting.GravatarSourceURL, err = url.Parse(gravatarSource)
+       assert.NoError(t, err)
+}
+
+func TestHashEmail(t *testing.T) {
+       assert.Equal(t,
+               "d41d8cd98f00b204e9800998ecf8427e",
+               HashEmail(""),
+       )
+       assert.Equal(t,
+               "353cbad9b58e69c96154ad99f92bedc7",
+               HashEmail("gitea@example.com"),
+       )
+}
+
+func TestSizedAvatarLink(t *testing.T) {
+       disableGravatar()
+       assert.Equal(t, "/suburl/img/avatar_default.png",
+               SizedAvatarLink("gitea@example.com", 100))
+
+       enableGravatar(t)
+       assert.Equal(t,
+               "https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
+               SizedAvatarLink("gitea@example.com", 100),
+       )
+}
index 2f9db5c2e2b5a62dfd784526c29b9ed33256fede..50c1c99c57bc4989916d01961640d31ebf00bc48 100644 (file)
@@ -13,7 +13,6 @@ import (
        "strings"
 
        "code.gitea.io/gitea/modules/avatar"
-       "code.gitea.io/gitea/modules/base"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
        "code.gitea.io/gitea/modules/storage"
@@ -41,7 +40,7 @@ func (u *User) generateRandomAvatar(e Engine) error {
        }
 
        if u.Avatar == "" {
-               u.Avatar = base.HashEmail(u.AvatarEmail)
+               u.Avatar = HashEmail(u.AvatarEmail)
        }
 
        if err := storage.SaveFrom(storage.Avatars, u.CustomAvatarRelativePath(), func(w io.Writer) error {
@@ -76,13 +75,13 @@ func (u *User) SizedRelAvatarLink(size int) string {
 //
 func (u *User) RealSizedAvatarLink(size int) string {
        if u.ID == -1 {
-               return base.DefaultAvatarLink()
+               return DefaultAvatarLink()
        }
 
        switch {
        case u.UseCustomAvatar:
                if u.Avatar == "" {
-                       return base.DefaultAvatarLink()
+                       return DefaultAvatarLink()
                }
                return setting.AppSubURL + "/avatars/" + u.Avatar
        case setting.DisableGravatar, setting.OfflineMode:
@@ -94,14 +93,14 @@ func (u *User) RealSizedAvatarLink(size int) string {
 
                return setting.AppSubURL + "/avatars/" + u.Avatar
        }
-       return base.SizedAvatarLink(u.AvatarEmail, size)
+       return 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)
+       return u.SizedRelAvatarLink(DefaultAvatarSize)
 }
 
 // AvatarLink returns user avatar absolute link.
index 00f15d97be5f741516f8cadefca9496ac1e970a8..62013737caac7a04026a63fc379e93e3c614f283 100644 (file)
@@ -168,7 +168,7 @@ func (s *SSPI) newUser(ctx *macaron.Context, username string, cfg *models.SSPICo
                IsActive:                     cfg.AutoActivateUsers,
                Language:                     cfg.DefaultLanguage,
                UseCustomAvatar:              true,
-               Avatar:                       base.DefaultAvatarLink(),
+               Avatar:                       models.DefaultAvatarLink(),
                EmailNotificationsPreference: models.EmailNotificationsDisabled,
        }
        if err := models.CreateUser(user); err != nil {
index a21fd9b0f4045a39fe8dc6d817943777907d40ed..2cc09fb25d6b575308f3dad30acb85a848fd741d 100644 (file)
@@ -12,9 +12,7 @@ import (
        "encoding/hex"
        "fmt"
        "net/http"
-       "net/url"
        "os"
-       "path"
        "path/filepath"
        "runtime"
        "strconv"
@@ -134,93 +132,6 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
        return code
 }
 
-// HashEmail hashes email address to MD5 string.
-// https://en.gravatar.com/site/implement/hash/
-func HashEmail(email string) string {
-       return EncodeMD5(strings.ToLower(strings.TrimSpace(email)))
-}
-
-// DefaultAvatarLink the default avatar link
-func DefaultAvatarLink() string {
-       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("LibravatarService.FromEmail(email=%s): error %v", email, err)
-               return nil, err
-       }
-       u, err := url.Parse(urlStr)
-       if err != nil {
-               log.Error("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 = &copyOfGravatarSourceURL
-               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()
-}
-
-// SizedAvatarLinkWithDomain returns a sized link to the avatar for the given email
-// address.
-func SizedAvatarLinkWithDomain(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 = &copyOfGravatarSourceURL
-               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()
-}
-
 // FileSize calculates the file size and generate user-friendly string.
 func FileSize(s int64) string {
        return humanize.IBytes(uint64(s))
index f765fd0db0e3cd4364e087535232405ac9ae01d1..0c5bd66579c42874333dfcbed8e4937da85ec464 100644 (file)
@@ -5,11 +5,8 @@
 package base
 
 import (
-       "net/url"
        "testing"
 
-       "code.gitea.io/gitea/modules/setting"
-
        "github.com/stretchr/testify/assert"
 )
 
@@ -56,44 +53,6 @@ func TestBasicAuthEncode(t *testing.T) {
 // TODO: Test VerifyTimeLimitCode()
 // TODO: Test CreateTimeLimitCode()
 
-func TestHashEmail(t *testing.T) {
-       assert.Equal(t,
-               "d41d8cd98f00b204e9800998ecf8427e",
-               HashEmail(""),
-       )
-       assert.Equal(t,
-               "353cbad9b58e69c96154ad99f92bedc7",
-               HashEmail("gitea@example.com"),
-       )
-}
-
-const gravatarSource = "https://secure.gravatar.com/avatar/"
-
-func disableGravatar() {
-       setting.EnableFederatedAvatar = false
-       setting.LibravatarService = nil
-       setting.DisableGravatar = true
-}
-
-func enableGravatar(t *testing.T) {
-       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,
-               "https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
-               SizedAvatarLink("gitea@example.com", 100),
-       )
-}
-
 func TestFileSize(t *testing.T) {
        var size int64 = 512
        assert.Equal(t, "512 B", FileSize(size))
index e02f3d11caabbba839ed2f76c7d1c2905eb04a37..fd8b8d927a12fd130886dd4cafe3bc2180f64553 100644 (file)
@@ -123,7 +123,7 @@ func (pc *PushCommits) AvatarLink(email string) string {
                var err error
                u, err = models.GetUserByEmail(email)
                if err != nil {
-                       pc.avatars[email] = models.AvatarLink(email)
+                       pc.avatars[email] = models.HashedAvatarLink(email)
                        if !models.IsErrUserNotExist(err) {
                                log.Error("GetUserByEmail: %v", err)
                                return ""
index 8b96397529bf448adf6eb3d19b21c4c25f28c210..5af1addb60f7d8aa4d8491a3f01ee731b8deacfc 100644 (file)
@@ -88,7 +88,6 @@ func NewFuncMap() []template.FuncMap {
                "AllowedReactions": func() []string {
                        return setting.UI.Reactions
                },
-               "AvatarLink":    models.AvatarLink,
                "Safe":          Safe,
                "SafeJS":        SafeJS,
                "Str2html":      Str2html,
@@ -339,7 +338,9 @@ func NewFuncMap() []template.FuncMap {
                        }
                        return false
                },
-               "svg": SVG,
+               "svg":           SVG,
+               "avatar":        Avatar,
+               "avatarByEmail": AvatarByEmail,
                "SortArrow": func(normSort, revSort, urlSort string, isDefault bool) template.HTML {
                        // if needed
                        if len(normSort) == 0 || len(urlSort) == 0 {
@@ -499,18 +500,38 @@ func NewTextFuncMap() []texttmpl.FuncMap {
 var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
 var heightRe = regexp.MustCompile(`height="[0-9]+?"`)
 
-// SVG render icons - arguments icon name (string), size (int), class (string)
-func SVG(icon string, others ...interface{}) template.HTML {
-       size := 16
+func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
+       size := defaultSize
        if len(others) > 0 && others[0].(int) != 0 {
                size = others[0].(int)
        }
 
-       class := ""
+       class := defaultClass
        if len(others) > 1 && others[1].(string) != "" {
-               class = others[1].(string)
+               if defaultClass == "" {
+                       class = others[1].(string)
+               } else {
+                       class = defaultClass + " " + others[1].(string)
+               }
+       }
+
+       return size, class
+}
+
+func avatarHTML(src string, size int, class string, name string) template.HTML {
+       sizeStr := fmt.Sprintf(`%d`, size)
+
+       if name == "" {
+               name = "avatar"
        }
 
+       return template.HTML(`<img class="` + class + `" src="` + src + `" title="` + html.EscapeString(name) + `" width="` + sizeStr + `" height="` + sizeStr + `"/>`)
+}
+
+// SVG render icons - arguments icon name (string), size (int), class (string)
+func SVG(icon string, others ...interface{}) template.HTML {
+       size, class := parseOthers(16, "", others...)
+
        if svgStr, ok := svg.SVGs[icon]; ok {
                if size != 16 {
                        svgStr = widthRe.ReplaceAllString(svgStr, fmt.Sprintf(`width="%d"`, size))
@@ -524,6 +545,38 @@ func SVG(icon string, others ...interface{}) template.HTML {
        return template.HTML("")
 }
 
+// Avatar renders user and repo avatars. args: user/repo, size (int), class (string)
+func Avatar(item interface{}, others ...interface{}) template.HTML {
+       size, class := parseOthers(28, "ui avatar image", others...)
+       if user, ok := item.(*models.User); ok {
+               src := user.RealSizedAvatarLink(size * 2) // request double size for finer rendering
+               if src != "" {
+                       return avatarHTML(src, size, class, user.DisplayName())
+               }
+       }
+
+       if repo, ok := item.(*models.Repository); ok {
+               src := repo.RelAvatarLink()
+               if src != "" {
+                       return avatarHTML(src, size, class, repo.FullName())
+               }
+       }
+
+       return template.HTML("")
+}
+
+// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
+func AvatarByEmail(email string, name string, others ...interface{}) template.HTML {
+       size, class := parseOthers(28, "ui avatar image", others...)
+       src := models.SizedAvatarLink(email, size*2) // request double size for finer rendering
+
+       if src != "" {
+               return avatarHTML(src, size, class, name)
+       }
+
+       return template.HTML("")
+}
+
 // Safe render raw as HTML
 func Safe(raw string) template.HTML {
        return template.HTML(raw)
index 812c55ea4da9d96dfdc2ec97f490755b22a6a7f2..bd11310988eac858179c11c5caf7afda35ddf30d 100644 (file)
@@ -10,7 +10,6 @@ import (
        "fmt"
        "html"
        gotemplate "html/template"
-       "net/url"
        "strings"
 
        "code.gitea.io/gitea/models"
@@ -19,7 +18,7 @@ import (
        "code.gitea.io/gitea/modules/git"
        "code.gitea.io/gitea/modules/highlight"
        "code.gitea.io/gitea/modules/log"
-       "code.gitea.io/gitea/modules/setting"
+       "code.gitea.io/gitea/modules/templates"
        "code.gitea.io/gitea/modules/timeutil"
 )
 
@@ -209,17 +208,15 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
                        commit := commitNames[part.Sha]
                        if index == 0 {
                                // User avatar image
-                               avatar := ""
                                commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Data["Lang"].(string))
+
+                               var avatar string
                                if commit.User != nil {
-                                       authorName := commit.Author.Name
-                                       if len(commit.User.FullName) > 0 {
-                                               authorName = commit.User.FullName
-                                       }
-                                       avatar = fmt.Sprintf(`<a href="%s/%s"><img class="ui avatar image" src="%s" title="%s" alt=""/></a>`, setting.AppSubURL, url.PathEscape(commit.User.Name), commit.User.RelAvatarLink(), html.EscapeString(authorName))
+                                       avatar = string(templates.Avatar(commit.User, 18, "mr-3"))
                                } else {
-                                       avatar = fmt.Sprintf(`<img class="ui avatar image" src="%s" title="%s"/>`, html.EscapeString(models.AvatarLink(commit.Author.Email)), html.EscapeString(commit.Author.Name))
+                                       avatar = string(templates.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "mr-3"))
                                }
+
                                commitInfo.WriteString(fmt.Sprintf(`<div class="blame-info%s"><div class="blame-data"><div class="blame-avatar">%s</div><div class="blame-message"><a href="%s/commit/%s" title="%[5]s">%[5]s</a></div><div class="blame-time">%s</div></div></div>`, attr, avatar, repoLink, part.Sha, html.EscapeString(commit.CommitMessage), commitSince))
                        } else {
                                commitInfo.WriteString(fmt.Sprintf(`<div class="blame-info%s">&#8203;</div>`, attr))
index 32d05f03cc989115b379012d554fab79297a832a..c3ece49089851ec1890093156b9b33d3fee2d7f1 100644 (file)
@@ -6,11 +6,11 @@ package user
 
 import (
        "errors"
+       "net/url"
        "strconv"
        "strings"
 
        "code.gitea.io/gitea/models"
-       "code.gitea.io/gitea/modules/base"
        "code.gitea.io/gitea/modules/context"
        "code.gitea.io/gitea/modules/log"
 )
@@ -46,23 +46,38 @@ func Avatar(ctx *context.Context) {
 
 // AvatarByEmailHash redirects the browser to the appropriate Avatar link
 func AvatarByEmailHash(ctx *context.Context) {
+       var err error
+
        hash := ctx.Params(":hash")
        if len(hash) == 0 {
                ctx.ServerError("invalid avatar hash", errors.New("hash cannot be empty"))
                return
        }
-       email, err := models.GetEmailForHash(hash)
+
+       var email string
+       email, err = models.GetEmailForHash(hash)
        if err != nil {
                ctx.ServerError("invalid avatar hash", err)
                return
        }
        if len(email) == 0 {
-               ctx.Redirect(base.DefaultAvatarLink())
+               ctx.Redirect(models.DefaultAvatarLink())
                return
        }
        size := ctx.QueryInt("size")
        if size == 0 {
-               size = base.DefaultAvatarSize
+               size = models.DefaultAvatarSize
        }
-       ctx.Redirect(base.SizedAvatarLinkWithDomain(email, size))
+
+       var avatarURL *url.URL
+       avatarURL, err = models.LibravatarURL(email)
+       if err != nil {
+               avatarURL, err = url.Parse(models.DefaultAvatarLink())
+               if err != nil {
+                       ctx.ServerError("invalid default avatar url", err)
+                       return
+               }
+       }
+
+       ctx.Redirect(models.MakeFinalAvatarURL(avatarURL, size))
 }
index 1b102b11d883993cec58c170f47328c9f18202c8..979e4d54887207c56a531a2da6d518f74b2221a5 100644 (file)
@@ -47,7 +47,7 @@
                <div class="right stackable menu">
                        <div class="ui dropdown jump item poping up" tabindex="-1" data-content="{{.i18n.Tr "user_profile_and_more"}}" data-variation="tiny inverted">
                                <span class="text">
-                                       <img class="ui tiny avatar image" width="24" height="24" src="{{.SignedUser.RelAvatarLink}}">
+                                       {{avatar .SignedUser 24 "tiny"}}
                                        <span class="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span>
                                        <span class="mobile-only">{{.SignedUser.Name}}</span>
                                        <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>
 
                        <div class="ui dropdown jump item poping up" tabindex="-1" data-content="{{.i18n.Tr "user_profile_and_more"}}" data-variation="tiny inverted">
                                <span class="text">
-                                       <img class="ui tiny avatar image" width="24" height="24" src="{{.SignedUser.RelAvatarLink}}">
+                                       {{avatar .SignedUser 24 "tiny"}}
                                        <span class="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span>
                                        <span class="mobile-only">{{.SignedUser.Name}}</span>
                                        <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>
index a2d62f3db28bf2334b57496b6e7c2618d81e814d..8785ac84db2c7d7f4f288147c6574c1f4fb00a85 100644 (file)
@@ -7,7 +7,7 @@
                <div class="ui user list">
                        {{range .Users}}
                                <div class="item">
-                                 <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                                       {{avatar .}}
                                  <div class="content">
                                        <span class="header">
                                                <a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}
index 6899971a06ecb08b64d7c975e3b11e0d91c16346..fe4b278e2ca4b0f50edd6e079a0fd2d438862b1c 100644 (file)
@@ -2,9 +2,7 @@
        {{range .Repos}}
                <div class="item">
                        <div class="ui header">
-                               {{if .RelAvatarLink}}
-                                       <img class="ui avatar image" src="{{.RelAvatarLink}}">
-                               {{end}}
+                               {{avatar .}}
                                <a class="name" href="{{.Link}}">
                                        {{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}
                                </a>
index 64fa93d6e7f48df547c777a4236bbe0c9a96eee7..cdb8e9d37592a116ad20ec4a4125bfc1ad7bbce6 100644 (file)
@@ -7,7 +7,7 @@
                <div class="ui user list">
                        {{range .Users}}
                                <div class="item">
-                                 <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                                       {{avatar .}}
                                  <div class="content">
                                        <span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span>
                                        <div class="description">
index e232726822adae5be000c8e0ef0bc69fb4506e37..d06c54d9ca48b7bb451123764d5c5538cf14711c 100644 (file)
@@ -3,7 +3,7 @@
                <div class="ui vertically grid head">
                        <div class="column">
                                <div class="ui header">
-                                       <img class="ui image" src="{{.SizedRelAvatarLink 100}}">
+                                       {{avatar . 100}}
                                        <span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
                                        <span class="org-visibility">
                                                {{if .Visibility.IsLimited}}<div class="ui medium orange horizontal label">{{$.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
index 54474965bd5e9ad2e342da15a63a2f2bb1ba630e..a0f37a8009be03aabb2716fb2f6510120719cdf8 100644 (file)
@@ -1,9 +1,7 @@
 {{template "base/head" .}}
 <div class="page-content organization profile">
-       {{/* overflow: auto is the clearfix - this avoids the image going beyond
-       the container where it is supposed to stay inside. */}}
-       <div class="ui container" style="overflow: auto">
-               <img class="ui left" id="org-avatar" src="{{.Org.SizedRelAvatarLink 140}}"/>
+       <div class="ui container df">
+               {{avatar .Org 140 "org-avatar"}}
                <div id="org-info">
                        <div class="ui header">
                                {{.Org.DisplayName}}
@@ -53,7 +51,9 @@
                                        {{$isMember := .IsOrganizationMember}}
                                        {{range .Members}}
                                                {{if or $isMember (.IsPublicMember $.Org.ID)}}
-                                                       <a href="{{.HomeLink}}" title="{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}"><img class="ui avatar" src="{{.RelAvatarLink}}"></a>
+                                                       <a href="{{.HomeLink}}" title="{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}">
+                                                               {{avatar .}}
+                                                       </a>
                                                {{end}}
                                        {{end}}
                                </div>
index 1a168da1331f77e9b9048d1d3ef02ea1e976bdd7..74d84b2e876fa7eeff93e029d79be5a6f0e621cd 100644 (file)
@@ -8,7 +8,7 @@
                        {{ range .Members}}
                                <div class="item ui grid">
                                        <div class="ui one wide column">
-                                               <img class="ui avatar" src="{{.SizedRelAvatarLink 48}}">
+                                               {{avatar . 48}}
                                        </div>
                                        <div class="ui three wide column">
                                                <div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div>
index 51abf4ec4f12bfaa992966eefbe8d03f40f9fec9..bfc151d00046ad0f12a17f94fe441cded4b66f31 100644 (file)
@@ -33,7 +33,7 @@
                                                                </form>
                                                        {{end}}
                                                        <a href="{{.HomeLink}}">
-                                                               <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                                                               {{avatar .}}
                                                                {{.DisplayName}}
                                                        </a>
                                                </div>
index 0fae4eb6cf7d2f7b378229a42794702aa3781790..4e877bef2a1a4e27765ab0d5bbab04b688675d33 100644 (file)
@@ -32,7 +32,7 @@
                                        <div class="ui attached segment members">
                                                {{range .Members}}
                                                        <a href="{{.HomeLink}}" title="{{.Name}}">
-                                                               <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                                                               {{avatar .}}
                                                        </a>
                                                {{end}}
                                        </div>
index 4bc96ae4597b06600ed32fb27a31c4bcaeb0c277..0448c938bd51acde30a30c95c9a6f7b66ba0b8bb 100644 (file)
                        <div class="ui stackable grid">
                                <div class="nine wide column">
                                        {{if .Author}}
-                                               <img class="ui avatar image" src="{{.Author.RelAvatarLink}}" />
-                                       {{if .Author.FullName}}
-                                       <a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a>
-                                       {{else}}
-                                       <a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a>
-                                       {{end}}
+                                               {{avatar .Author}}
+                                               {{if .Author.FullName}}
+                                                       <a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a>
+                                               {{else}}
+                                                       <a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a>
+                                               {{end}}
                                        {{else}}
-                                               <img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" />
+                                               {{avatarByEmail .Commit.Author.Email .Commit.Author.Email 12}}
                                                <strong>{{.Commit.Author.Name}}</strong>
                                        {{end}}
                                        <span class="text grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span>
                                                <div class="committed-by">
                                                        <span class="text grey">{{svg "octicon-git-commit"}}{{.i18n.Tr "repo.diff.committed_by"}}</span>
                                                        {{if ne .Verification.CommittingUser.ID 0}}
-                                                               <img class="ui avatar image" src="{{.Verification.CommittingUser.RelAvatarLink}}" />
+                                                               {{avatar .Verification.CommittingUser}}
                                                                <a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a>
                                                        {{else}}
-                                                               <img class="ui avatar image" src="{{AvatarLink .Commit.Committer.Email}}" />
+                                                               {{avatarByEmail .Commit.Committer.Email .Commit.Committer.Name}}
                                                                <strong>{{.Commit.Committer.Name}}</strong>
                                                        {{end}}
                                                </div>
                                                {{else}}
                                                        <span class="ui text">{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span>
                                                {{end}}
-                                               <img class="ui avatar image" src="{{.Verification.SigningUser.RelAvatarLink}}" />
+                                               {{avatar .Verification.SigningUser}}
                                                <a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong></a>
                                                <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
                                        {{else}}
                                                <span title="{{.i18n.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span>
                                                <span class="ui text">{{.i18n.Tr "repo.commits.signed_by"}}:</span>
-                                               <img class="ui avatar image" src="{{AvatarLink .Verification.SigningEmail}}" />
+                                               {{avatarByEmail .Verification.SigningEmail ""}}
                                                <strong>{{.Verification.SigningUser.Name}}</strong>
                                                <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="cogs icon" title="{{.i18n.Tr "gpg.default_key"}}"></i>{{.Verification.SigningKey.KeyID}}</span>
                                        {{end}}
index e5c4c7488d91ff2d41a60e4eca82a449b864770d..e7489bf51d244319b42a859b06c3120d77ce1110 100644 (file)
                                                                {{if .User.FullName}}
                                                                        {{$userName = .User.FullName}}
                                                                {{end}}
-                                                               <img class="ui avatar image" src="{{.User.RelAvatarLink}}" alt=""/>&nbsp;&nbsp;<a href="{{AppSubUrl}}/{{.User.Name}}">{{$userName}}</a>
+                                                               {{avatar .User 28 "mr-2"}}<a href="{{AppSubUrl}}/{{.User.Name}}">{{$userName}}</a>
                                                        {{else}}
-                                                               <img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/>&nbsp;&nbsp;{{$userName}}
+                                                               {{avatarByEmail .Author.Email .Author.Name 28 "mr-2"}}
+                                                               {{$userName}}
                                                        {{end}}
                                                </td>
                                                <td class="sha">
index cb8c6e05fd027c201f0515c7f7547aae224e56b6..aaf4174bbda0f4d0d1bf2fd4ca623165e120c1d2 100644 (file)
@@ -7,9 +7,11 @@
        <div class="singular-commit" id="{{$tag}}">
                <span class="badge badge-commit">{{svg "octicon-git-commit"}}</span>
                {{if .User}}
-                       <a class="ui avatar image" href="{{AppSubUrl}}/{{.User.Name}}"><img src="{{.User.RelAvatarLink}}" alt=""/></a>
+                       <a href="{{AppSubUrl}}/{{.User.Name}}">
+                               {{avatar .User}}
+                       </a>
                {{else}}
-                       <img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/>
+                       {{avatarByEmail .Author.Email .Author.Name}}
                {{end}}
 
                <span class="ui float right shabox">
index 4593e77526594f69b5e879a28fde0380cba55120..82c1b038b431f6f149e581889f848ce99700f30f 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser 28 "mini"}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu">
                                                                <div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
-                                                                       <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
+                                                                       {{avatar .SignedUser 28 "mini"}}
                                                                        {{.SignedUser.ShortName 20}}
                                                                </div>
                                                                {{range .Orgs}}
                                                                        <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar . 28 "mini"}}
                                                                                {{.ShortName 20}}
                                                                        </div>
                                                                {{end}}
index fa553fba96ea3374a2261aec49051cd89045d4bf..59bc89b79e8ef623b4e26fc933c60be881bb1cf1 100644 (file)
@@ -6,7 +6,7 @@
                <span class="avatar"><img src="/img/avatar_default.png"></span>
        {{else}}
                <a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
-                       <img src="{{.Poster.RelAvatarLink}}">
+                       {{avatar .Poster}}
                </a>
        {{end}}
        <div class="content comment-container">
index ff94d018215e2985b5d9dc7973196f0e022680cb..494a2113eebc0bcd138c42fdbb4eaebdb1e6da0c 100644 (file)
@@ -1,5 +1,5 @@
 <div class="commit-form-wrapper">
-       <img width="48" height="48" class="ui image commit-avatar" src="{{.SignedUser.RelAvatarLink}}">
+       {{avatar .SignedUser 48 "commit-avatar"}}
        <div class="commit-form">
                <h3>{{- if .CanCommitToBranch.WillSign}}
                        <span title="{{.i18n.Tr "repo.signing.will_sign" .CanCommitToBranch.SigningKey}}">{{svg "octicon-lock" 24}}</span>
index 28547f1aff1a194b1274ee4014283791afed8d5f..192291275f34ac8b4471161037ac810b00bb74ad 100644 (file)
@@ -8,7 +8,7 @@
                <div class="ui list">
                        {{range .Forks}}
                                <div class="item">
-                                       <img class="ui avatar image" src="{{.Owner.RelAvatarLink}}">
+                                       {{avatar .Owner}}
                                        <div class="link">
                                                <a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
                                                /
index d89bcf1cc7f4b79567f00b7b3852e892813abaf1..9f7a6c1c66978592990a58971553d6fcd358d025 100644 (file)
                                                        {{if $commit.User.FullName}}
                                                                {{$userName = $commit.User.FullName}}
                                                        {{end}}
-                                                       <img class="ui avatar image" src="{{$commit.User.RelAvatarLink}}" alt=""/><a href="{{AppSubUrl}}/{{$commit.User.Name}}">{{$userName}}</a>
+                                                       {{avatar $commit.User}}
+                                                       <a href="{{AppSubUrl}}/{{$commit.User}}">{{$userName}}</a>
                                                {{else}}
-                                                       <img class="ui avatar image" src="{{AvatarLink $commit.Commit.Author.Email}}" alt=""/>{{$userName}}
+                                                       {{avatarByEmail $commit.Commit.Author.Email $userName}}
+                                                       {{$userName}}
                                                {{end}}
                                        </span>
                                        <span class="time df ac">{{$commit.Date}}</span>
index 51f49b59946dd212e8c8d897d1bcc7f5ad381365..348c6b1ffa10c934e67fdde9cdfd236b21dc9de6 100644 (file)
@@ -3,8 +3,8 @@
        <div class="ui container">
                <div class="repo-header">
                        <div class="ui huge breadcrumb repo-title">
-                               {{if .RelAvatarLink}}
-                                       <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                               {{if .Name}}
+                                       {{avatar .}}
                                {{else}}
                                        {{template "repo/header_icon" .}}
                                {{end}}
index 7883ee9bc657b697d2a2905c477d6280b841668e..b9d49c209d13fc504e6e62f022efbff5713f26de 100644 (file)
@@ -7,7 +7,7 @@
                {{else if and (not $.IsMirror) (not $.IsFork) ($.Owner)}}
                        {{svg "octicon-repo" 32}}
                        {{if $.Owner.Visibility.IsPrivate}}
-                               <img class="ui avatar image" src="{{$.Owner.RelAvatarLink}}">
+                               {{avatar $.Owner}}
                        {{end}}
                {{else if $.IsMirror}}
                        {{svg "octicon-mirror" 32}}
index 6125c01b74da8a7bad8475ac3287343431556edc..2b64d267006288af39bb076a1fc00ecabbbd5c6e 100644 (file)
@@ -70,7 +70,9 @@
                                                <div class="menu">
                                                        <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}">{{.i18n.Tr "repo.issues.filter_assginee_no_select"}}</a>
                                                        {{range .Assignees}}
-                                                               <a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{.ID}}"><img src="{{.RelAvatarLink}}"> {{.GetDisplayName}}</a>
+                                                               <a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&assignee={{.ID}}">
+                                                                       {{avatar .}}
+                                                               </a>
                                                        {{end}}
                                                </div>
                                        </div>
                                                        </div>
                                                        {{range .Assignees}}
                                                                <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
-                                                                       <img src="{{.RelAvatarLink}}"> {{.GetDisplayName}}
+                                                                       {{avatar .}}
                                                                </div>
                                                        {{end}}
                                                </div>
index 9f0a2f99a7923df7a9a59d6c00df25f827d335ab..638134c442620ee01fe9840d623094aac58b5345 100644 (file)
                                                <div class="menu">
                                                        <a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}">{{.i18n.Tr "repo.issues.filter_assginee_no_select"}}</a>
                                                        {{range .Assignees}}
-                                                               <a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&assignee={{.ID}}"><img src="{{.RelAvatarLink}}"> {{.GetDisplayName}}</a>
+                                                               <a class="{{if eq $.AssigneeID .ID}}active selected{{end}} item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{$.SelectLabels}}&assignee={{.ID}}">
+                                                                       {{avatar . 28 "mr-2"}}
+                                                                       {{.GetDisplayName}}
+                                                               </a>
                                                        {{end}}
                                                </div>
                                        </div>
                                                        </div>
                                                        {{range .Assignees}}
                                                                <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
-                                                                       <img src="{{.RelAvatarLink}}"> {{.GetDisplayName}}
+                                                                       {{avatar . 28 "mr-2"}}
+                                                                       {{.GetDisplayName}}
                                                                </div>
                                                        {{end}}
                                                </div>
index 4814ba590b3ef6b552dd22282239c521c4d3fab7..aa142e3ded9f584f0dadc5788a9303f173601e86 100644 (file)
@@ -9,7 +9,7 @@
                <div class="ui comments">
                        <div class="comment">
                                <a class="avatar" href="{{.SignedUser.HomeLink}}">
-                                       <img src="{{.SignedUser.RelAvatarLink}}">
+                                       {{avatar .SignedUser}}
                                </a>
                                <div class="ui segment content">
                                        <div class="field">
                                                        <a class="item muted" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}">
                                                                <span class="octicon-check invisible">{{svg "octicon-check"}}</span>
                                                                <span class="text">
-                                                                       <img class="ui avatar image" src="{{.RelAvatarLink}}"> {{.GetDisplayName}}
+                                                                       {{avatar . 28 "mr-3"}}{{.GetDisplayName}}
                                                                </span>
                                                        </a>
                                                {{end}}
                                        </span>
                                        {{range .Assignees}}
                                                <a class="hide item p-2 muted" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}">
-                                                       <img class="ui avatar image" src="{{.RelAvatarLink}}" style="vertical-align: middle;">&nbsp;{{.GetDisplayName}}
+                                                       {{avatar . 28 "mr-3 vm"}}{{.GetDisplayName}}
                                                </a>
                                        {{end}}
                                </div>
index 81e2c80c3c48c566e4922d662774a81e76d6ef47..355ba0ea298e4a08c3aa00ff2e0c03e5f3c0ba9b 100644 (file)
@@ -16,7 +16,7 @@
                                <span class="timeline-avatar"><img src="/img/avatar_default.png"></span>
                        {{else}}
                                <a class="timeline-avatar" {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>
-                                       <img src="{{.Issue.Poster.RelAvatarLink}}">
+                                       {{avatar .Issue.Poster}}
                                </a>
                        {{end}}
                                <div class="content comment-container">
@@ -93,7 +93,7 @@
                                {{ if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived) }}
                                <div class="timeline-item comment form">
                                        <a class="timeline-avatar" href="{{.SignedUser.HomeLink}}">
-                                               <img src="{{.SignedUser.RelAvatarLink}}">
+                                               {{avatar .SignedUser}}
                                        </a>
                                        <div class="content">
                                                <form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
                                        {{if .Repository.IsArchived}}
                                        <div class="timeline-item comment form">
                                                <a class="timeline-avatar" href="{{.SignedUser.HomeLink}}">
-                                                       <img src="{{.SignedUser.RelAvatarLink}}">
+                                                       {{avatar .SignedUser}}
                                                </a>
                                                <div class="content">
                                                        <form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
index 2bd2d86430da5b156e77a30ae3e15ce1f4e67a9c..698e4698d069ba13df08d9c8c150faf8703df859 100644 (file)
@@ -15,7 +15,7 @@
                        <span class="timeline-avatar"><img src="/img/avatar_default.png"></span>
                {{else}}
                        <a class="timeline-avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
-                               <img src="{{.Poster.RelAvatarLink}}">
+                               {{avatar .Poster}}
                        </a>
                {{end}}
                        <div class="content comment-container">
@@ -92,8 +92,8 @@
        {{else if eq .Type 1}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge bg-green text-white">{{svg "octicon-dot-fill"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 2}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge bg-red text-white">{{svg "octicon-circle-slash"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 28}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge bg-purple text-white">{{svg "octicon-git-merge"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
                {{ $createdStr:= TimeSinceUnix .CreatedUnix $.Lang }}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-bookmark"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        {{if eq .RefAction 3}}<del>{{end}}
                        <span class="text grey">
        {{else if eq .Type 4}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-bookmark"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
                {{if or .AddedLabels .RemovedLabels}}
                        <div class="timeline-item event" id="{{.HashTag}}">
                                <span class="badge">{{svg "octicon-tag"}}</span>
-                               <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                                       <img src="{{.Poster.RelAvatarLink}}">
+                               <a href="{{.Poster.HomeLink}}">
+                                       {{avatar .Poster}}
                                </a>
                                <span class="text grey">
                                        <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 8}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-milestone"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
                        <span class="badge">{{svg "octicon-person"}}</span>
                        {{if gt .AssigneeID 0}}
                                {{if .RemovedAssignee}}
-                                       <a class="ui avatar image" href="{{.Assignee.HomeLink}}">
-                                               <img src="{{.Assignee.RelAvatarLink}}">
+                                       <a href="{{.Assignee.HomeLink}}">
+                                               {{avatar .Assignee}}
                                        </a>
                                        <span class="text grey">
                                                <a class="author" href="{{.Assignee.HomeLink}}">{{.Assignee.GetDisplayName}}</a>
                                                {{ end }}
                                        </span>
                                {{else}}
-                                       <a class="ui avatar image" href="{{.Assignee.HomeLink}}">
-                                               <img src="{{.Assignee.RelAvatarLink}}">
+                                       <a href="{{.Assignee.HomeLink}}">
+                                               {{avatar .Assignee}}
                                        </a>
                                        <span class="text grey">
                                                <a class="author" href="{{.Assignee.HomeLink}}">{{.Assignee.GetDisplayName}}</a>
        {{else if eq .Type 10}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-pencil"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 11}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-git-branch"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 12}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 13}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 14}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 15}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 16}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 17}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 18}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 19}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-package-dependents"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 20}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-package-dependents"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
                                {{if .OriginalAuthor }}
                                {{else}}
                                <a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
-                                       <img src="{{.Poster.RelAvatarLink}}">
+                                       {{avatar .Poster}}
                                </a>
                                {{end}}
                                <span class="badge {{if eq .Review.Type 1}}bg-green
                                                                                        <div class="comment code-comment" id="{{.HashTag}}">
                                                                                                {{if not .OriginalAuthor }}
                                                                                                        <a class="avatar">
-                                                                                                               <img src="{{.Poster.RelAvatarLink}}">
+                                                                                                               {{avatar .Poster}}
                                                                                                        </a>
                                                                                                {{end}}
                                                                                                <div class="content">
        {{else if eq .Type 23}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-lock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        {{ if .Content }}
                                <span class="text grey">
        {{else if eq .Type 24}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-key"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 25}}
                <div class="timeline-item event">
                        <span class="badge">{{svg "octicon-git-branch"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
        {{else if eq .Type 26}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-clock"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
        {{else if eq .Type 27}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-eye"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
                {{if not $.UnitProjectsGlobalDisabled}}
                <div class="timeline-item event" id="{{.HashTag}}">
                        <span class="badge">{{svg "octicon-project"}}</span>
-                       <a class="ui avatar image" href="{{.Poster.HomeLink}}">
-                               <img src="{{.Poster.RelAvatarLink}}">
+                       <a href="{{.Poster.HomeLink}}">
+                               {{avatar .Poster}}
                        </a>
                        <span class="text grey">
                                <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
index 9d8ea7ba8aced898454cf8c1b91c574837832e39..c8fb50d86d7919b952e9f15a9c55d867b18ae8af 100644 (file)
@@ -9,8 +9,8 @@
                                        <div class="review-item">
                                                <div class="review-item-left">
                                                        {{if .User}}
-                                                               <a class="ui avatar image" href="{{.User.HomeLink}}">
-                                                                       <img src="{{.User.RelAvatarLink}}">
+                                                               <a href="{{.User.HomeLink}}">
+                                                                       {{avatar .User}}
                                                                </a>
                                                        {{end}}
                                                        <span class="text grey">
index de1fd582325c3f9631ebab5aacbbef9293c060cf..d778beee4e87ffd73f22fe29d99039779f656617 100644 (file)
@@ -26,7 +26,7 @@
                                                        <a class="{{if not .CanChange}}ui poping up{{end}} item {{if .Checked}} checked {{end}} {{if not .CanChange}}ban-change{{end}}" href="#" data-id="{{.ItemID}}" data-id-selector="#review_request_{{.ItemID}}" {{if not .CanChange}} data-content="{{$.i18n.Tr "repo.issues.remove_request_review_block"}}"{{end}}>
                                                                <span class="octicon-check {{if not .Checked}}invisible{{end}}">{{svg "octicon-check"}}</span>
                                                                <span class="text">
-                                                                       <img class="ui avatar image mr-2" loading="lazy" src="{{.User.RelAvatarLink}}">
+                                                                       {{avatar .User 28 "mr-3"}}
                                                                        {{.User.GetDisplayName}}
                                                                </span>
                                                        </a>
@@ -56,7 +56,7 @@
                                        <div class="item mb-2">
                                                {{if .User}}
                                                        <a class="muted sidebar-item-link" href="{{.User.HomeLink}}">
-                                                               <img class="ui avatar image mr-3" src="{{.User.RelAvatarLink}}">
+                                                               {{avatar .User 28 "mr-3"}}
                                                                {{.User.GetDisplayName}}
                                                        </a>
                                                {{else if .Team}}
                                                {{end}}
                                                <span class="octicon-check {{if not $checked}}invisible{{end}}">{{svg "octicon-check"}}</span>
                                                <span class="text">
-                                                       <img class="ui avatar image mr-2" loading="lazy" src="{{.RelAvatarLink}}">
+                                                       {{avatar . 28 "mr-3"}}
                                                        {{.GetDisplayName}}
                                                </span>
                                        </a>
                                {{range .Issue.Assignees}}
                                        <div class="item">
                                                <a class="muted sidebar-item-link" href="{{$.RepoLink}}/{{if $.Issue.IsPull}}pulls{{else}}issues{{end}}?assignee={{.ID}}">
-                                                       <img class="ui avatar image mr-3" src="{{.RelAvatarLink}}">
+                                                       {{avatar . 28 "mr-3"}}
                                                        {{.GetDisplayName}}
                                                </a>
                                        </div>
                        <div>
                                {{range .Participants}}
                                        <a {{if gt .ID 0}}href="{{.HomeLink}}"{{end}}>
-                                               <img class="ui avatar image poping up" src="{{.RelAvatarLink}}" data-content="{{.GetDisplayName}}" data-position="top center" data-variation="small inverted">
+                                               <div class="ui poping up" data-content="{{.GetDisplayName}}" data-position="top center" data-variation="small inverted">
+                                                       {{avatar .}}
+                                               </div>
                                        </a>
                                {{end}}
                        </div>
                                                {{range $user, $trackedtime := .WorkingUsers}}
                                                        <div class="comment">
                                                                <a class="avatar">
-                                                                       <img src="{{$user.RelAvatarLink}}">
+                                                                       {{avatar $user}}
                                                                </a>
                                                                <div class="content">
                                                                        <a class="author">{{$user.DisplayName}}</a>
index d767de63f95d42defc93d95d5a99ad5f0dbd53e0..7fb8f3d585d976c3c327579154edadd02f975e9b 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu" title="{{.SignedUser.Name}}">
                                                                <div class="item" data-value="{{.SignedUser.ID}}">
-                                                                       <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
+                                                                       {{avatar .SignedUser}}
                                                                        {{.SignedUser.ShortName 20}}
                                                                </div>
                                                                {{range .Orgs}}
                                                                        <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar .}}
                                                                                {{.ShortName 20}}
                                                                        </div>
                                                                {{end}}
index feab91a9ec182e361227139ba6e2dfd19f0be78d..4ad6e6024fae59c85a441d2e59414edb86c2bb41 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu" title="{{.SignedUser.Name}}">
                                                                <div class="item" data-value="{{.SignedUser.ID}}">
-                                                                       <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
+                                                                       {{avatar .SignedUser}}
                                                                        {{.SignedUser.ShortName 20}}
                                                                </div>
                                                                {{range .Orgs}}
                                                                <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                       <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                       {{avatar .}}
                                                                        {{.ShortName 20}}
                                                                </div>
                                                                {{end}}
index 54d7dc97e7a98b13e3d41ad2633480acd8e01d1c..c31444aaef2611caf4f974a04114ac3d9b05dc88 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser 28 "mini"}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu" title="{{.SignedUser.Name}}">
                                                                <div class="item" data-value="{{.SignedUser.ID}}">
-                                                                       <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
+                                                                       {{avatar .SignedUser 28 "mini"}}
                                                                        {{.SignedUser.ShortName 20}}
                                                                </div>
                                                                {{range .Orgs}}
                                                                        <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar . 28 "mini"}}
                                                                                {{.ShortName 20}}
                                                                        </div>
                                                                {{end}}
index 4f6ac81186b863a786ba88ad98ebcacc96a8f54f..823bf25de2f2199661c21fd56938fbf2c562a77c 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser 28 "mini"}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu" title="{{.SignedUser.Name}}">
                                                                <div class="item" data-value="{{.SignedUser.ID}}">
-                                                                       <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
+                                                                       {{avatar .SignedUser 28 "mini"}}
                                                                        {{.SignedUser.ShortName 20}}
                                                                </div>
                                                                {{range .Orgs}}
                                                                        <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar . 28 "mini"}}
                                                                                {{.ShortName 20}}
                                                                        </div>
                                                                {{end}}
index d90a0d38ae0dc3feda0a49faac00589eeae7bae8..dcc43d02023c0f4d1bd8a8a6b543b33b56708e25 100644 (file)
@@ -78,7 +78,6 @@
                                        {{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}}
                                                <div class="ui dropdown jump item poping up right" data-variation="tiny inverted">
                                                        <span class="ui text">
-                                                               <img class="ui tiny avatar image" width="24" height="24">
                                                                <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-kebab-horizontal" 24}}</span>
                                                        </span>
                                                        <div class="menu user-menu" tabindex="-1">
index 360fe7372a1d59c581d27e555ee151ace9134e09..42d9791163eb2b347a083dc2aff9c2fc187682ef 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser 28 "mini"}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu">
                                                                {{if .CanForkToUser}}
                                                                        <div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
-                                                                               <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
+                                                                               {{avatar .SignedUser 28 "mini"}}
                                                                                {{.SignedUser.ShortName 20}}
                                                                        </div>
                                                                {{end}}
                                                                {{range .Orgs}}
                                                                        <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar . 28 "mini"}}
                                                                                {{.ShortName 20}}
                                                                        </div>
                                                                {{end}}
index cab054224ccc0de6126664605797501c865e2946..70a614ff72a27ad1a0f2657d25a31832a7a23ee7 100644 (file)
@@ -90,7 +90,7 @@
                                                        <p class="text grey">
                                                                {{ if gt .Publisher.ID 0 }}
                                                                <span class="author">
-                                                                       <img class="img-10" src="{{.Publisher.RelAvatarLink}}">
+                                                                       {{avatar .Publisher 28 "img-10"}}
                                                                        <a href="{{AppSubUrl}}/{{.Publisher.Name}}">{{.Publisher.Name}}</a>
                                                                </span>
                                                                {{ end }}
                                                                {{if .OriginalAuthor}}
                                                                        {{svg "octicon-mark-github" 16 "mr-2"}}{{.OriginalAuthor}}
                                                                {{else if .Publisher}}
-                                                                       <img class="img-10" src="{{.Publisher.RelAvatarLink}}">
+                                                                       {{avatar .Publisher 28 "img-10"}}
                                                                        <a href="{{AppSubUrl}}/{{.Publisher.Name}}">{{.Publisher.GetDisplayName}}</a>
                                                                {{else}}
                                                                        Ghost
index b6e659d592a47bce449a0472d50f701b2a8e9e93..cb0808714f7f68c09c6a32e4c7219221efef42ad 100644 (file)
@@ -13,7 +13,7 @@
                                <div class="item ui grid">
                                        <div class="ui five wide column">
                                                <a href="{{AppSubUrl}}/{{.Name}}">
-                                                       <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                                                       {{avatar .}}
                                                        {{.DisplayName}}
                                                </a>
                                        </div>
index 674c2b64508ae6e5d9b994b5dc7c0384e6b4d0e9..84269dc612bef85f557016aaaecd0b9d896fb5ec 100644 (file)
@@ -35,7 +35,7 @@
                                                        </td>
                                                        <td>
                                                                <a href="{{AppSubUrl}}/{{$lock.Owner.Name}}">
-                                                                       <img class="ui avatar image" src="{{$lock.Owner.RelAvatarLink}}">
+                                                                       {{avatar $lock.Owner}}
                                                                        {{$lock.Owner.DisplayName}}
                                                                </a>
                                                        </td>
index 35d6d4ab11c99ed8158425d9285a993c44cec973..f6ecd67fd21598b0b2d8675424cb57de17923a30 100644 (file)
@@ -48,7 +48,7 @@
                                                                <div class="menu">
                                                                        {{range .Users}}
                                                                                <div class="item" data-value="{{.ID}}">
-                                                                                       <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                                       {{avatar . 28 "mini"}}
                                                                                        {{.GetDisplayName}}
                                                                                </div>
                                                                        {{end}}
@@ -98,7 +98,7 @@
                                                                <div class="menu">
                                                                {{range .Users}}
                                                                        <div class="item" data-value="{{.ID}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar . 28 "mini"}}
                                                                        {{.GetDisplayName}}
                                                                        </div>
                                                                {{end}}
                                                                <div class="menu">
                                                                {{range .Users}}
                                                                        <div class="item" data-value="{{.ID}}">
-                                                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                                                               {{avatar . 28 "mini"}}
                                                                        {{.GetDisplayName}}
                                                                        </div>
                                                                {{end}}
index c51c0ef548a950bf405af332125667d57100ba9e..4fbb15e933fcc9294361dcfa81508711f7561c7b 100644 (file)
@@ -3,10 +3,10 @@
                <div title="{{if eq .verification.TrustStatus "trusted"}}{{else if eq .verification.TrustStatus "untrusted"}}{{$.root.i18n.Tr "repo.commits.signed_by_untrusted_user"}}: {{else}}{{$.root.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}: {{end}}{{.verification.Reason}}">
                {{if ne .verification.SigningUser.ID 0}}
                        {{svg "gitea-lock"}}
-                       <img class="ui signature avatar image" src="{{.verification.SigningUser.RelAvatarLink}}" />
+                       {{avatar .verification.SigningUser "signature"}}
                {{else}}
                        <span title="{{$.root.i18n.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span>
-                       <img class="ui signature avatar image" src="{{AvatarLink .verification.SigningEmail}}" />
+                       {{avatarByEmail .verification.SigningEmail "" 28 "signature"}}
                {{end}}
                </div>
        {{else}}
index d50da4de7139aca6840a0e0a2d8aa5867a64b7ce..3526c698b9e0f5427e81877be38b8bb775bda1ea 100644 (file)
@@ -8,7 +8,7 @@
                {{range .Cards}}
                        <li class="item ui segment">
                                <a href="{{.HomeLink}}">
-                                       <img class="avatar" src="{{.RelAvatarLink}}"/>
+                                       {{avatar .}}
                                </a>
                                <h3 class="name"><a href="{{.HomeLink}}">{{.DisplayName}}</a></h3>
 
index b4f2260fe2bdd348340af9dd3daeb653ce46b7b8..651747f20aacd4de4d093ef447b975df80147a56 100644 (file)
@@ -3,7 +3,7 @@
                <tr class="commit-list">
                        <th colspan="2">
                                {{if .LatestCommitUser}}
-                                       <img class="ui avatar image img-12" src="{{.LatestCommitUser.RelAvatarLink}}" />
+                                       {{avatar .LatestCommitUser 28 "img-12"}}
                                        {{if .LatestCommitUser.FullName}}
                                                <a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
                                        {{else}}
@@ -11,7 +11,7 @@
                                        {{end}}
                                {{else}}
                                        {{if .LatestCommit.Author}}
-                                               <img class="ui avatar image img-12" src="{{AvatarLink .LatestCommit.Author.Email}}" />
+                                               {{avatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 28 "img-12"}}
                                                <strong>{{.LatestCommit.Author.Name}}</strong>
                                        {{end}}
                                {{end}}
index ddba083b59679a27b8189118da27248eed0e8c9c..62f55f6c40b5b6009e3674f8e8093680304e4f92 100644 (file)
                                <div class="issue-item-icon-right text grey">
                                        {{range .Assignees}}
                                                <a class="ui assignee poping up" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-variation="inverted" data-position="left center">
-                                                       <img class="ui avatar image" src="{{.RelAvatarLink}}">
+                                                       {{avatar .}}
                                                </a>
                                        {{end}}
                                </div>
index 739caeba5f2e0ee021c0a0a08eaed6c28762cf19..76c751de8ab0ea7498dcd947232c34806685793d 100644 (file)
@@ -1,7 +1,7 @@
 {{range .Feeds}}
        <div class="news">
                <div class="ui left">
-                       <img class="ui avatar image" src="{{.GetActAvatar}}" alt="">
+                       {{avatar .ActUser}}
                </div>
                <div class="ui grid">
                        <div class="ui fourteen wide column">
                                                                {{if $push.Commits}}
                                                                        {{range $push.Commits}}
                                                                                {{ $commitLink := printf "%s/commit/%s" $repoLink .Sha1}}
-                                                                               <li><img class="img-8" src="{{$push.AvatarLink .AuthorEmail}}"> <a class="commit-id" href="{{$commitLink}}">{{ShortSha .Sha1}}</a> <span class="text truncate light grey">{{RenderCommitMessage .Message $repoLink $.ComposeMetas}}</span></li>
+                                                                               <li>
+                                                                                       {{avatarByEmail .AuthorEmail .AuthorName 28 "img-8 mr-2"}}
+                                                                                       <a class="commit-id mr-2" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
+                                                                                       <span class="text truncate light grey">
+                                                                                               {{RenderCommitMessage .Message $repoLink $.ComposeMetas}}
+                                                                                       </span>
+                                                                               </li>
                                                                        {{end}}
                                                                {{end}}
                                                                {{if and (gt $push.Len 1) $push.CompareURL}}<li><a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{$.i18n.Tr "action.compare_commits" $push.Len}} ยป</a></li>{{end}}
index 0fc328e9c6f41cc10b903479e2340c754fc314b9..030219c81650a932e6236f9b002125632b8981d9 100644 (file)
@@ -3,7 +3,7 @@
                <div class="item">
                        <div class="ui floating dropdown link jump">
                                <span class="text">
-                                       <img class="ui avatar image" src="{{.ContextUser.RelAvatarLink}}" title="{{.ContextUser.Name}}" width="28" height="28">
+                                       {{avatar .ContextUser}}
                                        {{.ContextUser.ShortName 20}}
                                        {{if .ContextUser.IsOrganization}}
                                                <span class="org-visibility">
                                        </div>
                                        <div class="scrolling menu items">
                                                <a class="{{if eq .ContextUser.ID .SignedUser.ID}}active selected{{end}} item" href="{{AppSubUrl}}/{{if .PageIsIssues}}issues{{else if .PageIsPulls}}pulls{{else if .PageIsMilestonesDashboard}}milestones{{end}}">
-                                                       <img class="ui avatar image" src="{{.SignedUser.RelAvatarLink}}" width="28" height="28">
-                                                       {{.SignedUser.Name}}
+                                                       {{avatar .SignedUser}}
                                                </a>
                                                {{range .Orgs}}
                                                        <a class="{{if eq $.ContextUser.ID .ID}}active selected{{end}} item" title="{{.Name}}" href="{{AppSubUrl}}/org/{{.Name}}/{{if $.PageIsIssues}}issues{{else if $.PageIsPulls}}pulls{{else if $.PageIsMilestonesDashboard}}milestones{{else}}dashboard{{end}}">
-                                                               <img class="ui avatar image" src="{{.RelAvatarLink}}" width="28" height="28">
+                                                               {{avatar .}}
                                                                {{.ShortName 20}}
                                                                <span class="org-visibility">
                                                                        {{if .Visibility.IsLimited}}<div class="ui orange tiny horizontal label">{{$.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
index 12498868f480bff4ffeeac27ac6e5e880ca39a91..12e14597646068ccb0f08c9ee35d8fe68dd22326 100644 (file)
@@ -6,19 +6,19 @@
                                <div class="ui card">
                                        {{if eq .SignedUserName .Owner.Name}}
                                                <a class="image poping up" href="{{AppSubUrl}}/user/settings" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center">
-                                                       <img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}" height="290" width="290"/>
+                                                       {{avatar .Owner 290}}
                                                </a>
                                        {{else}}
-                                               <span class="image">
-                                                       <img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}" height="290" width="290"/>
+                                               <span class="image" id="profile-avatar">
+                                                       {{avatar .Owner 290}}
                                                </span>
                                        {{end}}
-                                       <div class="content word-break">
+                                       <div class="content word-break profile-avatar-name">
                                                {{if .Owner.FullName}}<span class="header text center">{{.Owner.FullName}}</span>{{end}}
                                                <span class="username text center">{{.Owner.Name}}</span>
                                        </div>
                                        <div class="extra content word-break">
-                                               <ul class="text black">
+                                               <ul>
                                                        {{if .Owner.Location}}
                                                                <li>{{svg "octicon-location"}} {{.Owner.Location}}</li>
                                                        {{end}}
@@ -54,7 +54,9 @@
                                                                {{range .Orgs}}
                                                                        {{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}}
                                                                        <li>
-                                                                               <a href="{{.HomeLink}}"><img class="ui image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a>
+                                                                               <a class="poping up" href="{{.HomeLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted">
+                                                                                       {{avatar .}}
+                                                                               </a>
                                                                        </li>
                                                                        {{end}}
                                                                {{end}}
index d4db08a77f340bf90d7033b3bbc2dd277e3cd7e1..b444783dbb95f13811e00945e89958c2cfe7fd4b 100644 (file)
                                                <div class="ui selection owner dropdown">
                                                        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
                                                        <span class="text" title="{{.ContextUser.Name}}">
-                                                               <img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
+                                                               {{avatar .ContextUser 28 "mini"}}
                                                                {{.ContextUser.ShortName 20}}
                                                        </span>
                                                        {{svg "octicon-triangle-down" 14 "dropdown icon"}}
                                                        <div class="menu">
                                                                <div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
-                                                                       <img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> {{.SignedUser.ShortName 20}}
+                                                                       {{avatar .SignedUser 28 "mini"}}
+                                                                       {{.SignedUser.ShortName 20}}
                                                                </div>
                                                                {{range .Orgs}}
                                                                <div class="item" data-value="{{.ID}}" title="{{.Name}}">
-                                                                       <img class="ui mini image" src="{{.RelAvatarLink}}"> {{.ShortName 20}}
+                                                                       {{avatar . 28 "mini"}}
+                                                                       {{.ShortName 20}}
                                                                </div>
                                                                {{end}}
                                                        </div>
index 8d03ddead9de92790e65f4703b193ccdf1f460c9..07b56b90e8335c73876d65e732966ffedd4af15a 100644 (file)
@@ -22,7 +22,7 @@
                                                                <button type="submit" class="ui blue small button" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.members.leave"}}</button>
                                                        </form>
                                                </div>
-                                               <img class="ui mini image" src="{{.RelAvatarLink}}">
+                                               {{avatar . 28 "mini"}}
                                                <div class="content">
                                                                <a href="{{.HomeLink}}">{{.Name}}</a>
                                                </div>
index e8ed3748b3cf90fd54ced3d31c0e1d4095a37e86..20978bc605f17094a387e7871fd5a448ef1e6ffa 100644 (file)
@@ -410,6 +410,19 @@ a.muted:hover,
   border-color: var(--color-secondary);
 }
 
+.ui.avatar.images .image,
+.ui.avatar.images img,
+.ui.avatar.images svg,
+.ui.avatar.image img,
+.ui.avatar.image svg,
+.ui.avatar.image,
+.ui.cards > .card img.avatar,
+.ui.cards > .card .avatar img,
+.ui.card img.avatar,
+.ui.card .avatar img {
+  border-radius: var(--border-radius);
+}
+
 .dont-break-out {
   overflow-wrap: break-word;
   word-wrap: break-word;
@@ -791,15 +804,6 @@ a.muted:hover,
     font-weight: normal;
   }
 
-  .avatar.image,
-  .avatar.image img,
-  .avatar.image svg,
-  .avatar.images .image,
-  .avatar.images img,
-  .avatar.images svg {
-    border-radius: 3px;
-  }
-
   .form {
     .fake {
       display: none !important;
@@ -1559,6 +1563,11 @@ a.ui.label:hover {
   margin-bottom: .4em;
 }
 
+.ui.cards > .card > .extra,
+.ui.card > .extra {
+  color: var(--color-text);
+}
+
 .color-icon {
   margin-right: .5em;
   margin-left: .5em;
index d340f2855332a45a42a1f03b6feaa2f73d8d04c6..771c7e088ccf462843cf8205047ae414584dfe5d 100644 (file)
       margin-right: auto;
     }
 
-    .ui.avatar {
+    .left .ui.avatar {
       margin-top: 13px;
     }
 
index 089c649f618c70e0c62c0f0531fd5cc36c4c2ebe..7002a158f4a3d91fcd53966cda38d9b28ea55104 100644 (file)
   }
 
   &.profile {
-    #org-avatar {
+    .org-avatar {
       width: 100px;
       height: 100px;
       margin-right: 15px;
     }
 
     #org-info {
+      overflow-wrap: anywhere;
+
       .ui.header {
         font-size: 36px;
         margin-bottom: 0;
index a830e14c18d400f5624d3632f5ac34f4710d3d90..7c0f38195367891b4fdf461dc7595b40a1fa4359 100644 (file)
           position: absolute;
           left: -72px;
           img {
-            width: 40px;
-            height: 40px;
+            width: 40px !important;
+            height: 40px !important;
           }
         }
 
index c1eb15237e9b268eb2cfd3175f00a41d633145c5..2b37e8ee94b1d742fdf56718c7a9f804b4131bda 100644 (file)
         line-height: 1.3rem;
       }
 
+      .profile-avatar-name {
+        border-top: none;
+      }
+
       .extra.content {
         padding: 0;
 
       }
 
       #profile-avatar {
+        background: none;
+        padding: 1rem 1rem .25rem;
+
         img {
           width: 100%;
+          height: auto;
+          object-fit: contain;
+          margin: 0;
         }
+
         @media @mediaSm {
           height: 250px;
           overflow: hidden;
index eac28df21e75a71704955832a07b2baf5a65bbcb..3cf5161651ef91de35a18dba6b1482197e586bbc 100644 (file)
@@ -8,6 +8,7 @@
 .fc { flex-direction: column !important; }
 .f1 { flex: 1 !important; }
 .fw { flex-wrap: wrap !important; }
+.vm { vertical-align: middle !important; }
 
 .mono {
   font-family: var(--fonts-monospace) !important;