|  | @@ -5,17 +5,21 @@
 | 
	
		
			
				|  |  |  package models
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  | +	"bytes"
 | 
	
		
			
				|  |  |  	"container/list"
 | 
	
		
			
				|  |  |  	"crypto/sha256"
 | 
	
		
			
				|  |  |  	"encoding/hex"
 | 
	
		
			
				|  |  |  	"errors"
 | 
	
		
			
				|  |  |  	"fmt"
 | 
	
		
			
				|  |  | +	"image"
 | 
	
		
			
				|  |  | +	"image/jpeg"
 | 
	
		
			
				|  |  |  	"os"
 | 
	
		
			
				|  |  |  	"path/filepath"
 | 
	
		
			
				|  |  |  	"strings"
 | 
	
		
			
				|  |  |  	"time"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	"github.com/Unknwon/com"
 | 
	
		
			
				|  |  | +	"github.com/nfnt/resize"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	"github.com/gogits/gogs/modules/base"
 | 
	
		
			
				|  |  |  	"github.com/gogits/gogs/modules/git"
 | 
	
	
		
			
				|  | @@ -45,33 +49,40 @@ var (
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // User represents the object of individual and member of organization.
 | 
	
		
			
				|  |  |  type User struct {
 | 
	
		
			
				|  |  | -	Id            int64
 | 
	
		
			
				|  |  | -	LowerName     string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | -	Name          string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | -	FullName      string
 | 
	
		
			
				|  |  | -	Email         string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | -	Passwd        string `xorm:"NOT NULL"`
 | 
	
		
			
				|  |  | -	LoginType     LoginType
 | 
	
		
			
				|  |  | -	LoginSource   int64 `xorm:"NOT NULL DEFAULT 0"`
 | 
	
		
			
				|  |  | -	LoginName     string
 | 
	
		
			
				|  |  | -	Type          UserType
 | 
	
		
			
				|  |  | -	Orgs          []*User       `xorm:"-"`
 | 
	
		
			
				|  |  | -	Repos         []*Repository `xorm:"-"`
 | 
	
		
			
				|  |  | +	Id          int64
 | 
	
		
			
				|  |  | +	LowerName   string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | +	Name        string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | +	FullName    string
 | 
	
		
			
				|  |  | +	Email       string `xorm:"UNIQUE NOT NULL"`
 | 
	
		
			
				|  |  | +	Passwd      string `xorm:"NOT NULL"`
 | 
	
		
			
				|  |  | +	LoginType   LoginType
 | 
	
		
			
				|  |  | +	LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
 | 
	
		
			
				|  |  | +	LoginName   string
 | 
	
		
			
				|  |  | +	Type        UserType
 | 
	
		
			
				|  |  | +	Orgs        []*User       `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
 | 
	
		
			
				|  |  |  	NumFollowings int
 | 
	
		
			
				|  |  |  	NumStars      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.
 | 
	
		
			
				|  |  |  	Description string
 | 
	
	
		
			
				|  | @@ -96,9 +107,12 @@ func (u *User) HomeLink() string {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // AvatarLink returns user gravatar link.
 | 
	
		
			
				|  |  |  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"
 | 
	
		
			
				|  |  | -	} else if setting.Service.EnableCacheAvatar {
 | 
	
		
			
				|  |  | +	case setting.Service.EnableCacheAvatar:
 | 
	
		
			
				|  |  |  		return setting.AppSubUrl + "/avatar/" + u.Avatar
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  	return setting.GravatarSource + u.Avatar
 | 
	
	
		
			
				|  | @@ -126,6 +140,43 @@ func (u *User) ValidtePassword(passwd string) bool {
 | 
	
		
			
				|  |  |  	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.
 | 
	
		
			
				|  |  |  func (u *User) IsOrganization() bool {
 | 
	
		
			
				|  |  |  	return u.Type == ORGANIZATION
 | 
	
	
		
			
				|  | @@ -517,41 +568,38 @@ func GetUserIdsByNames(names []string) []int64 {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // UserCommit represtns a commit with validation of user.
 | 
	
		
			
				|  |  |  type UserCommit struct {
 | 
	
		
			
				|  |  | -	UserName string
 | 
	
		
			
				|  |  | +	User *User
 | 
	
		
			
				|  |  |  	*git.Commit
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // 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)
 | 
	
		
			
				|  |  | -	if err == nil {
 | 
	
		
			
				|  |  | -		uname = u.Name
 | 
	
		
			
				|  |  | +	if err != nil {
 | 
	
		
			
				|  |  | +		return nil
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	return uname
 | 
	
		
			
				|  |  | +	return u
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
 | 
	
		
			
				|  |  |  func ValidateCommitsWithEmails(oldCommits *list.List) *list.List {
 | 
	
		
			
				|  |  | -	emails := map[string]string{}
 | 
	
		
			
				|  |  | +	emails := map[string]*User{}
 | 
	
		
			
				|  |  |  	newCommits := list.New()
 | 
	
		
			
				|  |  |  	e := oldCommits.Front()
 | 
	
		
			
				|  |  |  	for e != nil {
 | 
	
		
			
				|  |  |  		c := e.Value.(*git.Commit)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		uname := ""
 | 
	
		
			
				|  |  | +		var u *User
 | 
	
		
			
				|  |  |  		if v, ok := emails[c.Author.Email]; !ok {
 | 
	
		
			
				|  |  | -			u, err := GetUserByEmail(c.Author.Email)
 | 
	
		
			
				|  |  | -			if err == nil {
 | 
	
		
			
				|  |  | -				uname = u.Name
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			emails[c.Author.Email] = uname
 | 
	
		
			
				|  |  | +			u, _ = GetUserByEmail(c.Author.Email)
 | 
	
		
			
				|  |  | +			emails[c.Author.Email] = u
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  | -			uname = v
 | 
	
		
			
				|  |  | +			u = v
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		newCommits.PushBack(UserCommit{
 | 
	
		
			
				|  |  | -			UserName: uname,
 | 
	
		
			
				|  |  | -			Commit:   c,
 | 
	
		
			
				|  |  | +			User:   u,
 | 
	
		
			
				|  |  | +			Commit: c,
 | 
	
		
			
				|  |  |  		})
 | 
	
		
			
				|  |  |  		e = e.Next()
 | 
	
		
			
				|  |  |  	}
 |