mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	custom avatar upload
This commit is contained in:
		@@ -71,7 +71,6 @@ There are 5 ways to install Gogs:
 | 
				
			|||||||
- Router and middleware mechanism of [Macaron](https://github.com/Unknwon/macaron).
 | 
					- Router and middleware mechanism of [Macaron](https://github.com/Unknwon/macaron).
 | 
				
			||||||
- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
 | 
					- Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk).
 | 
				
			||||||
- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
 | 
					- System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog).
 | 
				
			||||||
- Usage and modification from [beego](http://beego.me) modules.
 | 
					 | 
				
			||||||
- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
 | 
					- Thanks [lavachen](http://www.lavachen.cn/) and [Rocker](http://weibo.com/rocker1989) for designing Logo.
 | 
				
			||||||
- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service.
 | 
					- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service.
 | 
				
			||||||
- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
 | 
					- Thanks [Crowdin](https://crowdin.com/project/gogs) for providing open source translation plan.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,8 +59,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## 特别鸣谢
 | 
					## 特别鸣谢
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
 | 
					- 基于 [Macaron](https://github.com/Unknwon/macaron) 的路由与中间件机制。
 | 
				
			||||||
- [beego](http://beego.me) 模块的使用与修改。
 | 
					 | 
				
			||||||
- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
 | 
					- 基于 [WeTalk](https://github.com/beego/wetalk) 修改的邮件服务和模块设计。
 | 
				
			||||||
- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
 | 
					- 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。
 | 
				
			||||||
- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。
 | 
					- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,6 +94,13 @@ func newMacaron() *macaron.Macaron {
 | 
				
			|||||||
			SkipLogging: !setting.DisableRouterLog,
 | 
								SkipLogging: !setting.DisableRouterLog,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
 | 
						m.Use(macaron.Static(
 | 
				
			||||||
 | 
							setting.AvatarUploadPath,
 | 
				
			||||||
 | 
							macaron.StaticOptions{
 | 
				
			||||||
 | 
								Prefix:      "avatars",
 | 
				
			||||||
 | 
								SkipLogging: !setting.DisableRouterLog,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						))
 | 
				
			||||||
	m.Use(macaron.Renderer(macaron.RenderOptions{
 | 
						m.Use(macaron.Renderer(macaron.RenderOptions{
 | 
				
			||||||
		Directory:  path.Join(setting.StaticRootPath, "templates"),
 | 
							Directory:  path.Join(setting.StaticRootPath, "templates"),
 | 
				
			||||||
		Funcs:      []template.FuncMap{base.TemplateFuncs},
 | 
							Funcs:      []template.FuncMap{base.TemplateFuncs},
 | 
				
			||||||
@@ -214,6 +221,7 @@ func runWeb(*cli.Context) {
 | 
				
			|||||||
	m.Group("/user/settings", func() {
 | 
						m.Group("/user/settings", func() {
 | 
				
			||||||
		m.Get("", user.Settings)
 | 
							m.Get("", user.Settings)
 | 
				
			||||||
		m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
 | 
							m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
 | 
				
			||||||
 | 
							m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), user.SettingsAvatar)
 | 
				
			||||||
		m.Get("/password", user.SettingsPassword)
 | 
							m.Get("/password", user.SettingsPassword)
 | 
				
			||||||
		m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
 | 
							m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
 | 
				
			||||||
		m.Get("/ssh", user.SettingsSSHKeys)
 | 
							m.Get("/ssh", user.SettingsSSHKeys)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -167,6 +167,7 @@ SESSION_LIFE_TIME = 86400
 | 
				
			|||||||
[picture]
 | 
					[picture]
 | 
				
			||||||
; The place to picture data, either "server" or "qiniu", default is "server"
 | 
					; The place to picture data, either "server" or "qiniu", default is "server"
 | 
				
			||||||
SERVICE = server
 | 
					SERVICE = server
 | 
				
			||||||
 | 
					AVATAR_UPLOAD_PATH = data/avatars
 | 
				
			||||||
; Chinese users can choose "duoshuo"
 | 
					; Chinese users can choose "duoshuo"
 | 
				
			||||||
GRAVATAR_SOURCE = gravatar
 | 
					GRAVATAR_SOURCE = gravatar
 | 
				
			||||||
DISABLE_GRAVATAR = false
 | 
					DISABLE_GRAVATAR = false
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -173,6 +173,7 @@ target_branch_not_exist = Target branch does not exist
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[user]
 | 
					[user]
 | 
				
			||||||
change_avatar = Change your avatar at gravatar.com
 | 
					change_avatar = Change your avatar at gravatar.com
 | 
				
			||||||
 | 
					change_custom_avatar = Change your avatar in settings
 | 
				
			||||||
join_on = Joined on
 | 
					join_on = Joined on
 | 
				
			||||||
repositories = Repositories
 | 
					repositories = Repositories
 | 
				
			||||||
activity = Public Activity
 | 
					activity = Public Activity
 | 
				
			||||||
@@ -201,6 +202,10 @@ change_username = Username Changed
 | 
				
			|||||||
change_username_desc = Username has been changed, do you want to continue? This will affect all links relate to your account.
 | 
					change_username_desc = Username has been changed, do you want to continue? This will affect all links relate to your account.
 | 
				
			||||||
continue = Continue
 | 
					continue = Continue
 | 
				
			||||||
cancel = Cancel
 | 
					cancel = Cancel
 | 
				
			||||||
 | 
					choose_new_avatar = Choose new avatar
 | 
				
			||||||
 | 
					upload_avatar = Upload Avatar
 | 
				
			||||||
 | 
					uploaded_avatar_not_a_image = Uploaded file is not a image
 | 
				
			||||||
 | 
					upload_avatar_success = Your new avatar has been uploaded successfully.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
change_password = Change Password
 | 
					change_password = Change Password
 | 
				
			||||||
old_password = Current Password
 | 
					old_password = Current Password
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							@@ -17,7 +17,7 @@ import (
 | 
				
			|||||||
	"github.com/gogits/gogs/modules/setting"
 | 
						"github.com/gogits/gogs/modules/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const APP_VER = "0.5.8.1119 Beta"
 | 
					const APP_VER = "0.5.8.1121 Beta"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	runtime.GOMAXPROCS(runtime.NumCPU())
 | 
						runtime.GOMAXPROCS(runtime.NumCPU())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,6 +58,7 @@ type Action struct {
 | 
				
			|||||||
	ActUserId    int64  // Action user id.
 | 
						ActUserId    int64  // Action user id.
 | 
				
			||||||
	ActUserName  string // Action user name.
 | 
						ActUserName  string // Action user name.
 | 
				
			||||||
	ActEmail     string
 | 
						ActEmail     string
 | 
				
			||||||
 | 
						ActAvatar    string `xorm:"-"`
 | 
				
			||||||
	RepoId       int64
 | 
						RepoId       int64
 | 
				
			||||||
	RepoUserName string
 | 
						RepoUserName string
 | 
				
			||||||
	RepoName     string
 | 
						RepoName     string
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										128
									
								
								models/user.go
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								models/user.go
									
									
									
									
									
								
							@@ -5,17 +5,21 @@
 | 
				
			|||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"container/list"
 | 
						"container/list"
 | 
				
			||||||
	"crypto/sha256"
 | 
						"crypto/sha256"
 | 
				
			||||||
	"encoding/hex"
 | 
						"encoding/hex"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"image"
 | 
				
			||||||
 | 
						"image/jpeg"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Unknwon/com"
 | 
						"github.com/Unknwon/com"
 | 
				
			||||||
 | 
						"github.com/nfnt/resize"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gogits/gogs/modules/base"
 | 
						"github.com/gogits/gogs/modules/base"
 | 
				
			||||||
	"github.com/gogits/gogs/modules/git"
 | 
						"github.com/gogits/gogs/modules/git"
 | 
				
			||||||
@@ -45,33 +49,40 @@ var (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// User represents the object of individual and member of organization.
 | 
					// User represents the object of individual and member of organization.
 | 
				
			||||||
type User struct {
 | 
					type User struct {
 | 
				
			||||||
	Id            int64
 | 
						Id          int64
 | 
				
			||||||
	LowerName     string `xorm:"UNIQUE NOT NULL"`
 | 
						LowerName   string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
	Name          string `xorm:"UNIQUE NOT NULL"`
 | 
						Name        string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
	FullName      string
 | 
						FullName    string
 | 
				
			||||||
	Email         string `xorm:"UNIQUE NOT NULL"`
 | 
						Email       string `xorm:"UNIQUE NOT NULL"`
 | 
				
			||||||
	Passwd        string `xorm:"NOT NULL"`
 | 
						Passwd      string `xorm:"NOT NULL"`
 | 
				
			||||||
	LoginType     LoginType
 | 
						LoginType   LoginType
 | 
				
			||||||
	LoginSource   int64 `xorm:"NOT NULL DEFAULT 0"`
 | 
						LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
 | 
				
			||||||
	LoginName     string
 | 
						LoginName   string
 | 
				
			||||||
	Type          UserType
 | 
						Type        UserType
 | 
				
			||||||
	Orgs          []*User       `xorm:"-"`
 | 
						Orgs        []*User       `xorm:"-"`
 | 
				
			||||||
	Repos         []*Repository `xorm:"-"`
 | 
						Repos       []*Repository `xorm:"-"`
 | 
				
			||||||
 | 
						Location    string
 | 
				
			||||||
 | 
						Website     string
 | 
				
			||||||
 | 
						Rands       string    `xorm:"VARCHAR(10)"`
 | 
				
			||||||
 | 
						Salt        string    `xorm:"VARCHAR(10)"`
 | 
				
			||||||
 | 
						Created     time.Time `xorm:"CREATED"`
 | 
				
			||||||
 | 
						Updated     time.Time `xorm:"UPDATED"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Permissions.
 | 
				
			||||||
 | 
						IsActive     bool
 | 
				
			||||||
 | 
						IsAdmin      bool
 | 
				
			||||||
 | 
						AllowGitHook bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Avatar.
 | 
				
			||||||
 | 
						Avatar          string `xorm:"VARCHAR(2048) NOT NULL"`
 | 
				
			||||||
 | 
						AvatarEmail     string `xorm:"NOT NULL"`
 | 
				
			||||||
 | 
						UseCustomAvatar bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Counters.
 | 
				
			||||||
	NumFollowers  int
 | 
						NumFollowers  int
 | 
				
			||||||
	NumFollowings int
 | 
						NumFollowings int
 | 
				
			||||||
	NumStars      int
 | 
						NumStars      int
 | 
				
			||||||
	NumRepos      int
 | 
						NumRepos      int
 | 
				
			||||||
	Avatar        string `xorm:"VARCHAR(2048) NOT NULL"`
 | 
					 | 
				
			||||||
	AvatarEmail   string `xorm:"NOT NULL"`
 | 
					 | 
				
			||||||
	Location      string
 | 
					 | 
				
			||||||
	Website       string
 | 
					 | 
				
			||||||
	IsActive      bool
 | 
					 | 
				
			||||||
	IsAdmin       bool
 | 
					 | 
				
			||||||
	AllowGitHook  bool
 | 
					 | 
				
			||||||
	Rands         string    `xorm:"VARCHAR(10)"`
 | 
					 | 
				
			||||||
	Salt          string    `xorm:"VARCHAR(10)"`
 | 
					 | 
				
			||||||
	Created       time.Time `xorm:"CREATED"`
 | 
					 | 
				
			||||||
	Updated       time.Time `xorm:"UPDATED"`
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// For organization.
 | 
						// For organization.
 | 
				
			||||||
	Description string
 | 
						Description string
 | 
				
			||||||
@@ -96,9 +107,12 @@ func (u *User) HomeLink() string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// AvatarLink returns user gravatar link.
 | 
					// AvatarLink returns user gravatar link.
 | 
				
			||||||
func (u *User) AvatarLink() string {
 | 
					func (u *User) AvatarLink() string {
 | 
				
			||||||
	if setting.DisableGravatar {
 | 
						switch {
 | 
				
			||||||
 | 
						case u.UseCustomAvatar:
 | 
				
			||||||
 | 
							return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id)
 | 
				
			||||||
 | 
						case setting.DisableGravatar:
 | 
				
			||||||
		return setting.AppSubUrl + "/img/avatar_default.jpg"
 | 
							return setting.AppSubUrl + "/img/avatar_default.jpg"
 | 
				
			||||||
	} else if setting.Service.EnableCacheAvatar {
 | 
						case setting.Service.EnableCacheAvatar:
 | 
				
			||||||
		return setting.AppSubUrl + "/avatar/" + u.Avatar
 | 
							return setting.AppSubUrl + "/avatar/" + u.Avatar
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return setting.GravatarSource + u.Avatar
 | 
						return setting.GravatarSource + u.Avatar
 | 
				
			||||||
@@ -126,6 +140,43 @@ func (u *User) ValidtePassword(passwd string) bool {
 | 
				
			|||||||
	return u.Passwd == newUser.Passwd
 | 
						return u.Passwd == newUser.Passwd
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UploadAvatar saves custom avatar for user.
 | 
				
			||||||
 | 
					// FIXME: splite uploads to different subdirs in case we have massive users.
 | 
				
			||||||
 | 
					func (u *User) UploadAvatar(data []byte) error {
 | 
				
			||||||
 | 
						savePath := filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
 | 
				
			||||||
 | 
						u.UseCustomAvatar = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						img, _, err := image.Decode(bytes.NewReader(data))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m := resize.Resize(200, 200, img, resize.NearestNeighbor)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sess := x.NewSession()
 | 
				
			||||||
 | 
						defer sess.Close()
 | 
				
			||||||
 | 
						if err = sess.Begin(); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if _, err = sess.Id(u.Id).AllCols().Update(u); err != nil {
 | 
				
			||||||
 | 
							sess.Rollback()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fw, err := os.Create(savePath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							sess.Rollback()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer fw.Close()
 | 
				
			||||||
 | 
						if err = jpeg.Encode(fw, m, nil); err != nil {
 | 
				
			||||||
 | 
							sess.Rollback()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsOrganization returns true if user is actually a organization.
 | 
					// IsOrganization returns true if user is actually a organization.
 | 
				
			||||||
func (u *User) IsOrganization() bool {
 | 
					func (u *User) IsOrganization() bool {
 | 
				
			||||||
	return u.Type == ORGANIZATION
 | 
						return u.Type == ORGANIZATION
 | 
				
			||||||
@@ -517,41 +568,38 @@ func GetUserIdsByNames(names []string) []int64 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// UserCommit represtns a commit with validation of user.
 | 
					// UserCommit represtns a commit with validation of user.
 | 
				
			||||||
type UserCommit struct {
 | 
					type UserCommit struct {
 | 
				
			||||||
	UserName string
 | 
						User *User
 | 
				
			||||||
	*git.Commit
 | 
						*git.Commit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
 | 
					// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
 | 
				
			||||||
func ValidateCommitWithEmail(c *git.Commit) (uname string) {
 | 
					func ValidateCommitWithEmail(c *git.Commit) *User {
 | 
				
			||||||
	u, err := GetUserByEmail(c.Author.Email)
 | 
						u, err := GetUserByEmail(c.Author.Email)
 | 
				
			||||||
	if err == nil {
 | 
						if err != nil {
 | 
				
			||||||
		uname = u.Name
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return uname
 | 
						return u
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
 | 
					// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
 | 
				
			||||||
func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
 | 
					func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
 | 
				
			||||||
	emails := map[string]string{}
 | 
						emails := map[string]*User{}
 | 
				
			||||||
	newCommits := list.New()
 | 
						newCommits := list.New()
 | 
				
			||||||
	e := oldCommits.Front()
 | 
						e := oldCommits.Front()
 | 
				
			||||||
	for e != nil {
 | 
						for e != nil {
 | 
				
			||||||
		c := e.Value.(*git.Commit)
 | 
							c := e.Value.(*git.Commit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		uname := ""
 | 
							var u *User
 | 
				
			||||||
		if v, ok := emails[c.Author.Email]; !ok {
 | 
							if v, ok := emails[c.Author.Email]; !ok {
 | 
				
			||||||
			u, err := GetUserByEmail(c.Author.Email)
 | 
								u, _ = GetUserByEmail(c.Author.Email)
 | 
				
			||||||
			if err == nil {
 | 
								emails[c.Author.Email] = u
 | 
				
			||||||
				uname = u.Name
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			emails[c.Author.Email] = uname
 | 
					 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			uname = v
 | 
								u = v
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		newCommits.PushBack(UserCommit{
 | 
							newCommits.PushBack(UserCommit{
 | 
				
			||||||
			UserName: uname,
 | 
								User:   u,
 | 
				
			||||||
			Commit:   c,
 | 
								Commit: c,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		e = e.Next()
 | 
							e = e.Next()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,8 @@
 | 
				
			|||||||
package auth
 | 
					package auth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"mime/multipart"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Unknwon/macaron"
 | 
						"github.com/Unknwon/macaron"
 | 
				
			||||||
	"github.com/macaron-contrib/binding"
 | 
						"github.com/macaron-contrib/binding"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -86,6 +88,14 @@ func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors)
 | 
				
			|||||||
	return validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type UploadAvatarForm struct {
 | 
				
			||||||
 | 
						Avatar *multipart.FileHeader `form:"avatar" binding:"Required"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 | 
				
			||||||
 | 
						return validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ChangePasswordForm struct {
 | 
					type ChangePasswordForm struct {
 | 
				
			||||||
	OldPassword string `form:"old_password" binding:"Required;MinSize(6);MaxSize(255)"`
 | 
						OldPassword string `form:"old_password" binding:"Required;MinSize(6);MaxSize(255)"`
 | 
				
			||||||
	Password    string `form:"password" binding:"Required;MinSize(6);MaxSize(255)"`
 | 
						Password    string `form:"password" binding:"Required;MinSize(6);MaxSize(255)"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -121,7 +121,7 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) {
 | 
				
			|||||||
	if img, err = decodeImageFile(imgPath); err != nil {
 | 
						if img, err = decodeImageFile(imgPath); err != nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m := resize.Resize(uint(size), 0, img, resize.Lanczos3)
 | 
						m := resize.Resize(uint(size), 0, img, resize.NearestNeighbor)
 | 
				
			||||||
	return jpeg.Encode(wr, m, nil)
 | 
						return jpeg.Encode(wr, m, nil)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,9 +66,10 @@ var (
 | 
				
			|||||||
	ScriptType   string
 | 
						ScriptType   string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Picture settings.
 | 
						// Picture settings.
 | 
				
			||||||
	PictureService  string
 | 
						PictureService   string
 | 
				
			||||||
	GravatarSource  string
 | 
						AvatarUploadPath string
 | 
				
			||||||
	DisableGravatar bool
 | 
						GravatarSource   string
 | 
				
			||||||
 | 
						DisableGravatar  bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Log settings.
 | 
						// Log settings.
 | 
				
			||||||
	LogRootPath string
 | 
						LogRootPath string
 | 
				
			||||||
@@ -259,6 +260,9 @@ func NewConfigContext() {
 | 
				
			|||||||
	ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
 | 
						ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", []string{"server"})
 | 
						PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", []string{"server"})
 | 
				
			||||||
 | 
						AvatarUploadPath = Cfg.MustValue("picture", "AVATAR_UPLOAD_PATH", "data/avatars")
 | 
				
			||||||
 | 
						os.MkdirAll(AvatarUploadPath, os.ModePerm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch Cfg.MustValue("picture", "GRAVATAR_SOURCE", "gravatar") {
 | 
						switch Cfg.MustValue("picture", "GRAVATAR_SOURCE", "gravatar") {
 | 
				
			||||||
	case "duoshuo":
 | 
						case "duoshuo":
 | 
				
			||||||
		GravatarSource = "http://gravatar.duoshuo.com/avatar/"
 | 
							GravatarSource = "http://gravatar.duoshuo.com/avatar/"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -100,6 +100,13 @@ func Dashboard(ctx *middleware.Context) {
 | 
				
			|||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							// FIXME: cache results?
 | 
				
			||||||
 | 
							u, err := models.GetUserByName(act.ActUserName)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Handle(500, "GetUserByName", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							act.ActAvatar = u.AvatarLink()
 | 
				
			||||||
		feeds = append(feeds, act)
 | 
							feeds = append(feeds, act)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["Feeds"] = feeds
 | 
						ctx.Data["Feeds"] = feeds
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
package user
 | 
					package user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/Unknwon/com"
 | 
						"github.com/Unknwon/com"
 | 
				
			||||||
@@ -83,6 +84,34 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
 | 
				
			|||||||
	ctx.Redirect(setting.AppSubUrl + "/user/settings")
 | 
						ctx.Redirect(setting.AppSubUrl + "/user/settings")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FIXME: limit size.
 | 
				
			||||||
 | 
					func SettingsAvatar(ctx *middleware.Context, form auth.UploadAvatarForm) {
 | 
				
			||||||
 | 
						defer ctx.Redirect(setting.AppSubUrl + "/user/settings")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if form.Avatar != nil {
 | 
				
			||||||
 | 
							fr, err := form.Avatar.Open()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Flash.Error(err.Error())
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							data, err := ioutil.ReadAll(fr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								ctx.Flash.Error(err.Error())
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, ok := base.IsImageFile(data); !ok {
 | 
				
			||||||
 | 
								ctx.Flash.Error(ctx.Tr("settings.uploaded_avatar_not_a_image"))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err = ctx.User.UploadAvatar(data); err != nil {
 | 
				
			||||||
 | 
								ctx.Flash.Error(err.Error())
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							ctx.Flash.Success(ctx.Tr("settings.upload_avatar_success"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func SettingsPassword(ctx *middleware.Context) {
 | 
					func SettingsPassword(ctx *middleware.Context) {
 | 
				
			||||||
	ctx.Data["Title"] = ctx.Tr("settings")
 | 
						ctx.Data["Title"] = ctx.Tr("settings")
 | 
				
			||||||
	ctx.Data["PageIsUserSettings"] = true
 | 
						ctx.Data["PageIsUserSettings"] = true
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
0.5.8.1119 Beta
 | 
					0.5.8.1121 Beta
 | 
				
			||||||
@@ -24,7 +24,13 @@
 | 
				
			|||||||
            {{$r := List .Commits}}
 | 
					            {{$r := List .Commits}}
 | 
				
			||||||
            {{range $r}}
 | 
					            {{range $r}}
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
                <td class="author"><img class="avatar-20" src="{{AvatarLink .Author.Email}}" alt=""/>   {{if .UserName}}<a href="{{AppSubUrl}}/{{.UserName}}">{{.Author.Name}}</a>{{else}}{{.Author.Name}}{{end}}</td>
 | 
					                <td class="author">
 | 
				
			||||||
 | 
					                    {{if .User}}
 | 
				
			||||||
 | 
					                    <img class="avatar-20" src="{{.User.AvatarLink}}" alt=""/>   <a href="{{AppSubUrl}}/{{.User.Name}}">{{.Author.Name}}</a>
 | 
				
			||||||
 | 
					                    {{else}}
 | 
				
			||||||
 | 
					                    <img class="avatar-20" src="{{AvatarLink .Author.Email}}" alt=""/>   {{.Author.Name}}
 | 
				
			||||||
 | 
					                    {{end}}
 | 
				
			||||||
 | 
					                </td>
 | 
				
			||||||
                <td class="sha"><a rel="nofollow" class="label label-green" href="{{AppSubUrl}}/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td>
 | 
					                <td class="sha"><a rel="nofollow" class="label label-green" href="{{AppSubUrl}}/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td>
 | 
				
			||||||
                <td class="message"><span class="text-truncate">{{.Summary}}</span></td>
 | 
					                <td class="message"><span class="text-truncate">{{.Summary}}</span></td>
 | 
				
			||||||
                <td class="date">{{TimeSince .Author.When $.Lang}}</td>
 | 
					                <td class="date">{{TimeSince .Author.When $.Lang}}</td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,10 +30,11 @@
 | 
				
			|||||||
                    </ul>
 | 
					                    </ul>
 | 
				
			||||||
                </span>
 | 
					                </span>
 | 
				
			||||||
                <p class="author">
 | 
					                <p class="author">
 | 
				
			||||||
                    <img class="avatar-30" src="{{AvatarLink .Commit.Author.Email}}" />
 | 
					 | 
				
			||||||
                    {{if .Author}}
 | 
					                    {{if .Author}}
 | 
				
			||||||
                    <a href="{{AppSubUrl}}/{{.Author}}"><strong>{{.Commit.Author.Name}}</strong></a>
 | 
					                    <img class="avatar-30" src="{{.Author.AvatarLink}}" />
 | 
				
			||||||
 | 
					                    <a href="{{AppSubUrl}}/{{.Author.Name}}"><strong>{{.Commit.Author.Name}}</strong></a>
 | 
				
			||||||
                    {{else}}
 | 
					                    {{else}}
 | 
				
			||||||
 | 
					                    <img class="avatar-30" src="{{AvatarLink .Commit.Author.Email}}" />
 | 
				
			||||||
                    <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> 
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,14 @@
 | 
				
			|||||||
    <tr>
 | 
					    <tr>
 | 
				
			||||||
        <th colspan="4" class="clear">
 | 
					        <th colspan="4" class="clear">
 | 
				
			||||||
            <span class="author left">
 | 
					            <span class="author left">
 | 
				
			||||||
 | 
					                {{if .LastCommitUser}}
 | 
				
			||||||
 | 
					                <img class="avatar-24 radius" src="{{.LastCommitUser.AvatarLink}}" />
 | 
				
			||||||
 | 
					                <a href="{{AppSubUrl}}/{{.LastCommitUser.Name}}"><strong>{{.LastCommit.Author.Name}}</strong></a>:
 | 
				
			||||||
 | 
					                {{else}}
 | 
				
			||||||
                <img class="avatar-24 radius" src="{{AvatarLink .LastCommit.Author.Email}}" />
 | 
					                <img class="avatar-24 radius" src="{{AvatarLink .LastCommit.Author.Email}}" />
 | 
				
			||||||
                {{if .LastCommitUser}}<a href="{{AppSubUrl}}/{{.LastCommitUser}}">{{end}}<strong>{{.LastCommit.Author.Name}}</strong>:{{if .LastCommitUser}}</a>{{end}}
 | 
					                <strong>{{.LastCommit.Author.Name}}</strong>:
 | 
				
			||||||
 | 
					                {{end}}
 | 
				
			||||||
 | 
					                 
 | 
				
			||||||
            </span>
 | 
					            </span>
 | 
				
			||||||
            <span class="last-commit"><a href="{{.RepoLink}}/commit/{{.LastCommit.Id}}" rel="nofollow">
 | 
					            <span class="last-commit"><a href="{{.RepoLink}}/commit/{{.LastCommit.Id}}" rel="nofollow">
 | 
				
			||||||
                <strong>{{ShortSha .LastCommit.Id.String}}</strong></a>
 | 
					                <strong>{{ShortSha .LastCommit.Id.String}}</strong></a>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
{{range .Feeds}}
 | 
					{{range .Feeds}}
 | 
				
			||||||
<div class="news clear">
 | 
					<div class="news clear">
 | 
				
			||||||
    <div class="avatar left">
 | 
					    <div class="avatar left">
 | 
				
			||||||
        <img class="avatar-30" src="{{AvatarLink .GetActEmail}}" alt="">
 | 
					        <img class="avatar-30" src="{{.ActAvatar}}" alt="">
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="content left {{if eq .GetOpType 5}}push-news{{end}} grid-4-5">
 | 
					    <div class="content left {{if eq .GetOpType 5}}push-news{{end}} grid-4-5">
 | 
				
			||||||
        <p class="text-bold">
 | 
					        <p class="text-bold">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,11 @@
 | 
				
			|||||||
    <div id="user-profile-page" class="container clear">
 | 
					    <div id="user-profile-page" class="container clear">
 | 
				
			||||||
        <div class="grid-1-5 left">
 | 
					        <div class="grid-1-5 left">
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
 | 
					                {{if .Owner.UseCustomAvatar}}
 | 
				
			||||||
 | 
					                <a href="{{AppSubUrl}}/user/settings" id="profile-avatar" original-title="{{.i18n.Tr "user.change_custom_avatar"}}">
 | 
				
			||||||
 | 
					                {{else}}
 | 
				
			||||||
                <a href="http://gravatar.com/emails/" id="profile-avatar" original-title="{{.i18n.Tr "user.change_avatar"}}">
 | 
					                <a href="http://gravatar.com/emails/" id="profile-avatar" original-title="{{.i18n.Tr "user.change_avatar"}}">
 | 
				
			||||||
 | 
					                {{end}}
 | 
				
			||||||
                    <img class="profile-avatar" src="{{.Owner.AvatarLink}}?s=200"title="{{.Owner.Name}}"/>
 | 
					                    <img class="profile-avatar" src="{{.Owner.AvatarLink}}?s=200"title="{{.Owner.Name}}"/>
 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
                <div class="text-center" id="profile-name">
 | 
					                <div class="text-center" id="profile-name">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,49 +11,63 @@
 | 
				
			|||||||
                        <div class="panel-header">
 | 
					                        <div class="panel-header">
 | 
				
			||||||
                            <strong>{{.i18n.Tr "settings.public_profile"}}</strong>
 | 
					                            <strong>{{.i18n.Tr "settings.public_profile"}}</strong>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                        <form class="form form-align panel-body" id="user-profile-form" action="{{AppSubUrl}}/user/settings" method="post">
 | 
					                        <div class="panel-body">
 | 
				
			||||||
                            {{.CsrfTokenHtml}}
 | 
					                            <form class="form form-align" id="user-profile-form" action="{{AppSubUrl}}/user/settings" method="post">
 | 
				
			||||||
                        	<div class="text-center panel-desc">{{.i18n.Tr "settings.profile_desc"}}</div>
 | 
					                                {{.CsrfTokenHtml}}
 | 
				
			||||||
                            <div class="field">
 | 
					                                <div class="text-center panel-desc">{{.i18n.Tr "settings.profile_desc"}}</div>
 | 
				
			||||||
                                <label>{{.i18n.Tr "settings.uid"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <label class="text-left">{{.SignedUser.Id}}</label>
 | 
					                                    <label>{{.i18n.Tr "settings.uid"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <label class="text-left">{{.SignedUser.Id}}</label>
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label class="req" for="username">{{.i18n.Tr "username"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="username" name="uname" type="text" value="{{.SignedUser.Name}}" data-uname="{{.SignedUser.Name}}" required />
 | 
					                                    <label class="req" for="username">{{.i18n.Tr "username"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="username" name="uname" type="text" value="{{.SignedUser.Name}}" data-uname="{{.SignedUser.Name}}" required />
 | 
				
			||||||
                            <div class="white-popup-block mfp-hide" id="change-username-modal">
 | 
					                                </div>
 | 
				
			||||||
                                <h1 class="text-red">{{.i18n.Tr "settings.change_username"}}</h1>
 | 
					                                <div class="white-popup-block mfp-hide" id="change-username-modal">
 | 
				
			||||||
                                <p>{{.i18n.Tr "settings.change_username_desc"}}</p>
 | 
					                                    <h1 class="text-red">{{.i18n.Tr "settings.change_username"}}</h1>
 | 
				
			||||||
                                <br>
 | 
					                                    <p>{{.i18n.Tr "settings.change_username_desc"}}</p>
 | 
				
			||||||
                                <button class="btn btn-red btn-large btn-radius" id="change-username-submit">{{.i18n.Tr "settings.continue"}}</button>
 | 
					                                    <br>
 | 
				
			||||||
                                <button class="btn btn-large btn-radius popup-modal-dismiss">{{.i18n.Tr "settings.cancel"}}</button>
 | 
					                                    <button class="btn btn-red btn-large btn-radius" id="change-username-submit">{{.i18n.Tr "settings.continue"}}</button>
 | 
				
			||||||
                            </div>
 | 
					                                    <button class="btn btn-large btn-radius popup-modal-dismiss">{{.i18n.Tr "settings.cancel"}}</button>
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label for="full-name">{{.i18n.Tr "settings.full_name"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <input class="ipt ipt-large ipt-radius {{if .Err_FullName}}ipt-error{{end}}" id="full-name" name="fullname" type="text" value="{{.SignedUser.FullName}}" />
 | 
					                                    <label for="full-name">{{.i18n.Tr "settings.full_name"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <input class="ipt ipt-large ipt-radius {{if .Err_FullName}}ipt-error{{end}}" id="full-name" name="fullname" type="text" value="{{.SignedUser.FullName}}" />
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label class="req" for="email">{{.i18n.Tr "email"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <input class="ipt ipt-large ipt-radius {{if .Err_Email}}ipt-error{{end}}" id="email" name="email" type="email" value="{{.SignedUser.Email}}" required />
 | 
					                                    <label class="req" for="email">{{.i18n.Tr "email"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <input class="ipt ipt-large ipt-radius {{if .Err_Email}}ipt-error{{end}}" id="email" name="email" type="email" value="{{.SignedUser.Email}}" required />
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label for="website">{{.i18n.Tr "settings.website"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <input class="ipt ipt-large ipt-radius {{if .Err_Website}}ipt-error{{end}}" id="website" name="website" type="url" value="{{.SignedUser.Website}}" />
 | 
					                                    <label for="website">{{.i18n.Tr "settings.website"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <input class="ipt ipt-large ipt-radius {{if .Err_Website}}ipt-error{{end}}" id="website" name="website" type="url" value="{{.SignedUser.Website}}" />
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label for="location">{{.i18n.Tr "settings.location"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <input class="ipt ipt-large ipt-radius {{if .Err_Location}}ipt-error{{end}}" id="location" name="location" type="text" value="{{.SignedUser.Location}}" />
 | 
					                                    <label for="location">{{.i18n.Tr "settings.location"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <input class="ipt ipt-large ipt-radius {{if .Err_Location}}ipt-error{{end}}" id="location" name="location" type="text" value="{{.SignedUser.Location}}" />
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label for="gravatar-email">Gravatar {{.i18n.Tr "email"}}</label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <input class="ipt ipt-large ipt-radius {{if .Err_Avatar}}ipt-error{{end}}" id="gravatar-email" name="avatar" type="text" value="{{.SignedUser.AvatarEmail}}" />
 | 
					                                    <label for="gravatar-email">Gravatar {{.i18n.Tr "email"}}</label>
 | 
				
			||||||
                            </div>
 | 
					                                    <input class="ipt ipt-large ipt-radius {{if .Err_Avatar}}ipt-error{{end}}" id="gravatar-email" name="avatar" type="text" value="{{.SignedUser.AvatarEmail}}" />
 | 
				
			||||||
                            <div class="field">
 | 
					                                </div>
 | 
				
			||||||
                                <label></label>
 | 
					                                <div class="field">
 | 
				
			||||||
                                <button class="btn btn-green btn-large btn-radius" id="change-username-btn" href="#change-username-modal">{{.i18n.Tr "settings.update_profile"}}</button>
 | 
					                                    <label></label>
 | 
				
			||||||
                            </div>
 | 
					                                    <button class="btn btn-green btn-large btn-radius" id="change-username-btn" href="#change-username-modal">{{.i18n.Tr "settings.update_profile"}}</button>
 | 
				
			||||||
                        </form>
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </form>
 | 
				
			||||||
 | 
					                            <hr>
 | 
				
			||||||
 | 
					                            <form class="form form-align" id="user-profile-form" action="{{AppSubUrl}}/user/settings/avatar" method="post" enctype="multipart/form-data">
 | 
				
			||||||
 | 
					                                {{.CsrfTokenHtml}}
 | 
				
			||||||
 | 
					                                <div class="field">
 | 
				
			||||||
 | 
					                                    <label>{{.i18n.Tr "settings.choose_new_avatar"}}</label>
 | 
				
			||||||
 | 
					                                    <input name="avatar" type="file" required />
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div class="field">
 | 
				
			||||||
 | 
					                                    <label></label>
 | 
				
			||||||
 | 
					                                    <button class="btn btn-green btn-large btn-radius">{{.i18n.Tr "settings.upload_avatar"}}</button>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                            </form>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user