Direct avatar rendering (#13649)

* 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
mj-v1.14.3
silverwind 3 years ago committed by GitHub
parent 0d35ef5b43
commit 9269a038a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -140,12 +140,6 @@ func (a *Action) GetDisplayNameTitle() string {
return a.GetActFullName() 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. // GetRepoUserName returns the name of the action repository owner.
func (a *Action) GetRepoUserName() string { func (a *Action) GetRepoUserName() string {
a.loadRepo() a.loadRepo()

@ -8,9 +8,13 @@ import (
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"net/url" "net/url"
"path"
"strconv"
"strings" "strings"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@ -20,6 +24,28 @@ type EmailHash struct {
Email string `xorm:"UNIQUE NOT NULL"` 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 // GetEmailForHash converts a provided md5sum to the email
func GetEmailForHash(md5Sum string) (string, error) { func GetEmailForHash(md5Sum string) (string, error) {
return cache.GetString("Avatar:"+md5Sum, func() (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 // LibravatarURL returns the URL for the given email. This function should only
func AvatarLink(email string) string { // 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)) lowerEmail := strings.ToLower(strings.TrimSpace(email))
sum := fmt.Sprintf("%x", md5.Sum([]byte(lowerEmail))) sum := fmt.Sprintf("%x", md5.Sum([]byte(lowerEmail)))
_, _ = cache.GetString("Avatar:"+sum, func() (string, error) { _, _ = cache.GetString("Avatar:"+sum, func() (string, error) {
@ -57,3 +99,34 @@ func AvatarLink(email string) string {
}) })
return setting.AppSubURL + "/avatar/" + url.PathEscape(sum) 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)
}

@ -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),
)
}

@ -13,7 +13,6 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/avatar" "code.gitea.io/gitea/modules/avatar"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
@ -41,7 +40,7 @@ func (u *User) generateRandomAvatar(e Engine) error {
} }
if u.Avatar == "" { 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 { 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 { func (u *User) RealSizedAvatarLink(size int) string {
if u.ID == -1 { if u.ID == -1 {
return base.DefaultAvatarLink() return DefaultAvatarLink()
} }
switch { switch {
case u.UseCustomAvatar: case u.UseCustomAvatar:
if u.Avatar == "" { if u.Avatar == "" {
return base.DefaultAvatarLink() return DefaultAvatarLink()
} }
return setting.AppSubURL + "/avatars/" + u.Avatar return setting.AppSubURL + "/avatars/" + u.Avatar
case setting.DisableGravatar, setting.OfflineMode: case setting.DisableGravatar, setting.OfflineMode:
@ -94,14 +93,14 @@ func (u *User) RealSizedAvatarLink(size int) string {
return setting.AppSubURL + "/avatars/" + u.Avatar 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 // 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 // may either be a sub-URL to this site, or a full URL to an external avatar
// service. // service.
func (u *User) RelAvatarLink() string { func (u *User) RelAvatarLink() string {
return u.SizedRelAvatarLink(base.DefaultAvatarSize) return u.SizedRelAvatarLink(DefaultAvatarSize)
} }
// AvatarLink returns user avatar absolute link. // AvatarLink returns user avatar absolute link.

@ -168,7 +168,7 @@ func (s *SSPI) newUser(ctx *macaron.Context, username string, cfg *models.SSPICo
IsActive: cfg.AutoActivateUsers, IsActive: cfg.AutoActivateUsers,
Language: cfg.DefaultLanguage, Language: cfg.DefaultLanguage,
UseCustomAvatar: true, UseCustomAvatar: true,
Avatar: base.DefaultAvatarLink(), Avatar: models.DefaultAvatarLink(),
EmailNotificationsPreference: models.EmailNotificationsDisabled, EmailNotificationsPreference: models.EmailNotificationsDisabled,
} }
if err := models.CreateUser(user); err != nil { if err := models.CreateUser(user); err != nil {

@ -12,9 +12,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"os" "os"
"path"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
@ -134,93 +132,6 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
return code 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. // FileSize calculates the file size and generate user-friendly string.
func FileSize(s int64) string { func FileSize(s int64) string {
return humanize.IBytes(uint64(s)) return humanize.IBytes(uint64(s))

@ -5,11 +5,8 @@
package base package base
import ( import (
"net/url"
"testing" "testing"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -56,44 +53,6 @@ func TestBasicAuthEncode(t *testing.T) {
// TODO: Test VerifyTimeLimitCode() // TODO: Test VerifyTimeLimitCode()
// TODO: Test CreateTimeLimitCode() // 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) { func TestFileSize(t *testing.T) {
var size int64 = 512 var size int64 = 512
assert.Equal(t, "512 B", FileSize(size)) assert.Equal(t, "512 B", FileSize(size))

@ -123,7 +123,7 @@ func (pc *PushCommits) AvatarLink(email string) string {
var err error var err error
u, err = models.GetUserByEmail(email) u, err = models.GetUserByEmail(email)
if err != nil { if err != nil {
pc.avatars[email] = models.AvatarLink(email) pc.avatars[email] = models.HashedAvatarLink(email)
if !models.IsErrUserNotExist(err) { if !models.IsErrUserNotExist(err) {
log.Error("GetUserByEmail: %v", err) log.Error("GetUserByEmail: %v", err)
return "" return ""

@ -88,7 +88,6 @@ func NewFuncMap() []template.FuncMap {
"AllowedReactions": func() []string { "AllowedReactions": func() []string {
return setting.UI.Reactions return setting.UI.Reactions
}, },
"AvatarLink": models.AvatarLink,
"Safe": Safe, "Safe": Safe,
"SafeJS": SafeJS, "SafeJS": SafeJS,
"Str2html": Str2html, "Str2html": Str2html,
@ -339,7 +338,9 @@ func NewFuncMap() []template.FuncMap {
} }
return false return false
}, },
"svg": SVG, "svg": SVG,
"avatar": Avatar,
"avatarByEmail": AvatarByEmail,
"SortArrow": func(normSort, revSort, urlSort string, isDefault bool) template.HTML { "SortArrow": func(normSort, revSort, urlSort string, isDefault bool) template.HTML {
// if needed // if needed
if len(normSort) == 0 || len(urlSort) == 0 { if len(normSort) == 0 || len(urlSort) == 0 {
@ -499,18 +500,38 @@ func NewTextFuncMap() []texttmpl.FuncMap {
var widthRe = regexp.MustCompile(`width="[0-9]+?"`) var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
var heightRe = regexp.MustCompile(`height="[0-9]+?"`) var heightRe = regexp.MustCompile(`height="[0-9]+?"`)
// SVG render icons - arguments icon name (string), size (int), class (string) func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
func SVG(icon string, others ...interface{}) template.HTML { size := defaultSize
size := 16
if len(others) > 0 && others[0].(int) != 0 { if len(others) > 0 && others[0].(int) != 0 {
size = others[0].(int) size = others[0].(int)
} }
class := "" class := defaultClass
if len(others) > 1 && others[1].(string) != "" { 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 svgStr, ok := svg.SVGs[icon]; ok {
if size != 16 { if size != 16 {
svgStr = widthRe.ReplaceAllString(svgStr, fmt.Sprintf(`width="%d"`, size)) svgStr = widthRe.ReplaceAllString(svgStr, fmt.Sprintf(`width="%d"`, size))
@ -524,6 +545,38 @@ func SVG(icon string, others ...interface{}) template.HTML {
return 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 // Safe render raw as HTML
func Safe(raw string) template.HTML { func Safe(raw string) template.HTML {
return template.HTML(raw) return template.HTML(raw)

@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"html" "html"
gotemplate "html/template" gotemplate "html/template"
"net/url"
"strings" "strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -19,7 +18,7 @@ import (
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
) )
@ -209,17 +208,15 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
commit := commitNames[part.Sha] commit := commitNames[part.Sha]
if index == 0 { if index == 0 {
// User avatar image // User avatar image
avatar := ""
commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Data["Lang"].(string)) commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Data["Lang"].(string))
var avatar string
if commit.User != nil { if commit.User != nil {
authorName := commit.Author.Name avatar = string(templates.Avatar(commit.User, 18, "mr-3"))
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))
} else { } 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)) 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 { } else {
commitInfo.WriteString(fmt.Sprintf(`<div class="blame-info%s">&#8203;</div>`, attr)) commitInfo.WriteString(fmt.Sprintf(`<div class="blame-info%s">&#8203;</div>`, attr))

@ -6,11 +6,11 @@ package user
import ( import (
"errors" "errors"
"net/url"
"strconv" "strconv"
"strings" "strings"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
) )
@ -46,23 +46,38 @@ func Avatar(ctx *context.Context) {
// AvatarByEmailHash redirects the browser to the appropriate Avatar link // AvatarByEmailHash redirects the browser to the appropriate Avatar link
func AvatarByEmailHash(ctx *context.Context) { func AvatarByEmailHash(ctx *context.Context) {
var err error
hash := ctx.Params(":hash") hash := ctx.Params(":hash")
if len(hash) == 0 { if len(hash) == 0 {
ctx.ServerError("invalid avatar hash", errors.New("hash cannot be empty")) ctx.ServerError("invalid avatar hash", errors.New("hash cannot be empty"))
return return
} }
email, err := models.GetEmailForHash(hash)
var email string
email, err = models.GetEmailForHash(hash)
if err != nil { if err != nil {
ctx.ServerError("invalid avatar hash", err) ctx.ServerError("invalid avatar hash", err)
return return
} }
if len(email) == 0 { if len(email) == 0 {
ctx.Redirect(base.DefaultAvatarLink()) ctx.Redirect(models.DefaultAvatarLink())
return return
} }
size := ctx.QueryInt("size") size := ctx.QueryInt("size")
if size == 0 { 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))
} }

@ -47,7 +47,7 @@
<div class="right stackable menu"> <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"> <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"> <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="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span>
<span class="mobile-only">{{.SignedUser.Name}}</span> <span class="mobile-only">{{.SignedUser.Name}}</span>
<span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span> <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>
@ -102,7 +102,7 @@
<div class="ui dropdown jump item poping up" tabindex="-1" data-content="{{.i18n.Tr "user_profile_and_more"}}" data-variation="tiny inverted"> <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"> <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="sr-only">{{.i18n.Tr "user_profile_and_more"}}</span>
<span class="mobile-only">{{.SignedUser.Name}}</span> <span class="mobile-only">{{.SignedUser.Name}}</span>
<span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span> <span class="fitted not-mobile" tabindex="-1">{{svg "octicon-triangle-down"}}</span>

@ -7,7 +7,7 @@
<div class="ui user list"> <div class="ui user list">
{{range .Users}} {{range .Users}}
<div class="item"> <div class="item">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{avatar .}}
<div class="content"> <div class="content">
<span class="header"> <span class="header">
<a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}} <a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}

@ -2,9 +2,7 @@
{{range .Repos}} {{range .Repos}}
<div class="item"> <div class="item">
<div class="ui header"> <div class="ui header">
{{if .RelAvatarLink}} {{avatar .}}
<img class="ui avatar image" src="{{.RelAvatarLink}}">
{{end}}
<a class="name" href="{{.Link}}"> <a class="name" href="{{.Link}}">
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}} {{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}
</a> </a>

@ -7,7 +7,7 @@
<div class="ui user list"> <div class="ui user list">
{{range .Users}} {{range .Users}}
<div class="item"> <div class="item">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{avatar .}}
<div class="content"> <div class="content">
<span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span> <span class="header"><a href="{{.HomeLink}}">{{.Name}}</a> {{.FullName}}</span>
<div class="description"> <div class="description">

@ -3,7 +3,7 @@
<div class="ui vertically grid head"> <div class="ui vertically grid head">
<div class="column"> <div class="column">
<div class="ui header"> <div class="ui header">
<img class="ui image" src="{{.SizedRelAvatarLink 100}}"> {{avatar . 100}}
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> <span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
<span class="org-visibility"> <span class="org-visibility">
{{if .Visibility.IsLimited}}<div class="ui medium orange horizontal label">{{$.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}} {{if .Visibility.IsLimited}}<div class="ui medium orange horizontal label">{{$.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}

@ -1,9 +1,7 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content organization profile"> <div class="page-content organization profile">
{{/* overflow: auto is the clearfix - this avoids the image going beyond <div class="ui container df">
the container where it is supposed to stay inside. */}} {{avatar .Org 140 "org-avatar"}}
<div class="ui container" style="overflow: auto">
<img class="ui left" id="org-avatar" src="{{.Org.SizedRelAvatarLink 140}}"/>
<div id="org-info"> <div id="org-info">
<div class="ui header"> <div class="ui header">
{{.Org.DisplayName}} {{.Org.DisplayName}}
@ -53,7 +51,9 @@
{{$isMember := .IsOrganizationMember}} {{$isMember := .IsOrganizationMember}}
{{range .Members}} {{range .Members}}
{{if or $isMember (.IsPublicMember $.Org.ID)}} {{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}}
{{end}} {{end}}
</div> </div>

@ -8,7 +8,7 @@
{{ range .Members}} {{ range .Members}}
<div class="item ui grid"> <div class="item ui grid">
<div class="ui one wide column"> <div class="ui one wide column">
<img class="ui avatar" src="{{.SizedRelAvatarLink 48}}"> {{avatar . 48}}
</div> </div>
<div class="ui three wide column"> <div class="ui three wide column">
<div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div> <div class="meta"><a href="{{.HomeLink}}">{{.Name}}</a></div>

@ -33,7 +33,7 @@
</form> </form>
{{end}} {{end}}
<a href="{{.HomeLink}}"> <a href="{{.HomeLink}}">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{avatar .}}
{{.DisplayName}} {{.DisplayName}}
</a> </a>
</div> </div>

@ -32,7 +32,7 @@
<div class="ui attached segment members"> <div class="ui attached segment members">
{{range .Members}} {{range .Members}}
<a href="{{.HomeLink}}" title="{{.Name}}"> <a href="{{.HomeLink}}" title="{{.Name}}">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{avatar .}}
</a> </a>
{{end}} {{end}}
</div> </div>

@ -38,14 +38,14 @@
<div class="ui stackable grid"> <div class="ui stackable grid">
<div class="nine wide column"> <div class="nine wide column">
{{if .Author}} {{if .Author}}
<img class="ui avatar image" src="{{.Author.RelAvatarLink}}" /> {{avatar .Author}}
{{if .Author.FullName}} {{if .Author.FullName}}
<a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a> <a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a>
{{else}} {{else}}
<a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a> <a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a>
{{end}} {{end}}
{{else}} {{else}}
<img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" /> {{avatarByEmail .Commit.Author.Email .Commit.Author.Email 12}}
<strong>{{.Commit.Author.Name}}</strong> <strong>{{.Commit.Author.Name}}</strong>
{{end}} {{end}}
<span class="text grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span> <span class="text grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span>
@ -53,10 +53,10 @@
<div class="committed-by"> <div class="committed-by">
<span class="text grey">{{svg "octicon-git-commit"}}{{.i18n.Tr "repo.diff.committed_by"}}</span> <span class="text grey">{{svg "octicon-git-commit"}}{{.i18n.Tr "repo.diff.committed_by"}}</span>
{{if ne .Verification.CommittingUser.ID 0}} {{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> <a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a>
{{else}} {{else}}
<img class="ui avatar image" src="{{AvatarLink .Commit.Committer.Email}}" /> {{avatarByEmail .Commit.Committer.Email .Commit.Committer.Name}}
<strong>{{.Commit.Committer.Name}}</strong> <strong>{{.Commit.Committer.Name}}</strong>
{{end}} {{end}}
</div> </div>
@ -98,13 +98,13 @@
{{else}} {{else}}
<span class="ui text">{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span> <span class="ui text">{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span>
{{end}} {{end}}
<img class="ui avatar image" src="{{.Verification.SigningUser.RelAvatarLink}}" /> {{avatar .Verification.SigningUser}}
<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong></a> <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> <span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
{{else}} {{else}}
<span title="{{.i18n.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span> <span title="{{.i18n.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span>
<span class="ui text">{{.i18n.Tr "repo.commits.signed_by"}}:</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> <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> <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}} {{end}}

@ -18,9 +18,10 @@
{{if .User.FullName}} {{if .User.FullName}}
{{$userName = .User.FullName}} {{$userName = .User.FullName}}
{{end}} {{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}} {{else}}
<img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/>&nbsp;&nbsp;{{$userName}} {{avatarByEmail .Author.Email .Author.Name 28 "mr-2"}}
{{$userName}}
{{end}} {{end}}
</td> </td>
<td class="sha"> <td class="sha">

@ -7,9 +7,11 @@
<div class="singular-commit" id="{{$tag}}"> <div class="singular-commit" id="{{$tag}}">
<span class="badge badge-commit">{{svg "octicon-git-commit"}}</span> <span class="badge badge-commit">{{svg "octicon-git-commit"}}</span>
{{if .User}} {{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}} {{else}}
<img class="ui avatar image" src="{{AvatarLink .Author.Email}}" alt=""/> {{avatarByEmail .Author.Email .Author.Name}}
{{end}} {{end}}
<span class="ui float right shabox"> <span class="ui float right shabox">

@ -14,18 +14,18 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser 28 "mini"}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu"> <div class="menu">
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> <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}} {{.SignedUser.ShortName 20}}
</div> </div>
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.ShortName 20}} {{.ShortName 20}}
</div> </div>
{{end}} {{end}}

@ -6,7 +6,7 @@
<span class="avatar"><img src="/img/avatar_default.png"></span> <span class="avatar"><img src="/img/avatar_default.png"></span>
{{else}} {{else}}
<a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}> <a class="avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
{{end}} {{end}}
<div class="content comment-container"> <div class="content comment-container">

@ -1,5 +1,5 @@
<div class="commit-form-wrapper"> <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"> <div class="commit-form">
<h3>{{- if .CanCommitToBranch.WillSign}} <h3>{{- if .CanCommitToBranch.WillSign}}
<span title="{{.i18n.Tr "repo.signing.will_sign" .CanCommitToBranch.SigningKey}}">{{svg "octicon-lock" 24}}</span> <span title="{{.i18n.Tr "repo.signing.will_sign" .CanCommitToBranch.SigningKey}}">{{svg "octicon-lock" 24}}</span>

@ -8,7 +8,7 @@
<div class="ui list"> <div class="ui list">
{{range .Forks}} {{range .Forks}}
<div class="item"> <div class="item">
<img class="ui avatar image" src="{{.Owner.RelAvatarLink}}"> {{avatar .Owner}}
<div class="link"> <div class="link">
<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a> <a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
/ /

@ -67,9 +67,11 @@
{{if $commit.User.FullName}} {{if $commit.User.FullName}}
{{$userName = $commit.User.FullName}} {{$userName = $commit.User.FullName}}
{{end}} {{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}} {{else}}
<img class="ui avatar image" src="{{AvatarLink $commit.Commit.Author.Email}}" alt=""/>{{$userName}} {{avatarByEmail $commit.Commit.Author.Email $userName}}
{{$userName}}
{{end}} {{end}}
</span> </span>
<span class="time df ac">{{$commit.Date}}</span> <span class="time df ac">{{$commit.Date}}</span>

@ -3,8 +3,8 @@
<div class="ui container"> <div class="ui container">
<div class="repo-header"> <div class="repo-header">
<div class="ui huge breadcrumb repo-title"> <div class="ui huge breadcrumb repo-title">
{{if .RelAvatarLink}} {{if .Name}}
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{avatar .}}
{{else}} {{else}}
{{template "repo/header_icon" .}} {{template "repo/header_icon" .}}
{{end}} {{end}}

@ -7,7 +7,7 @@
{{else if and (not $.IsMirror) (not $.IsFork) ($.Owner)}} {{else if and (not $.IsMirror) (not $.IsFork) ($.Owner)}}
{{svg "octicon-repo" 32}} {{svg "octicon-repo" 32}}
{{if $.Owner.Visibility.IsPrivate}} {{if $.Owner.Visibility.IsPrivate}}
<img class="ui avatar image" src="{{$.Owner.RelAvatarLink}}"> {{avatar $.Owner}}
{{end}} {{end}}
{{else if $.IsMirror}} {{else if $.IsMirror}}
{{svg "octicon-mirror" 32}} {{svg "octicon-mirror" 32}}

@ -70,7 +70,9 @@
<div class="menu"> <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> <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}} {{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}} {{end}}
</div> </div>
</div> </div>
@ -173,7 +175,7 @@
</div> </div>
{{range .Assignees}} {{range .Assignees}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee"> <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
<img src="{{.RelAvatarLink}}"> {{.GetDisplayName}} {{avatar .}}
</div> </div>
{{end}} {{end}}
</div> </div>

@ -68,7 +68,10 @@
<div class="menu"> <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> <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}} {{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}} {{end}}
</div> </div>
</div> </div>
@ -151,7 +154,8 @@
</div> </div>
{{range .Assignees}} {{range .Assignees}}
<div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee"> <div class="item issue-action" data-element-id="{{.ID}}" data-url="{{$.RepoLink}}/issues/assignee">
<img src="{{.RelAvatarLink}}"> {{.GetDisplayName}} {{avatar . 28 "mr-2"}}
{{.GetDisplayName}}
</div> </div>
{{end}} {{end}}
</div> </div>

@ -9,7 +9,7 @@
<div class="ui comments"> <div class="ui comments">
<div class="comment"> <div class="comment">
<a class="avatar" href="{{.SignedUser.HomeLink}}"> <a class="avatar" href="{{.SignedUser.HomeLink}}">
<img src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser}}
</a> </a>
<div class="ui segment content"> <div class="ui segment content">
<div class="field"> <div class="field">
@ -219,7 +219,7 @@
<a class="item muted" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}"> <a class="item muted" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}">
<span class="octicon-check invisible">{{svg "octicon-check"}}</span> <span class="octicon-check invisible">{{svg "octicon-check"}}</span>
<span class="text"> <span class="text">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{.GetDisplayName}} {{avatar . 28 "mr-3"}}{{.GetDisplayName}}
</span> </span>
</a> </a>
{{end}} {{end}}
@ -231,7 +231,7 @@
</span> </span>
{{range .Assignees}} {{range .Assignees}}
<a class="hide item p-2 muted" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}"> <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> </a>
{{end}} {{end}}
</div> </div>

@ -16,7 +16,7 @@
<span class="timeline-avatar"><img src="/img/avatar_default.png"></span> <span class="timeline-avatar"><img src="/img/avatar_default.png"></span>
{{else}} {{else}}
<a class="timeline-avatar" {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}> <a class="timeline-avatar" {{if gt .Issue.Poster.ID 0}}href="{{.Issue.Poster.HomeLink}}"{{end}}>
<img src="{{.Issue.Poster.RelAvatarLink}}"> {{avatar .Issue.Poster}}
</a> </a>
{{end}} {{end}}
<div class="content comment-container"> <div class="content comment-container">
@ -93,7 +93,7 @@
{{ if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived) }} {{ if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived) }}
<div class="timeline-item comment form"> <div class="timeline-item comment form">
<a class="timeline-avatar" href="{{.SignedUser.HomeLink}}"> <a class="timeline-avatar" href="{{.SignedUser.HomeLink}}">
<img src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser}}
</a> </a>
<div class="content"> <div class="content">
<form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post"> <form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">
@ -144,7 +144,7 @@
{{if .Repository.IsArchived}} {{if .Repository.IsArchived}}
<div class="timeline-item comment form"> <div class="timeline-item comment form">
<a class="timeline-avatar" href="{{.SignedUser.HomeLink}}"> <a class="timeline-avatar" href="{{.SignedUser.HomeLink}}">
<img src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser}}
</a> </a>
<div class="content"> <div class="content">
<form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post"> <form class="ui segment form" id="comment-form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/comments" method="post">

@ -15,7 +15,7 @@
<span class="timeline-avatar"><img src="/img/avatar_default.png"></span> <span class="timeline-avatar"><img src="/img/avatar_default.png"></span>
{{else}} {{else}}
<a class="timeline-avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}> <a class="timeline-avatar" {{if gt .Poster.ID 0}}href="{{.Poster.HomeLink}}"{{end}}>
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
{{end}} {{end}}
<div class="content comment-container"> <div class="content comment-container">
@ -92,8 +92,8 @@
{{else if eq .Type 1}} {{else if eq .Type 1}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge bg-green text-white">{{svg "octicon-dot-fill"}}</span> <span class="badge bg-green text-white">{{svg "octicon-dot-fill"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -107,8 +107,8 @@
{{else if eq .Type 2}} {{else if eq .Type 2}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge bg-red text-white">{{svg "octicon-circle-slash"}}</span> <span class="badge bg-red text-white">{{svg "octicon-circle-slash"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -122,8 +122,8 @@
{{else if eq .Type 28}} {{else if eq .Type 28}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge bg-purple text-white">{{svg "octicon-git-merge"}}</span> <span class="badge bg-purple text-white">{{svg "octicon-git-merge"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -147,8 +147,8 @@
{{ $createdStr:= TimeSinceUnix .CreatedUnix $.Lang }} {{ $createdStr:= TimeSinceUnix .CreatedUnix $.Lang }}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-bookmark"}}</span> <span class="badge">{{svg "octicon-bookmark"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
{{if eq .RefAction 3}}<del>{{end}} {{if eq .RefAction 3}}<del>{{end}}
<span class="text grey"> <span class="text grey">
@ -164,8 +164,8 @@
{{else if eq .Type 4}} {{else if eq .Type 4}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-bookmark"}}</span> <span class="badge">{{svg "octicon-bookmark"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -180,8 +180,8 @@
{{if or .AddedLabels .RemovedLabels}} {{if or .AddedLabels .RemovedLabels}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-tag"}}</span> <span class="badge">{{svg "octicon-tag"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -198,8 +198,8 @@
{{else if eq .Type 8}} {{else if eq .Type 8}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-milestone"}}</span> <span class="badge">{{svg "octicon-milestone"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -211,8 +211,8 @@
<span class="badge">{{svg "octicon-person"}}</span> <span class="badge">{{svg "octicon-person"}}</span>
{{if gt .AssigneeID 0}} {{if gt .AssigneeID 0}}
{{if .RemovedAssignee}} {{if .RemovedAssignee}}
<a class="ui avatar image" href="{{.Assignee.HomeLink}}"> <a href="{{.Assignee.HomeLink}}">
<img src="{{.Assignee.RelAvatarLink}}"> {{avatar .Assignee}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Assignee.HomeLink}}">{{.Assignee.GetDisplayName}}</a> <a class="author" href="{{.Assignee.HomeLink}}">{{.Assignee.GetDisplayName}}</a>
@ -223,8 +223,8 @@
{{ end }} {{ end }}
</span> </span>
{{else}} {{else}}
<a class="ui avatar image" href="{{.Assignee.HomeLink}}"> <a href="{{.Assignee.HomeLink}}">
<img src="{{.Assignee.RelAvatarLink}}"> {{avatar .Assignee}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Assignee.HomeLink}}">{{.Assignee.GetDisplayName}}</a> <a class="author" href="{{.Assignee.HomeLink}}">{{.Assignee.GetDisplayName}}</a>
@ -240,8 +240,8 @@
{{else if eq .Type 10}} {{else if eq .Type 10}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-pencil"}}</span> <span class="badge">{{svg "octicon-pencil"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -251,8 +251,8 @@
{{else if eq .Type 11}} {{else if eq .Type 11}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-git-branch"}}</span> <span class="badge">{{svg "octicon-git-branch"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -262,8 +262,8 @@
{{else if eq .Type 12}} {{else if eq .Type 12}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -273,8 +273,8 @@
{{else if eq .Type 13}} {{else if eq .Type 13}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -288,8 +288,8 @@
{{else if eq .Type 14}} {{else if eq .Type 14}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -303,8 +303,8 @@
{{else if eq .Type 15}} {{else if eq .Type 15}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -314,8 +314,8 @@
{{else if eq .Type 16}} {{else if eq .Type 16}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -325,8 +325,8 @@
{{else if eq .Type 17}} {{else if eq .Type 17}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -336,8 +336,8 @@
{{else if eq .Type 18}} {{else if eq .Type 18}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -347,8 +347,8 @@
{{else if eq .Type 19}} {{else if eq .Type 19}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-package-dependents"}}</span> <span class="badge">{{svg "octicon-package-dependents"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -372,8 +372,8 @@
{{else if eq .Type 20}} {{else if eq .Type 20}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-package-dependents"}}</span> <span class="badge">{{svg "octicon-package-dependents"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -400,7 +400,7 @@
{{if .OriginalAuthor }} {{if .OriginalAuthor }}
{{else}} {{else}}
<a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}> <a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
{{end}} {{end}}
<span class="badge {{if eq .Review.Type 1}}bg-green <span class="badge {{if eq .Review.Type 1}}bg-green
@ -509,7 +509,7 @@
<div class="comment code-comment" id="{{.HashTag}}"> <div class="comment code-comment" id="{{.HashTag}}">
{{if not .OriginalAuthor }} {{if not .OriginalAuthor }}
<a class="avatar"> <a class="avatar">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
{{end}} {{end}}
<div class="content"> <div class="content">
@ -560,8 +560,8 @@
{{else if eq .Type 23}} {{else if eq .Type 23}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-lock"}}</span> <span class="badge">{{svg "octicon-lock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
{{ if .Content }} {{ if .Content }}
<span class="text grey"> <span class="text grey">
@ -578,8 +578,8 @@
{{else if eq .Type 24}} {{else if eq .Type 24}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-key"}}</span> <span class="badge">{{svg "octicon-key"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -589,8 +589,8 @@
{{else if eq .Type 25}} {{else if eq .Type 25}}
<div class="timeline-item event"> <div class="timeline-item event">
<span class="badge">{{svg "octicon-git-branch"}}</span> <span class="badge">{{svg "octicon-git-branch"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a> <a href="{{.Poster.HomeLink}}">{{.Poster.Name}}</a>
@ -600,8 +600,8 @@
{{else if eq .Type 26}} {{else if eq .Type 26}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-clock"}}</span> <span class="badge">{{svg "octicon-clock"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -615,8 +615,8 @@
{{else if eq .Type 27}} {{else if eq .Type 27}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-eye"}}</span> <span class="badge">{{svg "octicon-eye"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
@ -658,8 +658,8 @@
{{if not $.UnitProjectsGlobalDisabled}} {{if not $.UnitProjectsGlobalDisabled}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-project"}}</span> <span class="badge">{{svg "octicon-project"}}</span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}"> <a href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}"> {{avatar .Poster}}
</a> </a>
<span class="text grey"> <span class="text grey">
<a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> <a class="author" href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>

@ -9,8 +9,8 @@
<div class="review-item"> <div class="review-item">
<div class="review-item-left"> <div class="review-item-left">
{{if .User}} {{if .User}}
<a class="ui avatar image" href="{{.User.HomeLink}}"> <a href="{{.User.HomeLink}}">
<img src="{{.User.RelAvatarLink}}"> {{avatar .User}}
</a> </a>
{{end}} {{end}}
<span class="text grey"> <span class="text grey">

@ -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}}> <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="octicon-check {{if not .Checked}}invisible{{end}}">{{svg "octicon-check"}}</span>
<span class="text"> <span class="text">
<img class="ui avatar image mr-2" loading="lazy" src="{{.User.RelAvatarLink}}"> {{avatar .User 28 "mr-3"}}
{{.User.GetDisplayName}} {{.User.GetDisplayName}}
</span> </span>
</a> </a>
@ -56,7 +56,7 @@
<div class="item mb-2"> <div class="item mb-2">
{{if .User}} {{if .User}}
<a class="muted sidebar-item-link" href="{{.User.HomeLink}}"> <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}} {{.User.GetDisplayName}}
</a> </a>
{{else if .Team}} {{else if .Team}}
@ -267,7 +267,7 @@
{{end}} {{end}}
<span class="octicon-check {{if not $checked}}invisible{{end}}">{{svg "octicon-check"}}</span> <span class="octicon-check {{if not $checked}}invisible{{end}}">{{svg "octicon-check"}}</span>
<span class="text"> <span class="text">
<img class="ui avatar image mr-2" loading="lazy" src="{{.RelAvatarLink}}"> {{avatar . 28 "mr-3"}}
{{.GetDisplayName}} {{.GetDisplayName}}
</span> </span>
</a> </a>
@ -280,7 +280,7 @@
{{range .Issue.Assignees}} {{range .Issue.Assignees}}
<div class="item"> <div class="item">
<a class="muted sidebar-item-link" href="{{$.RepoLink}}/{{if $.Issue.IsPull}}pulls{{else}}issues{{end}}?assignee={{.ID}}"> <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}} {{.GetDisplayName}}
</a> </a>
</div> </div>
@ -295,7 +295,9 @@
<div> <div>
{{range .Participants}} {{range .Participants}}
<a {{if gt .ID 0}}href="{{.HomeLink}}"{{end}}> <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> </a>
{{end}} {{end}}
</div> </div>
@ -376,7 +378,7 @@
{{range $user, $trackedtime := .WorkingUsers}} {{range $user, $trackedtime := .WorkingUsers}}
<div class="comment"> <div class="comment">
<a class="avatar"> <a class="avatar">
<img src="{{$user.RelAvatarLink}}"> {{avatar $user}}
</a> </a>
<div class="content"> <div class="content">
<a class="author">{{$user.DisplayName}}</a> <a class="author">{{$user.DisplayName}}</a>

@ -48,18 +48,18 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}"> <div class="menu" title="{{.SignedUser.Name}}">
<div class="item" data-value="{{.SignedUser.ID}}"> <div class="item" data-value="{{.SignedUser.ID}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser}}
{{.SignedUser.ShortName 20}} {{.SignedUser.ShortName 20}}
</div> </div>
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar .}}
{{.ShortName 20}} {{.ShortName 20}}
</div> </div>
{{end}} {{end}}

@ -82,18 +82,18 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}"> <div class="menu" title="{{.SignedUser.Name}}">
<div class="item" data-value="{{.SignedUser.ID}}"> <div class="item" data-value="{{.SignedUser.ID}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser}}
{{.SignedUser.ShortName 20}} {{.SignedUser.ShortName 20}}
</div> </div>
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar .}}
{{.ShortName 20}} {{.ShortName 20}}
</div> </div>
{{end}} {{end}}

@ -82,18 +82,18 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser 28 "mini"}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}"> <div class="menu" title="{{.SignedUser.Name}}">
<div class="item" data-value="{{.SignedUser.ID}}"> <div class="item" data-value="{{.SignedUser.ID}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser 28 "mini"}}
{{.SignedUser.ShortName 20}} {{.SignedUser.ShortName 20}}
</div> </div>
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.ShortName 20}} {{.ShortName 20}}
</div> </div>
{{end}} {{end}}

@ -82,18 +82,18 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser 28 "mini"}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu" title="{{.SignedUser.Name}}"> <div class="menu" title="{{.SignedUser.Name}}">
<div class="item" data-value="{{.SignedUser.ID}}"> <div class="item" data-value="{{.SignedUser.ID}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> {{avatar .SignedUser 28 "mini"}}
{{.SignedUser.ShortName 20}} {{.SignedUser.ShortName 20}}
</div> </div>
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.ShortName 20}} {{.ShortName 20}}
</div> </div>
{{end}} {{end}}

@ -78,7 +78,6 @@
{{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}} {{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}}
<div class="ui dropdown jump item poping up right" data-variation="tiny inverted"> <div class="ui dropdown jump item poping up right" data-variation="tiny inverted">
<span class="ui text"> <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 class="fitted not-mobile" tabindex="-1">{{svg "octicon-kebab-horizontal" 24}}</span>
</span> </span>
<div class="menu user-menu" tabindex="-1"> <div class="menu user-menu" tabindex="-1">

@ -14,20 +14,20 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser 28 "mini"}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu"> <div class="menu">
{{if .CanForkToUser}} {{if .CanForkToUser}}
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> <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}} {{.SignedUser.ShortName 20}}
</div> </div>
{{end}} {{end}}
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.ShortName 20}} {{.ShortName 20}}
</div> </div>
{{end}} {{end}}

@ -90,7 +90,7 @@
<p class="text grey"> <p class="text grey">
{{ if gt .Publisher.ID 0 }} {{ if gt .Publisher.ID 0 }}
<span class="author"> <span class="author">
<img class="img-10" src="{{.Publisher.RelAvatarLink}}"> {{avatar .Publisher 28 "img-10"}}
<a href="{{AppSubUrl}}/{{.Publisher.Name}}">{{.Publisher.Name}}</a> <a href="{{AppSubUrl}}/{{.Publisher.Name}}">{{.Publisher.Name}}</a>
</span> </span>
{{ end }} {{ end }}
@ -117,7 +117,7 @@
{{if .OriginalAuthor}} {{if .OriginalAuthor}}
{{svg "octicon-mark-github" 16 "mr-2"}}{{.OriginalAuthor}} {{svg "octicon-mark-github" 16 "mr-2"}}{{.OriginalAuthor}}
{{else if .Publisher}} {{else if .Publisher}}
<img class="img-10" src="{{.Publisher.RelAvatarLink}}"> {{avatar .Publisher 28 "img-10"}}
<a href="{{AppSubUrl}}/{{.Publisher.Name}}">{{.Publisher.GetDisplayName}}</a> <a href="{{AppSubUrl}}/{{.Publisher.Name}}">{{.Publisher.GetDisplayName}}</a>
{{else}} {{else}}
Ghost Ghost

@ -13,7 +13,7 @@
<div class="item ui grid"> <div class="item ui grid">
<div class="ui five wide column"> <div class="ui five wide column">
<a href="{{AppSubUrl}}/{{.Name}}"> <a href="{{AppSubUrl}}/{{.Name}}">
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{avatar .}}
{{.DisplayName}} {{.DisplayName}}
</a> </a>
</div> </div>

@ -35,7 +35,7 @@
</td> </td>
<td> <td>
<a href="{{AppSubUrl}}/{{$lock.Owner.Name}}"> <a href="{{AppSubUrl}}/{{$lock.Owner.Name}}">
<img class="ui avatar image" src="{{$lock.Owner.RelAvatarLink}}"> {{avatar $lock.Owner}}
{{$lock.Owner.DisplayName}} {{$lock.Owner.DisplayName}}
</a> </a>
</td> </td>

@ -48,7 +48,7 @@
<div class="menu"> <div class="menu">
{{range .Users}} {{range .Users}}
<div class="item" data-value="{{.ID}}"> <div class="item" data-value="{{.ID}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.GetDisplayName}} {{.GetDisplayName}}
</div> </div>
{{end}} {{end}}
@ -98,7 +98,7 @@
<div class="menu"> <div class="menu">
{{range .Users}} {{range .Users}}
<div class="item" data-value="{{.ID}}"> <div class="item" data-value="{{.ID}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.GetDisplayName}} {{.GetDisplayName}}
</div> </div>
{{end}} {{end}}
@ -178,7 +178,7 @@
<div class="menu"> <div class="menu">
{{range .Users}} {{range .Users}}
<div class="item" data-value="{{.ID}}"> <div class="item" data-value="{{.ID}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
{{.GetDisplayName}} {{.GetDisplayName}}
</div> </div>
{{end}} {{end}}

@ -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}}"> <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}} {{if ne .verification.SigningUser.ID 0}}
{{svg "gitea-lock"}} {{svg "gitea-lock"}}
<img class="ui signature avatar image" src="{{.verification.SigningUser.RelAvatarLink}}" /> {{avatar .verification.SigningUser "signature"}}
{{else}} {{else}}
<span title="{{$.root.i18n.Tr "gpg.default_key"}}">{{svg "gitea-lock-cog"}}</span> <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}} {{end}}
</div> </div>
{{else}} {{else}}

@ -8,7 +8,7 @@
{{range .Cards}} {{range .Cards}}
<li class="item ui segment"> <li class="item ui segment">
<a href="{{.HomeLink}}"> <a href="{{.HomeLink}}">
<img class="avatar" src="{{.RelAvatarLink}}"/> {{avatar .}}
</a> </a>
<h3 class="name"><a href="{{.HomeLink}}">{{.DisplayName}}</a></h3> <h3 class="name"><a href="{{.HomeLink}}">{{.DisplayName}}</a></h3>

@ -3,7 +3,7 @@
<tr class="commit-list"> <tr class="commit-list">
<th colspan="2"> <th colspan="2">
{{if .LatestCommitUser}} {{if .LatestCommitUser}}
<img class="ui avatar image img-12" src="{{.LatestCommitUser.RelAvatarLink}}" /> {{avatar .LatestCommitUser 28 "img-12"}}
{{if .LatestCommitUser.FullName}} {{if .LatestCommitUser.FullName}}
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> <a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
{{else}} {{else}}
@ -11,7 +11,7 @@
{{end}} {{end}}
{{else}} {{else}}
{{if .LatestCommit.Author}} {{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> <strong>{{.LatestCommit.Author.Name}}</strong>
{{end}} {{end}}
{{end}} {{end}}

@ -121,7 +121,7 @@
<div class="issue-item-icon-right text grey"> <div class="issue-item-icon-right text grey">
{{range .Assignees}} {{range .Assignees}}
<a class="ui assignee poping up" href="{{.HomeLink}}" data-content="{{.GetDisplayName}}" data-variation="inverted" data-position="left center"> <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> </a>
{{end}} {{end}}
</div> </div>

@ -1,7 +1,7 @@
{{range .Feeds}} {{range .Feeds}}
<div class="news"> <div class="news">
<div class="ui left"> <div class="ui left">
<img class="ui avatar image" src="{{.GetActAvatar}}" alt=""> {{avatar .ActUser}}
</div> </div>
<div class="ui grid"> <div class="ui grid">
<div class="ui fourteen wide column"> <div class="ui fourteen wide column">
@ -84,7 +84,13 @@
{{if $push.Commits}} {{if $push.Commits}}
{{range $push.Commits}} {{range $push.Commits}}
{{ $commitLink := printf "%s/commit/%s" $repoLink .Sha1}} {{ $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}}
{{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}} {{if and (gt $push.Len 1) $push.CompareURL}}<li><a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{$.i18n.Tr "action.compare_commits" $push.Len}} »</a></li>{{end}}

@ -3,7 +3,7 @@
<div class="item"> <div class="item">
<div class="ui floating dropdown link jump"> <div class="ui floating dropdown link jump">
<span class="text"> <span class="text">
<img class="ui avatar image" src="{{.ContextUser.RelAvatarLink}}" title="{{.ContextUser.Name}}" width="28" height="28"> {{avatar .ContextUser}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
{{if .ContextUser.IsOrganization}} {{if .ContextUser.IsOrganization}}
<span class="org-visibility"> <span class="org-visibility">
@ -19,12 +19,11 @@
</div> </div>
<div class="scrolling menu items"> <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}}"> <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"> {{avatar .SignedUser}}
{{.SignedUser.Name}}
</a> </a>
{{range .Orgs}} {{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}}"> <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}} {{.ShortName 20}}
<span class="org-visibility"> <span class="org-visibility">
{{if .Visibility.IsLimited}}<div class="ui orange tiny horizontal label">{{$.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}} {{if .Visibility.IsLimited}}<div class="ui orange tiny horizontal label">{{$.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}

@ -6,19 +6,19 @@
<div class="ui card"> <div class="ui card">
{{if eq .SignedUserName .Owner.Name}} {{if eq .SignedUserName .Owner.Name}}
<a class="image poping up" href="{{AppSubUrl}}/user/settings" id="profile-avatar" data-content="{{.i18n.Tr "user.change_avatar"}}" data-variation="inverted tiny" data-position="bottom center"> <a class="image poping up" href="{{AppSubUrl}}/user/settings" 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> </a>
{{else}} {{else}}
<span class="image"> <span class="image" id="profile-avatar">
<img src="{{.Owner.SizedRelAvatarLink 290}}" title="{{.Owner.Name}}" height="290" width="290"/> {{avatar .Owner 290}}
</span> </span>
{{end}} {{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}} {{if .Owner.FullName}}<span class="header text center">{{.Owner.FullName}}</span>{{end}}
<span class="username text center">{{.Owner.Name}}</span> <span class="username text center">{{.Owner.Name}}</span>
</div> </div>
<div class="extra content word-break"> <div class="extra content word-break">
<ul class="text black"> <ul>
{{if .Owner.Location}} {{if .Owner.Location}}
<li>{{svg "octicon-location"}} {{.Owner.Location}}</li> <li>{{svg "octicon-location"}} {{.Owner.Location}}</li>
{{end}} {{end}}
@ -54,7 +54,9 @@
{{range .Orgs}} {{range .Orgs}}
{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}} {{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}}
<li> <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> </li>
{{end}} {{end}}
{{end}} {{end}}

@ -14,17 +14,19 @@
<div class="ui selection owner dropdown"> <div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> <input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}"> <span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> {{avatar .ContextUser 28 "mini"}}
{{.ContextUser.ShortName 20}} {{.ContextUser.ShortName 20}}
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu"> <div class="menu">
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> <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> </div>
{{range .Orgs}} {{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> <div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{.ShortName 20}} {{avatar . 28 "mini"}}
{{.ShortName 20}}
</div> </div>
{{end}} {{end}}
</div> </div>

@ -22,7 +22,7 @@
<button type="submit" class="ui blue small button" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.members.leave"}}</button> <button type="submit" class="ui blue small button" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.members.leave"}}</button>
</form> </form>
</div> </div>
<img class="ui mini image" src="{{.RelAvatarLink}}"> {{avatar . 28 "mini"}}
<div class="content"> <div class="content">
<a href="{{.HomeLink}}">{{.Name}}</a> <a href="{{.HomeLink}}">{{.Name}}</a>
</div> </div>

@ -410,6 +410,19 @@ a.muted:hover,
border-color: var(--color-secondary); 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 { .dont-break-out {
overflow-wrap: break-word; overflow-wrap: break-word;
word-wrap: break-word; word-wrap: break-word;
@ -791,15 +804,6 @@ a.muted:hover,
font-weight: normal; font-weight: normal;
} }
.avatar.image,
.avatar.image img,
.avatar.image svg,
.avatar.images .image,
.avatar.images img,
.avatar.images svg {
border-radius: 3px;
}
.form { .form {
.fake { .fake {
display: none !important; display: none !important;
@ -1559,6 +1563,11 @@ a.ui.label:hover {
margin-bottom: .4em; margin-bottom: .4em;
} }
.ui.cards > .card > .extra,
.ui.card > .extra {
color: var(--color-text);
}
.color-icon { .color-icon {
margin-right: .5em; margin-right: .5em;
margin-left: .5em; margin-left: .5em;

@ -100,7 +100,7 @@
margin-right: auto; margin-right: auto;
} }
.ui.avatar { .left .ui.avatar {
margin-top: 13px; margin-top: 13px;
} }

@ -34,13 +34,15 @@
} }
&.profile { &.profile {
#org-avatar { .org-avatar {
width: 100px; width: 100px;
height: 100px; height: 100px;
margin-right: 15px; margin-right: 15px;
} }
#org-info { #org-info {
overflow-wrap: anywhere;
.ui.header { .ui.header {
font-size: 36px; font-size: 36px;
margin-bottom: 0; margin-bottom: 0;

@ -826,8 +826,8 @@
position: absolute; position: absolute;
left: -72px; left: -72px;
img { img {
width: 40px; width: 40px !important;
height: 40px; height: 40px !important;
} }
} }

@ -15,6 +15,10 @@
line-height: 1.3rem; line-height: 1.3rem;
} }
.profile-avatar-name {
border-top: none;
}
.extra.content { .extra.content {
padding: 0; padding: 0;
@ -46,9 +50,16 @@
} }
#profile-avatar { #profile-avatar {
background: none;
padding: 1rem 1rem .25rem;
img { img {
width: 100%; width: 100%;
height: auto;
object-fit: contain;
margin: 0;
} }
@media @mediaSm { @media @mediaSm {
height: 250px; height: 250px;
overflow: hidden; overflow: hidden;

@ -8,6 +8,7 @@
.fc { flex-direction: column !important; } .fc { flex-direction: column !important; }
.f1 { flex: 1 !important; } .f1 { flex: 1 !important; }
.fw { flex-wrap: wrap !important; } .fw { flex-wrap: wrap !important; }
.vm { vertical-align: middle !important; }
.mono { .mono {
font-family: var(--fonts-monospace) !important; font-family: var(--fonts-monospace) !important;

Loading…
Cancel
Save