2014-04-10 22:20:58 +04:00
// Copyright 2014 The Gogs 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 (
2014-11-21 18:58:08 +03:00
"bytes"
2014-09-23 23:30:04 +04:00
"container/list"
2014-04-10 22:20:58 +04:00
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
2014-11-21 18:58:08 +03:00
"image"
2015-08-09 06:46:10 +03:00
_ "image/jpeg"
2015-11-14 00:43:43 +03:00
"image/png"
2014-04-10 22:20:58 +04:00
"os"
"path/filepath"
"strings"
"time"
2016-07-23 13:58:18 +03:00
"unicode/utf8"
2014-04-10 22:20:58 +04:00
2014-07-26 08:24:27 +04:00
"github.com/Unknwon/com"
2015-09-01 19:19:52 +03:00
"github.com/go-xorm/xorm"
2014-11-21 18:58:08 +03:00
"github.com/nfnt/resize"
2014-04-10 22:20:58 +04:00
2015-12-16 01:25:45 +03:00
"github.com/gogits/git-module"
2015-11-27 08:24:24 +03:00
2014-12-04 02:19:35 +03:00
"github.com/gogits/gogs/modules/avatar"
2014-04-10 22:20:58 +04:00
"github.com/gogits/gogs/modules/base"
"github.com/gogits/gogs/modules/log"
2016-02-21 01:10:05 +03:00
"github.com/gogits/gogs/modules/markdown"
2014-05-26 04:11:25 +04:00
"github.com/gogits/gogs/modules/setting"
2014-04-10 22:20:58 +04:00
)
2014-06-25 08:44:48 +04:00
type UserType int
2014-04-10 22:20:58 +04:00
const (
2016-03-11 23:33:12 +03:00
USER_TYPE_INDIVIDUAL UserType = iota // Historic reason to make it starts at 0.
USER_TYPE_ORGANIZATION
2014-04-10 22:20:58 +04:00
)
var (
2014-05-22 05:37:13 +04:00
ErrUserNotKeyOwner = errors . New ( "User does not the owner of public key" )
2014-12-17 18:40:10 +03:00
ErrEmailNotExist = errors . New ( "E-mail does not exist" )
ErrEmailNotActivated = errors . New ( "E-mail address has not been activated" )
2014-05-11 10:12:45 +04:00
ErrUserNameIllegal = errors . New ( "User name contains illegal characters" )
ErrLoginSourceNotExist = errors . New ( "Login source does not exist" )
ErrLoginSourceNotActived = errors . New ( "Login source is not actived" )
2014-05-15 22:46:04 +04:00
ErrUnsupportedLoginType = errors . New ( "Login source is unknown" )
2014-04-10 22:20:58 +04:00
)
// User represents the object of individual and member of organization.
type User struct {
2016-07-23 20:08:22 +03:00
ID int64 ` xorm:"pk autoincr" `
2014-12-17 11:26:19 +03:00
LowerName string ` xorm:"UNIQUE NOT NULL" `
Name string ` xorm:"UNIQUE NOT NULL" `
FullName string
2015-12-21 15:24:11 +03:00
// Email is the primary email address (to be used for communication)
2015-09-06 17:08:14 +03:00
Email string ` xorm:"NOT NULL" `
2014-11-21 18:58:08 +03:00
Passwd string ` xorm:"NOT NULL" `
LoginType LoginType
LoginSource int64 ` xorm:"NOT NULL DEFAULT 0" `
LoginName string
2015-09-06 17:08:14 +03:00
Type UserType
2015-09-07 20:58:23 +03:00
OwnedOrgs [ ] * User ` xorm:"-" `
2014-11-21 18:58:08 +03:00
Orgs [ ] * User ` xorm:"-" `
Repos [ ] * Repository ` xorm:"-" `
Location string
Website string
2016-03-10 03:53:30 +03:00
Rands string ` xorm:"VARCHAR(10)" `
Salt string ` xorm:"VARCHAR(10)" `
Created time . Time ` xorm:"-" `
CreatedUnix int64
Updated time . Time ` xorm:"-" `
UpdatedUnix int64
2014-11-21 18:58:08 +03:00
2015-10-25 11:26:26 +03:00
// Remember visibility choice for convenience, true for private
2015-08-28 11:44:04 +03:00
LastRepoVisibility bool
2015-12-11 23:11:13 +03:00
// Maximum repository creation limit, -1 means use gloabl default
2015-12-10 20:48:45 +03:00
MaxRepoCreation int ` xorm:"NOT NULL DEFAULT -1" `
2015-08-28 11:44:04 +03:00
2015-12-21 15:24:11 +03:00
// Permissions
2016-07-16 05:08:04 +03:00
IsActive bool // Activate primary email
2015-11-04 02:40:52 +03:00
IsAdmin bool
AllowGitHook bool
AllowImportLocal bool // Allow migrate repository by local path
2016-07-16 05:22:16 +03:00
ProhibitLogin bool
2014-11-21 18:58:08 +03:00
2015-12-21 15:24:11 +03:00
// Avatar
2014-11-21 18:58:08 +03:00
Avatar string ` xorm:"VARCHAR(2048) NOT NULL" `
AvatarEmail string ` xorm:"NOT NULL" `
UseCustomAvatar bool
2015-12-21 15:24:11 +03:00
// Counters
NumFollowers int
2015-12-22 03:09:28 +03:00
NumFollowing int ` xorm:"NOT NULL DEFAULT 0" `
2015-12-21 15:24:11 +03:00
NumStars int
NumRepos int
2014-06-25 08:44:48 +04:00
2015-12-21 15:24:11 +03:00
// For organization
2014-06-27 11:37:01 +04:00
Description string
NumTeams int
NumMembers int
2014-06-28 23:43:25 +04:00
Teams [ ] * Team ` xorm:"-" `
Members [ ] * User ` xorm:"-" `
2014-04-10 22:20:58 +04:00
}
2016-03-10 03:53:30 +03:00
func ( u * User ) BeforeInsert ( ) {
2016-07-23 15:24:44 +03:00
u . CreatedUnix = time . Now ( ) . Unix ( )
2016-03-10 03:53:30 +03:00
u . UpdatedUnix = u . CreatedUnix
}
2015-12-10 20:37:53 +03:00
func ( u * User ) BeforeUpdate ( ) {
2015-12-10 20:46:05 +03:00
if u . MaxRepoCreation < - 1 {
u . MaxRepoCreation = - 1
2015-12-10 20:37:53 +03:00
}
2016-07-23 15:24:44 +03:00
u . UpdatedUnix = time . Now ( ) . Unix ( )
2015-12-10 20:37:53 +03:00
}
2015-09-01 19:19:52 +03:00
func ( u * User ) AfterSet ( colName string , _ xorm . Cell ) {
switch colName {
case "full_name" :
2016-02-21 01:10:05 +03:00
u . FullName = markdown . Sanitizer . Sanitize ( u . FullName )
2016-03-10 03:53:30 +03:00
case "created_unix" :
u . Created = time . Unix ( u . CreatedUnix , 0 ) . Local ( )
case "updated_unix" :
u . Updated = time . Unix ( u . UpdatedUnix , 0 ) . Local ( )
2015-09-01 19:19:52 +03:00
}
}
2015-12-11 03:02:57 +03:00
// returns true if user login type is LOGIN_PLAIN.
func ( u * User ) IsLocal ( ) bool {
return u . LoginType <= LOGIN_PLAIN
}
2015-11-16 18:14:12 +03:00
// HasForkedRepo checks if user has already forked a repository with given ID.
func ( u * User ) HasForkedRepo ( repoID int64 ) bool {
2016-07-23 20:08:22 +03:00
_ , has := HasForkedRepo ( u . ID , repoID )
2015-11-16 18:14:12 +03:00
return has
}
2015-12-10 20:37:53 +03:00
func ( u * User ) RepoCreationNum ( ) int {
2015-12-10 20:46:05 +03:00
if u . MaxRepoCreation <= - 1 {
2015-12-10 20:37:53 +03:00
return setting . Repository . MaxCreationLimit
}
return u . MaxRepoCreation
}
func ( u * User ) CanCreateRepo ( ) bool {
2015-12-10 20:46:05 +03:00
if u . MaxRepoCreation <= - 1 {
2015-12-11 23:11:13 +03:00
if setting . Repository . MaxCreationLimit <= - 1 {
2015-12-11 00:27:47 +03:00
return true
}
2015-12-10 20:37:53 +03:00
return u . NumRepos < setting . Repository . MaxCreationLimit
}
return u . NumRepos < u . MaxRepoCreation
}
2015-11-04 02:40:52 +03:00
// CanEditGitHook returns true if user can edit Git hooks.
func ( u * User ) CanEditGitHook ( ) bool {
return u . IsAdmin || u . AllowGitHook
}
// CanImportLocal returns true if user can migrate repository by local path.
func ( u * User ) CanImportLocal ( ) bool {
return u . IsAdmin || u . AllowImportLocal
}
2014-07-26 08:24:27 +04:00
// DashboardLink returns the user dashboard page link.
func ( u * User ) DashboardLink ( ) string {
if u . IsOrganization ( ) {
2014-09-20 04:11:34 +04:00
return setting . AppSubUrl + "/org/" + u . Name + "/dashboard/"
2014-07-26 08:24:27 +04:00
}
2014-09-20 04:11:34 +04:00
return setting . AppSubUrl + "/"
2014-07-26 08:24:27 +04:00
}
2015-08-26 16:45:51 +03:00
// HomeLink returns the user or organization home page link.
2014-06-25 08:44:48 +04:00
func ( u * User ) HomeLink ( ) string {
2014-09-28 15:55:58 +04:00
return setting . AppSubUrl + "/" + u . Name
2014-04-10 22:20:58 +04:00
}
2015-09-17 08:54:12 +03:00
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
func ( u * User ) GenerateEmailActivateCode ( email string ) string {
code := base . CreateTimeLimitCode (
2016-07-23 20:08:22 +03:00
com . ToStr ( u . ID ) + email + u . LowerName + u . Passwd + u . Rands ,
2015-09-17 08:54:12 +03:00
setting . Service . ActiveCodeLives , nil )
// Add tail hex username
code += hex . EncodeToString ( [ ] byte ( u . LowerName ) )
return code
}
// GenerateActivateCode generates an activate code based on user information.
func ( u * User ) GenerateActivateCode ( ) string {
return u . GenerateEmailActivateCode ( u . Email )
}
2014-04-10 22:20:58 +04:00
2015-09-06 17:08:14 +03:00
// CustomAvatarPath returns user custom avatar file path.
func ( u * User ) CustomAvatarPath ( ) string {
2016-07-23 20:08:22 +03:00
return filepath . Join ( setting . AvatarUploadPath , com . ToStr ( u . ID ) )
2015-09-06 17:08:14 +03:00
}
// GenerateRandomAvatar generates a random avatar for user.
func ( u * User ) GenerateRandomAvatar ( ) error {
seed := u . Email
if len ( seed ) == 0 {
seed = u . Name
}
img , err := avatar . RandomImage ( [ ] byte ( seed ) )
if err != nil {
return fmt . Errorf ( "RandomImage: %v" , err )
}
2016-02-02 18:22:27 +03:00
if err = os . MkdirAll ( filepath . Dir ( u . CustomAvatarPath ( ) ) , os . ModePerm ) ; err != nil {
2015-09-06 17:08:14 +03:00
return fmt . Errorf ( "MkdirAll: %v" , err )
}
fw , err := os . Create ( u . CustomAvatarPath ( ) )
if err != nil {
return fmt . Errorf ( "Create: %v" , err )
}
defer fw . Close ( )
2016-07-21 10:31:14 +03:00
if err = png . Encode ( fw , img ) ; err != nil {
2015-09-06 17:08:14 +03:00
return fmt . Errorf ( "Encode: %v" , err )
}
2016-07-23 20:08:22 +03:00
log . Info ( "New random avatar created: %d" , u . ID )
2015-09-06 17:08:14 +03:00
return nil
}
2015-08-28 18:36:13 +03:00
func ( u * User ) RelAvatarLink ( ) string {
2016-06-27 13:12:30 +03:00
defaultImgUrl := "/img/avatar_default.png"
2016-07-23 20:08:22 +03:00
if u . ID == - 1 {
2015-08-14 21:48:05 +03:00
return defaultImgUrl
}
2014-11-21 18:58:08 +03:00
switch {
case u . UseCustomAvatar :
2015-09-06 17:08:14 +03:00
if ! com . IsExist ( u . CustomAvatarPath ( ) ) {
2015-08-09 06:46:10 +03:00
return defaultImgUrl
}
2016-07-23 20:08:22 +03:00
return "/avatars/" + com . ToStr ( u . ID )
2015-03-13 03:32:38 +03:00
case setting . DisableGravatar , setting . OfflineMode :
2015-09-06 17:08:14 +03:00
if ! com . IsExist ( u . CustomAvatarPath ( ) ) {
if err := u . GenerateRandomAvatar ( ) ; err != nil {
log . Error ( 3 , "GenerateRandomAvatar: %v" , err )
2015-08-09 06:46:10 +03:00
}
}
2016-07-23 20:08:22 +03:00
return "/avatars/" + com . ToStr ( u . ID )
2014-04-10 22:20:58 +04:00
}
2014-11-17 04:27:04 +03:00
return setting . GravatarSource + u . Avatar
2014-04-10 22:20:58 +04:00
}
2016-07-16 03:19:30 +03:00
// AvatarLink returns user avatar link.
2015-08-28 18:36:13 +03:00
func ( u * User ) AvatarLink ( ) string {
link := u . RelAvatarLink ( )
2015-09-02 12:16:30 +03:00
if link [ 0 ] == '/' && link [ 1 ] != '/' {
2016-07-16 03:19:30 +03:00
return strings . TrimSuffix ( setting . AppUrl , "/" ) + link
2015-08-28 18:36:13 +03:00
}
return link
}
2015-12-21 15:24:11 +03:00
// User.GetFollwoers returns range of user's followers.
func ( u * User ) GetFollowers ( page int ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 , ItemsPerPage )
2016-07-23 20:08:22 +03:00
sess := x . Limit ( ItemsPerPage , ( page - 1 ) * ItemsPerPage ) . Where ( "follow.follow_id=?" , u . ID )
2015-12-21 15:24:11 +03:00
if setting . UsePostgreSQL {
sess = sess . Join ( "LEFT" , "follow" , ` "user".id=follow.user_id ` )
} else {
sess = sess . Join ( "LEFT" , "follow" , "user.id=follow.user_id" )
}
return users , sess . Find ( & users )
}
func ( u * User ) IsFollowing ( followID int64 ) bool {
2016-07-23 20:08:22 +03:00
return IsFollowing ( u . ID , followID )
2015-12-21 15:24:11 +03:00
}
// GetFollowing returns range of user's following.
func ( u * User ) GetFollowing ( page int ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 , ItemsPerPage )
2016-07-23 20:08:22 +03:00
sess := x . Limit ( ItemsPerPage , ( page - 1 ) * ItemsPerPage ) . Where ( "follow.user_id=?" , u . ID )
2015-12-21 15:24:11 +03:00
if setting . UsePostgreSQL {
sess = sess . Join ( "LEFT" , "follow" , ` "user".id=follow.follow_id ` )
} else {
sess = sess . Join ( "LEFT" , "follow" , "user.id=follow.follow_id" )
}
return users , sess . Find ( & users )
}
2014-04-10 22:20:58 +04:00
// NewGitSig generates and returns the signature of given user.
2014-06-25 08:44:48 +04:00
func ( u * User ) NewGitSig ( ) * git . Signature {
2014-04-10 22:20:58 +04:00
return & git . Signature {
2014-06-25 08:44:48 +04:00
Name : u . Name ,
Email : u . Email ,
2014-04-10 22:20:58 +04:00
When : time . Now ( ) ,
}
}
// EncodePasswd encodes password to safe format.
2014-06-25 08:44:48 +04:00
func ( u * User ) EncodePasswd ( ) {
newPasswd := base . PBKDF2 ( [ ] byte ( u . Passwd ) , [ ] byte ( u . Salt ) , 10000 , 50 , sha256 . New )
u . Passwd = fmt . Sprintf ( "%x" , newPasswd )
}
2015-04-16 21:40:39 +03:00
// ValidatePassword checks if given password matches the one belongs to the user.
2015-04-16 21:36:32 +03:00
func ( u * User ) ValidatePassword ( passwd string ) bool {
2014-08-02 21:47:33 +04:00
newUser := & User { Passwd : passwd , Salt : u . Salt }
newUser . EncodePasswd ( )
return u . Passwd == newUser . Passwd
}
2014-11-21 18:58:08 +03:00
// UploadAvatar saves custom avatar for user.
2014-12-07 04:22:48 +03:00
// FIXME: split uploads to different subdirs in case we have massive users.
2014-11-21 18:58:08 +03:00
func ( u * User ) UploadAvatar ( data [ ] byte ) error {
img , _ , err := image . Decode ( bytes . NewReader ( data ) )
if err != nil {
2015-11-14 00:43:43 +03:00
return fmt . Errorf ( "Decode: %v" , err )
2014-11-21 18:58:08 +03:00
}
2015-09-07 01:19:30 +03:00
2016-07-21 10:31:14 +03:00
m := resize . Resize ( avatar . AVATAR_SIZE , avatar . AVATAR_SIZE , img , resize . NearestNeighbor )
2014-11-21 18:58:08 +03:00
sess := x . NewSession ( )
2015-09-07 01:19:30 +03:00
defer sessionRelease ( sess )
2014-11-21 18:58:08 +03:00
if err = sess . Begin ( ) ; err != nil {
return err
}
2015-11-14 00:43:43 +03:00
u . UseCustomAvatar = true
if err = updateUser ( sess , u ) ; err != nil {
return fmt . Errorf ( "updateUser: %v" , err )
2014-11-21 18:58:08 +03:00
}
2014-11-21 20:51:36 +03:00
os . MkdirAll ( setting . AvatarUploadPath , os . ModePerm )
2014-11-22 18:22:53 +03:00
fw , err := os . Create ( u . CustomAvatarPath ( ) )
2014-11-21 18:58:08 +03:00
if err != nil {
2015-11-14 00:43:43 +03:00
return fmt . Errorf ( "Create: %v" , err )
2014-11-21 18:58:08 +03:00
}
defer fw . Close ( )
2015-09-07 01:19:30 +03:00
2015-11-14 00:43:43 +03:00
if err = png . Encode ( fw , m ) ; err != nil {
return fmt . Errorf ( "Encode: %v" , err )
2014-11-21 18:58:08 +03:00
}
return sess . Commit ( )
}
2016-03-06 19:36:30 +03:00
// DeleteAvatar deletes the user's custom avatar.
func ( u * User ) DeleteAvatar ( ) error {
2016-07-23 20:08:22 +03:00
log . Trace ( "DeleteAvatar[%d]: %s" , u . ID , u . CustomAvatarPath ( ) )
2016-03-06 19:36:30 +03:00
os . Remove ( u . CustomAvatarPath ( ) )
u . UseCustomAvatar = false
if err := UpdateUser ( u ) ; err != nil {
2016-03-06 21:24:42 +03:00
return fmt . Errorf ( "UpdateUser: %v" , err )
2016-03-06 19:36:30 +03:00
}
return nil
}
2015-08-13 21:43:40 +03:00
// IsAdminOfRepo returns true if user has admin or higher access of repository.
func ( u * User ) IsAdminOfRepo ( repo * Repository ) bool {
2016-03-06 04:45:23 +03:00
has , err := HasAccess ( u , repo , ACCESS_MODE_ADMIN )
if err != nil {
log . Error ( 3 , "HasAccess: %v" , err )
2015-08-13 21:43:40 +03:00
}
2016-03-06 04:45:23 +03:00
return has
2015-08-13 21:43:40 +03:00
}
2016-03-06 04:45:23 +03:00
// IsWriterOfRepo returns true if user has write access to given repository.
func ( u * User ) IsWriterOfRepo ( repo * Repository ) bool {
2016-03-04 23:43:01 +03:00
has , err := HasAccess ( u , repo , ACCESS_MODE_WRITE )
if err != nil {
log . Error ( 3 , "HasAccess: %v" , err )
}
return has
}
2014-06-28 08:40:07 +04:00
// IsOrganization returns true if user is actually a organization.
2014-06-25 08:44:48 +04:00
func ( u * User ) IsOrganization ( ) bool {
2016-03-11 23:33:12 +03:00
return u . Type == USER_TYPE_ORGANIZATION
2014-06-25 08:44:48 +04:00
}
2014-08-15 14:29:41 +04:00
// IsUserOrgOwner returns true if user is in the owner team of given organization.
func ( u * User ) IsUserOrgOwner ( orgId int64 ) bool {
2016-07-23 20:08:22 +03:00
return IsOrganizationOwner ( orgId , u . ID )
2014-08-15 14:29:41 +04:00
}
// IsPublicMember returns true if user public his/her membership in give organization.
func ( u * User ) IsPublicMember ( orgId int64 ) bool {
2016-07-23 20:08:22 +03:00
return IsPublicMembership ( orgId , u . ID )
2014-08-15 14:29:41 +04:00
}
2015-09-06 15:54:08 +03:00
func ( u * User ) getOrganizationCount ( e Engine ) ( int64 , error ) {
2016-07-23 20:08:22 +03:00
return e . Where ( "uid=?" , u . ID ) . Count ( new ( OrgUser ) )
2015-09-06 15:54:08 +03:00
}
2014-06-28 23:43:25 +04:00
// GetOrganizationCount returns count of membership of organization of user.
func ( u * User ) GetOrganizationCount ( ) ( int64 , error ) {
2015-09-06 15:54:08 +03:00
return u . getOrganizationCount ( x )
2014-06-28 23:43:25 +04:00
}
2016-07-24 09:32:46 +03:00
// GetRepositories returns repositories that user owns, including private repositories.
func ( u * User ) GetRepositories ( page , pageSize int ) ( err error ) {
u . Repos , err = GetUserRepositories ( u . ID , true , page , pageSize )
2014-08-24 17:09:05 +04:00
return err
}
2016-07-24 09:32:46 +03:00
// GetRepositories returns mirror repositories that user owns, including private repositories.
func ( u * User ) GetMirrorRepositories ( ) ( [ ] * Repository , error ) {
return GetUserMirrorRepositories ( u . ID )
}
2015-09-07 20:58:23 +03:00
// GetOwnedOrganizations returns all organizations that user owns.
func ( u * User ) GetOwnedOrganizations ( ) ( err error ) {
2016-07-23 20:08:22 +03:00
u . OwnedOrgs , err = GetOwnedOrgsByUserID ( u . ID )
2015-09-07 20:58:23 +03:00
return err
}
2014-06-28 08:40:07 +04:00
// GetOrganizations returns all organizations that user belongs to.
2015-12-17 10:28:47 +03:00
func ( u * User ) GetOrganizations ( all bool ) error {
2016-07-23 20:08:22 +03:00
ous , err := GetOrgUsersByUserID ( u . ID , all )
2014-06-25 08:44:48 +04:00
if err != nil {
return err
}
u . Orgs = make ( [ ] * User , len ( ous ) )
for i , ou := range ous {
2015-08-08 17:43:14 +03:00
u . Orgs [ i ] , err = GetUserByID ( ou . OrgID )
2014-06-25 08:44:48 +04:00
if err != nil {
return err
}
}
return nil
2014-04-10 22:20:58 +04:00
}
2015-08-27 08:26:38 +03:00
// DisplayName returns full name if it's not empty,
// returns username otherwise.
func ( u * User ) DisplayName ( ) string {
if len ( u . FullName ) > 0 {
return u . FullName
2014-09-17 17:11:51 +04:00
}
2015-08-27 08:26:38 +03:00
return u . Name
2014-09-17 17:11:51 +04:00
}
2015-11-19 01:42:20 +03:00
func ( u * User ) ShortName ( length int ) string {
2016-01-11 15:41:43 +03:00
return base . EllipsisString ( u . Name , length )
2015-11-19 01:42:20 +03:00
}
2014-04-10 22:20:58 +04:00
// IsUserExist checks if given user name exist,
// the user name should be noncased unique.
2015-02-23 02:24:49 +03:00
// If uid is presented, then check will rule out that one,
// it is used when update a user name in settings page.
func IsUserExist ( uid int64 , name string ) ( bool , error ) {
2014-04-10 22:20:58 +04:00
if len ( name ) == 0 {
return false , nil
}
2015-02-23 02:24:49 +03:00
return x . Where ( "id!=?" , uid ) . Get ( & User { LowerName : strings . ToLower ( name ) } )
2014-04-10 22:20:58 +04:00
}
2014-10-25 02:43:17 +04:00
// GetUserSalt returns a ramdom user salt token.
2014-04-10 22:20:58 +04:00
func GetUserSalt ( ) string {
return base . GetRandomString ( 10 )
}
2015-08-14 21:48:05 +03:00
// NewFakeUser creates and returns a fake user for someone has deleted his/her account.
func NewFakeUser ( ) * User {
return & User {
2016-07-23 20:08:22 +03:00
ID : - 1 ,
2015-08-14 21:48:05 +03:00
Name : "Someone" ,
LowerName : "someone" ,
}
}
2016-07-23 13:58:18 +03:00
var (
reversedUsernames = [ ] string { "debug" , "raw" , "install" , "api" , "avatar" , "user" , "org" , "help" , "stars" , "issues" , "pulls" , "commits" , "repo" , "template" , "admin" , "new" , "." , ".." }
reversedUserPatterns = [ ] string { "*.keys" }
)
// isUsableName checks if name is reserved or pattern of name is not allowed
// based on given reversed names and patterns.
// Names are exact match, patterns can be prefix or suffix match with placeholder '*'.
func isUsableName ( names , patterns [ ] string , name string ) error {
name = strings . TrimSpace ( strings . ToLower ( name ) )
if utf8 . RuneCountInString ( name ) == 0 {
return ErrNameEmpty
}
for i := range names {
if name == names [ i ] {
return ErrNameReserved { name }
}
}
for _ , pat := range patterns {
if pat [ 0 ] == '*' && strings . HasSuffix ( name , pat [ 1 : ] ) ||
( pat [ len ( pat ) - 1 ] == '*' && strings . HasPrefix ( name , pat [ : len ( pat ) - 1 ] ) ) {
return ErrNamePatternNotAllowed { pat }
}
}
return nil
}
func IsUsableUsername ( name string ) error {
return isUsableName ( reversedUsernames , reversedUserPatterns , name )
}
2014-06-25 08:44:48 +04:00
// CreateUser creates record of a new user.
2015-03-27 00:11:47 +03:00
func CreateUser ( u * User ) ( err error ) {
2016-07-23 13:58:18 +03:00
if err = IsUsableUsername ( u . Name ) ; err != nil {
2015-03-27 00:11:47 +03:00
return err
2014-06-25 08:44:48 +04:00
}
2015-02-23 02:24:49 +03:00
isExist , err := IsUserExist ( 0 , u . Name )
2014-06-25 08:44:48 +04:00
if err != nil {
2014-07-26 08:24:27 +04:00
return err
2014-06-25 08:44:48 +04:00
} else if isExist {
2015-03-27 00:11:47 +03:00
return ErrUserAlreadyExist { u . Name }
2014-06-25 08:44:48 +04:00
}
2015-11-24 04:43:04 +03:00
u . Email = strings . ToLower ( u . Email )
2014-06-25 08:44:48 +04:00
isExist , err = IsEmailUsed ( u . Email )
if err != nil {
2014-07-26 08:24:27 +04:00
return err
2014-06-25 08:44:48 +04:00
} else if isExist {
2015-03-27 00:11:47 +03:00
return ErrEmailAlreadyUsed { u . Email }
2014-06-25 08:44:48 +04:00
}
u . LowerName = strings . ToLower ( u . Name )
u . AvatarEmail = u . Email
2016-02-15 07:14:55 +03:00
u . Avatar = base . HashEmail ( u . AvatarEmail )
2014-06-25 08:44:48 +04:00
u . Rands = GetUserSalt ( )
u . Salt = GetUserSalt ( )
u . EncodePasswd ( )
2015-12-11 23:11:13 +03:00
u . MaxRepoCreation = - 1
2014-06-25 08:44:48 +04:00
sess := x . NewSession ( )
2016-07-16 05:08:04 +03:00
defer sessionRelease ( sess )
2014-06-25 08:44:48 +04:00
if err = sess . Begin ( ) ; err != nil {
2014-07-26 08:24:27 +04:00
return err
2014-06-25 08:44:48 +04:00
}
if _ , err = sess . Insert ( u ) ; err != nil {
2014-07-26 08:24:27 +04:00
return err
} else if err = os . MkdirAll ( UserPath ( u . Name ) , os . ModePerm ) ; err != nil {
return err
2014-06-25 08:44:48 +04:00
}
2014-04-22 20:55:27 +04:00
2015-08-18 23:58:45 +03:00
return sess . Commit ( )
2014-06-25 08:44:48 +04:00
}
2015-08-06 19:00:11 +03:00
func countUsers ( e Engine ) int64 {
count , _ := e . Where ( "type=0" ) . Count ( new ( User ) )
return count
}
2014-07-07 12:15:08 +04:00
// CountUsers returns number of users.
func CountUsers ( ) int64 {
2015-08-06 19:00:11 +03:00
return countUsers ( x )
2014-07-07 12:15:08 +04:00
}
2015-09-12 03:42:26 +03:00
// Users returns number of users in given page.
func Users ( page , pageSize int ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 , pageSize )
return users , x . Limit ( pageSize , ( page - 1 ) * pageSize ) . Where ( "type=0" ) . Asc ( "id" ) . Find ( & users )
2014-04-10 22:20:58 +04:00
}
// get user by erify code
func getVerifyUser ( code string ) ( user * User ) {
if len ( code ) <= base . TimeLimitCodeLength {
return nil
}
// use tail hex username query user
hexStr := code [ base . TimeLimitCodeLength : ]
if b , err := hex . DecodeString ( hexStr ) ; err == nil {
if user , err = GetUserByName ( string ( b ) ) ; user != nil {
return user
}
2014-07-26 08:24:27 +04:00
log . Error ( 4 , "user.getVerifyUser: %v" , err )
2014-04-10 22:20:58 +04:00
}
return nil
}
// verify active code when active account
func VerifyUserActiveCode ( code string ) ( user * User ) {
2014-05-26 04:11:25 +04:00
minutes := setting . Service . ActiveCodeLives
2014-04-10 22:20:58 +04:00
if user = getVerifyUser ( code ) ; user != nil {
// time limit code
prefix := code [ : base . TimeLimitCodeLength ]
2016-07-23 20:08:22 +03:00
data := com . ToStr ( user . ID ) + user . Email + user . LowerName + user . Passwd + user . Rands
2014-04-10 22:20:58 +04:00
if base . VerifyTimeLimitCode ( data , minutes , prefix ) {
return user
}
}
return nil
}
2014-12-17 18:40:10 +03:00
// verify active code when active account
func VerifyActiveEmailCode ( code , email string ) * EmailAddress {
minutes := setting . Service . ActiveCodeLives
if user := getVerifyUser ( code ) ; user != nil {
// time limit code
prefix := code [ : base . TimeLimitCodeLength ]
2016-07-23 20:08:22 +03:00
data := com . ToStr ( user . ID ) + email + user . LowerName + user . Passwd + user . Rands
2014-12-17 18:40:10 +03:00
if base . VerifyTimeLimitCode ( data , minutes , prefix ) {
emailAddress := & EmailAddress { Email : email }
if has , _ := x . Get ( emailAddress ) ; has {
return emailAddress
}
}
}
return nil
}
2014-04-10 22:20:58 +04:00
// ChangeUserName changes all corresponding setting from old user name to new one.
2014-07-26 08:24:27 +04:00
func ChangeUserName ( u * User , newUserName string ) ( err error ) {
2016-07-23 13:58:18 +03:00
if err = IsUsableUsername ( newUserName ) ; err != nil {
2015-03-27 00:11:47 +03:00
return err
}
isExist , err := IsUserExist ( 0 , newUserName )
if err != nil {
return err
} else if isExist {
return ErrUserAlreadyExist { newUserName }
2014-07-26 08:24:27 +04:00
}
2016-02-05 22:11:53 +03:00
if err = ChangeUsernameInPullRequests ( u . Name , newUserName ) ; err != nil {
2016-01-28 14:07:16 +03:00
return fmt . Errorf ( "ChangeUsernameInPullRequests: %v" , err )
2016-01-28 00:45:03 +03:00
}
2016-02-05 22:11:53 +03:00
// Delete all local copies of repository wiki that user owns.
2016-07-23 20:08:22 +03:00
if err = x . Where ( "owner_id=?" , u . ID ) . Iterate ( new ( Repository ) , func ( idx int , bean interface { } ) error {
2016-02-05 22:11:53 +03:00
repo := bean . ( * Repository )
RemoveAllWithNotice ( "Delete repository wiki local copy" , repo . LocalWikiPath ( ) )
return nil
} ) ; err != nil {
return fmt . Errorf ( "Delete repository wiki local copy: %v" , err )
}
2016-01-28 14:07:16 +03:00
return os . Rename ( UserPath ( u . Name ) , UserPath ( newUserName ) )
2014-04-10 22:20:58 +04:00
}
2015-08-29 20:13:24 +03:00
func updateUser ( e Engine , u * User ) error {
2016-03-05 08:51:51 +03:00
// Organization does not need email
2015-09-06 17:08:14 +03:00
if ! u . IsOrganization ( ) {
u . Email = strings . ToLower ( u . Email )
2016-07-23 20:08:22 +03:00
has , err := e . Where ( "id!=?" , u . ID ) . And ( "type=?" , u . Type ) . And ( "email=?" , u . Email ) . Get ( new ( User ) )
2015-09-06 17:08:14 +03:00
if err != nil {
return err
} else if has {
return ErrEmailAlreadyUsed { u . Email }
}
if len ( u . AvatarEmail ) == 0 {
u . AvatarEmail = u . Email
}
2016-02-15 07:14:55 +03:00
u . Avatar = base . HashEmail ( u . AvatarEmail )
2014-12-01 02:29:16 +03:00
}
2014-06-06 06:07:35 +04:00
u . LowerName = strings . ToLower ( u . Name )
2016-03-05 08:51:51 +03:00
u . Location = base . TruncateString ( u . Location , 255 )
u . Website = base . TruncateString ( u . Website , 255 )
u . Description = base . TruncateString ( u . Description , 255 )
2014-04-10 22:20:58 +04:00
2016-02-21 01:10:05 +03:00
u . FullName = markdown . Sanitizer . Sanitize ( u . FullName )
2016-07-23 20:08:22 +03:00
_ , err := e . Id ( u . ID ) . AllCols ( ) . Update ( u )
2014-04-10 22:20:58 +04:00
return err
}
2015-08-29 20:13:24 +03:00
// UpdateUser updates user's information.
func UpdateUser ( u * User ) error {
return updateUser ( x , u )
}
2015-09-06 15:54:08 +03:00
// deleteBeans deletes all given beans, beans should contain delete conditions.
func deleteBeans ( e Engine , beans ... interface { } ) ( err error ) {
2015-03-18 04:51:39 +03:00
for i := range beans {
if _ , err = e . Delete ( beans [ i ] ) ; err != nil {
return err
}
}
return nil
}
2014-11-13 13:27:01 +03:00
// FIXME: need some kind of mechanism to record failure. HINT: system notice
2015-09-06 15:54:08 +03:00
func deleteUser ( e * xorm . Session , u * User ) error {
2015-08-17 12:05:37 +03:00
// Note: A user owns any repository or belongs to any organization
// cannot perform delete operation.
2014-04-10 22:20:58 +04:00
// Check ownership of repository.
2015-09-06 15:54:08 +03:00
count , err := getRepositoryCount ( e , u )
2014-04-10 22:20:58 +04:00
if err != nil {
2015-03-18 04:51:39 +03:00
return fmt . Errorf ( "GetRepositoryCount: %v" , err )
2014-04-10 22:20:58 +04:00
} else if count > 0 {
2016-07-23 20:08:22 +03:00
return ErrUserOwnRepos { UID : u . ID }
2014-04-10 22:20:58 +04:00
}
2014-06-27 11:37:01 +04:00
// Check membership of organization.
2015-09-06 15:54:08 +03:00
count , err = u . getOrganizationCount ( e )
2014-06-27 11:37:01 +04:00
if err != nil {
2015-03-18 04:51:39 +03:00
return fmt . Errorf ( "GetOrganizationCount: %v" , err )
2014-06-27 11:37:01 +04:00
} else if count > 0 {
2016-07-23 20:08:22 +03:00
return ErrUserHasOrgs { UID : u . ID }
2014-06-27 11:37:01 +04:00
}
2015-08-17 12:05:37 +03:00
// ***** START: Watch *****
2015-03-18 04:51:39 +03:00
watches := make ( [ ] * Watch , 0 , 10 )
2016-07-23 20:08:22 +03:00
if err = e . Find ( & watches , & Watch { UserID : u . ID } ) ; err != nil {
2015-03-18 04:51:39 +03:00
return fmt . Errorf ( "get all watches: %v" , err )
2014-04-10 22:20:58 +04:00
}
2015-03-18 04:51:39 +03:00
for i := range watches {
2015-09-06 15:54:08 +03:00
if _ , err = e . Exec ( "UPDATE `repository` SET num_watches=num_watches-1 WHERE id=?" , watches [ i ] . RepoID ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "decrease repository watch number[%d]: %v" , watches [ i ] . RepoID , err )
}
2014-04-12 05:47:39 +04:00
}
2015-08-17 12:05:37 +03:00
// ***** END: Watch *****
2015-03-18 04:51:39 +03:00
2015-08-17 12:05:37 +03:00
// ***** START: Star *****
stars := make ( [ ] * Star , 0 , 10 )
2016-07-23 20:08:22 +03:00
if err = e . Find ( & stars , & Star { UID : u . ID } ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "get all stars: %v" , err )
}
for i := range stars {
2015-09-06 15:54:08 +03:00
if _ , err = e . Exec ( "UPDATE `repository` SET num_stars=num_stars-1 WHERE id=?" , stars [ i ] . RepoID ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "decrease repository star number[%d]: %v" , stars [ i ] . RepoID , err )
}
}
// ***** END: Star *****
2015-03-18 04:51:39 +03:00
2015-08-17 12:05:37 +03:00
// ***** START: Follow *****
followers := make ( [ ] * Follow , 0 , 10 )
2016-07-23 20:08:22 +03:00
if err = e . Find ( & followers , & Follow { UserID : u . ID } ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "get all followers: %v" , err )
}
for i := range followers {
2015-09-06 15:54:08 +03:00
if _ , err = e . Exec ( "UPDATE `user` SET num_followers=num_followers-1 WHERE id=?" , followers [ i ] . UserID ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "decrease user follower number[%d]: %v" , followers [ i ] . UserID , err )
}
2014-04-10 22:20:58 +04:00
}
2015-08-17 12:05:37 +03:00
// ***** END: Follow *****
2015-03-18 04:51:39 +03:00
2015-09-06 15:54:08 +03:00
if err = deleteBeans ( e ,
2016-07-23 20:08:22 +03:00
& AccessToken { UID : u . ID } ,
& Collaboration { UserID : u . ID } ,
& Access { UserID : u . ID } ,
& Watch { UserID : u . ID } ,
& Star { UID : u . ID } ,
& Follow { FollowID : u . ID } ,
& Action { UserID : u . ID } ,
& IssueUser { UID : u . ID } ,
& EmailAddress { UID : u . ID } ,
2015-03-18 04:51:39 +03:00
) ; err != nil {
2015-12-01 04:45:55 +03:00
return fmt . Errorf ( "deleteBeans: %v" , err )
2014-04-10 22:20:58 +04:00
}
2015-03-18 04:51:39 +03:00
2015-08-17 12:05:37 +03:00
// ***** START: PublicKey *****
2014-05-07 00:28:52 +04:00
keys := make ( [ ] * PublicKey , 0 , 10 )
2016-07-23 20:08:22 +03:00
if err = e . Find ( & keys , & PublicKey { OwnerID : u . ID } ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "get all public keys: %v" , err )
2014-04-10 22:20:58 +04:00
}
for _ , key := range keys {
2015-09-06 15:54:08 +03:00
if err = deletePublicKey ( e , key . ID ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "deletePublicKey: %v" , err )
2014-04-10 22:20:58 +04:00
}
}
2015-08-17 12:05:37 +03:00
// ***** END: PublicKey *****
2014-04-10 22:20:58 +04:00
2015-08-14 21:48:05 +03:00
// Clear assignee.
2016-07-23 20:08:22 +03:00
if _ , err = e . Exec ( "UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?" , u . ID ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "clear assignee: %v" , err )
2015-08-14 21:48:05 +03:00
}
2016-07-23 20:08:22 +03:00
if _ , err = e . Id ( u . ID ) . Delete ( new ( User ) ) ; err != nil {
2015-08-17 12:05:37 +03:00
return fmt . Errorf ( "Delete: %v" , err )
2015-03-18 04:51:39 +03:00
}
2015-08-17 12:05:37 +03:00
// FIXME: system notice
// Note: There are something just cannot be roll back,
// so just keep error logs of those operations.
RewriteAllPublicKeys ( )
os . RemoveAll ( UserPath ( u . Name ) )
2015-08-11 22:46:08 +03:00
os . Remove ( u . CustomAvatarPath ( ) )
2014-04-10 22:20:58 +04:00
2015-08-17 12:05:37 +03:00
return nil
2014-06-21 08:51:41 +04:00
}
2015-09-06 15:54:08 +03:00
// DeleteUser completely and permanently deletes everything of a user,
// but issues/comments/pulls will be kept and shown as someone has been deleted.
func DeleteUser ( u * User ) ( err error ) {
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return err
}
if err = deleteUser ( sess , u ) ; err != nil {
2015-09-13 20:26:20 +03:00
// Note: don't wrapper error here.
return err
2015-09-06 15:54:08 +03:00
}
return sess . Commit ( )
}
2014-12-17 11:26:19 +03:00
// DeleteInactivateUsers deletes all inactivate users and email addresses.
2015-08-17 12:05:37 +03:00
func DeleteInactivateUsers ( ) ( err error ) {
users := make ( [ ] * User , 0 , 10 )
if err = x . Where ( "is_active=?" , false ) . Find ( & users ) ; err != nil {
return fmt . Errorf ( "get all inactive users: %v" , err )
}
for _ , u := range users {
if err = DeleteUser ( u ) ; err != nil {
// Ignore users that were set inactive by admin.
if IsErrUserOwnRepos ( err ) || IsErrUserHasOrgs ( err ) {
continue
}
return err
}
2014-12-17 11:26:19 +03:00
}
2015-08-17 12:05:37 +03:00
_ , err = x . Where ( "is_activated=?" , false ) . Delete ( new ( EmailAddress ) )
2014-04-10 22:20:58 +04:00
return err
}
// UserPath returns the path absolute path of user repositories.
func UserPath ( userName string ) string {
2014-05-26 04:11:25 +04:00
return filepath . Join ( setting . RepoRootPath , strings . ToLower ( userName ) )
2014-04-10 22:20:58 +04:00
}
2015-11-05 05:57:10 +03:00
func GetUserByKeyID ( keyID int64 ) ( * User , error ) {
2014-04-10 22:20:58 +04:00
user := new ( User )
2015-11-05 05:57:10 +03:00
has , err := x . Sql ( "SELECT a.* FROM `user` AS a, public_key AS b WHERE a.id = b.owner_id AND b.id=?" , keyID ) . Get ( user )
2014-04-10 22:20:58 +04:00
if err != nil {
return nil , err
} else if ! has {
2014-05-22 05:37:13 +04:00
return nil , ErrUserNotKeyOwner
2014-04-10 22:20:58 +04:00
}
return user , nil
}
2015-08-08 17:43:14 +03:00
func getUserByID ( e Engine , id int64 ) ( * User , error ) {
2014-06-06 06:07:35 +04:00
u := new ( User )
2015-02-13 08:58:46 +03:00
has , err := e . Id ( id ) . Get ( u )
2014-04-10 22:20:58 +04:00
if err != nil {
return nil , err
2014-06-06 06:07:35 +04:00
} else if ! has {
2015-08-05 06:14:17 +03:00
return nil , ErrUserNotExist { id , "" }
2014-04-10 22:20:58 +04:00
}
2014-06-06 06:07:35 +04:00
return u , nil
2014-04-10 22:20:58 +04:00
}
2015-08-08 17:43:14 +03:00
// GetUserByID returns the user object by given ID if exists.
func GetUserByID ( id int64 ) ( * User , error ) {
return getUserByID ( x , id )
2015-02-13 08:58:46 +03:00
}
2015-08-10 16:47:23 +03:00
// GetAssigneeByID returns the user with write access of repository by given ID.
func GetAssigneeByID ( repo * Repository , userID int64 ) ( * User , error ) {
2016-07-23 20:08:22 +03:00
has , err := HasAccess ( & User { ID : userID } , repo , ACCESS_MODE_WRITE )
2015-08-10 16:47:23 +03:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrUserNotExist { userID , "" }
}
return GetUserByID ( userID )
}
2014-10-25 02:43:17 +04:00
// GetUserByName returns user by given name.
2014-04-10 22:20:58 +04:00
func GetUserByName ( name string ) ( * User , error ) {
if len ( name ) == 0 {
2015-08-05 06:14:17 +03:00
return nil , ErrUserNotExist { 0 , name }
2014-04-10 22:20:58 +04:00
}
2014-10-25 02:43:17 +04:00
u := & User { LowerName : strings . ToLower ( name ) }
has , err := x . Get ( u )
2014-04-10 22:20:58 +04:00
if err != nil {
return nil , err
} else if ! has {
2015-08-05 06:14:17 +03:00
return nil , ErrUserNotExist { 0 , name }
2014-04-10 22:20:58 +04:00
}
2014-10-25 02:43:17 +04:00
return u , nil
2014-04-10 22:20:58 +04:00
}
2014-10-12 02:02:48 +04:00
// GetUserEmailsByNames returns a list of e-mails corresponds to names.
2014-04-10 22:20:58 +04:00
func GetUserEmailsByNames ( names [ ] string ) [ ] string {
mails := make ( [ ] string , 0 , len ( names ) )
for _ , name := range names {
u , err := GetUserByName ( name )
if err != nil {
continue
}
mails = append ( mails , u . Email )
}
return mails
}
2016-07-16 05:08:04 +03:00
// GetUserIDsByNames returns a slice of ids corresponds to names.
func GetUserIDsByNames ( names [ ] string ) [ ] int64 {
2014-05-08 00:51:14 +04:00
ids := make ( [ ] int64 , 0 , len ( names ) )
for _ , name := range names {
u , err := GetUserByName ( name )
if err != nil {
continue
}
2016-07-23 20:08:22 +03:00
ids = append ( ids , u . ID )
2014-05-08 00:51:14 +04:00
}
return ids
}
2014-12-07 04:22:48 +03:00
// UserCommit represents a commit with validation of user.
2014-09-23 23:30:04 +04:00
type UserCommit struct {
2014-11-21 18:58:08 +03:00
User * User
2015-12-10 04:46:05 +03:00
* git . Commit
2014-09-23 23:30:04 +04:00
}
2014-09-26 16:55:13 +04:00
// ValidateCommitWithEmail chceck if author's e-mail of commit is corresponsind to a user.
2015-12-10 04:46:05 +03:00
func ValidateCommitWithEmail ( c * git . Commit ) * User {
2014-09-26 16:55:13 +04:00
u , err := GetUserByEmail ( c . Author . Email )
2014-11-21 18:58:08 +03:00
if err != nil {
return nil
2014-09-26 16:55:13 +04:00
}
2014-11-21 18:58:08 +03:00
return u
2014-09-26 16:55:13 +04:00
}
// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users.
func ValidateCommitsWithEmails ( oldCommits * list . List ) * list . List {
2015-08-05 12:36:22 +03:00
var (
u * User
emails = map [ string ] * User { }
newCommits = list . New ( )
e = oldCommits . Front ( )
)
2014-09-23 23:30:04 +04:00
for e != nil {
2015-12-10 04:46:05 +03:00
c := e . Value . ( * git . Commit )
2014-09-23 23:30:04 +04:00
2014-09-24 07:18:14 +04:00
if v , ok := emails [ c . Author . Email ] ; ! ok {
2014-11-21 18:58:08 +03:00
u , _ = GetUserByEmail ( c . Author . Email )
emails [ c . Author . Email ] = u
2014-09-24 07:18:14 +04:00
} else {
2014-11-21 18:58:08 +03:00
u = v
2014-09-23 23:30:04 +04:00
}
newCommits . PushBack ( UserCommit {
2014-11-21 18:58:08 +03:00
User : u ,
Commit : c ,
2014-09-23 23:30:04 +04:00
} )
e = e . Next ( )
}
return newCommits
}
2014-04-10 22:20:58 +04:00
// GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail ( email string ) ( * User , error ) {
if len ( email ) == 0 {
2015-08-05 06:14:17 +03:00
return nil , ErrUserNotExist { 0 , "email" }
2014-04-10 22:20:58 +04:00
}
2015-08-05 12:36:22 +03:00
email = strings . ToLower ( email )
2014-12-17 11:26:19 +03:00
// First try to find the user by primary email
2015-08-05 12:36:22 +03:00
user := & User { Email : email }
2014-06-21 08:51:41 +04:00
has , err := x . Get ( user )
2014-04-10 22:20:58 +04:00
if err != nil {
return nil , err
}
2014-12-17 11:26:19 +03:00
if has {
return user , nil
}
// Otherwise, check in alternative list for activated email addresses
2015-08-05 12:36:22 +03:00
emailAddress := & EmailAddress { Email : email , IsActivated : true }
2014-12-17 11:26:19 +03:00
has , err = x . Get ( emailAddress )
if err != nil {
return nil , err
}
if has {
2015-09-10 18:40:34 +03:00
return GetUserByID ( emailAddress . UID )
2014-12-17 11:26:19 +03:00
}
2015-11-14 01:10:25 +03:00
return nil , ErrUserNotExist { 0 , email }
2014-04-10 22:20:58 +04:00
}
2016-03-11 23:33:12 +03:00
type SearchUserOptions struct {
Keyword string
Type UserType
OrderBy string
Page int
2016-07-23 19:23:54 +03:00
PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum
2016-03-11 23:33:12 +03:00
}
// SearchUserByName takes keyword and part of user name to search,
// it returns results in given range and number of total results.
func SearchUserByName ( opts * SearchUserOptions ) ( users [ ] * User , _ int64 , _ error ) {
if len ( opts . Keyword ) == 0 {
return users , 0 , nil
}
opts . Keyword = strings . ToLower ( opts . Keyword )
2016-07-23 19:23:54 +03:00
if opts . PageSize <= 0 || opts . PageSize > setting . UI . ExplorePagingNum {
opts . PageSize = setting . UI . ExplorePagingNum
2016-03-11 23:33:12 +03:00
}
if opts . Page <= 0 {
opts . Page = 1
}
2016-03-15 16:16:58 +03:00
searchQuery := "%" + opts . Keyword + "%"
2016-03-11 23:33:12 +03:00
users = make ( [ ] * User , 0 , opts . PageSize )
// Append conditions
2016-03-16 23:55:19 +03:00
sess := x . Where ( "LOWER(lower_name) LIKE ?" , searchQuery ) .
Or ( "LOWER(full_name) LIKE ?" , searchQuery ) .
2016-03-15 16:16:58 +03:00
And ( "type = ?" , opts . Type )
2016-03-11 23:33:12 +03:00
var countSess xorm . Session
countSess = * sess
count , err := countSess . Count ( new ( User ) )
if err != nil {
return nil , 0 , fmt . Errorf ( "Count: %v" , err )
2014-05-01 07:48:01 +04:00
}
2016-03-12 00:11:33 +03:00
if len ( opts . OrderBy ) > 0 {
sess . OrderBy ( opts . OrderBy )
}
2016-03-11 23:33:12 +03:00
return users , count , sess . Limit ( opts . PageSize , ( opts . Page - 1 ) * opts . PageSize ) . Find ( & users )
2014-05-01 07:48:01 +04:00
}
2015-12-21 15:24:11 +03:00
// ___________ .__ .__
// \_ _____/___ | | | | ______ _ __
// | __)/ _ \| | | | / _ \ \/ \/ /
// | \( <_> ) |_| |_( <_> ) /
// \___ / \____/|____/____/\____/ \/\_/
// \/
// Follow represents relations of user and his/her followers.
2014-04-10 22:20:58 +04:00
type Follow struct {
2015-08-17 12:05:37 +03:00
ID int64 ` xorm:"pk autoincr" `
UserID int64 ` xorm:"UNIQUE(follow)" `
FollowID int64 ` xorm:"UNIQUE(follow)" `
2014-04-10 22:20:58 +04:00
}
2015-12-21 15:24:11 +03:00
func IsFollowing ( userID , followID int64 ) bool {
has , _ := x . Get ( & Follow { UserID : userID , FollowID : followID } )
return has
}
2014-04-10 22:20:58 +04:00
// FollowUser marks someone be another's follower.
2015-12-21 15:24:11 +03:00
func FollowUser ( userID , followID int64 ) ( err error ) {
if userID == followID || IsFollowing ( userID , followID ) {
return nil
}
2014-10-03 21:12:54 +04:00
sess := x . NewSession ( )
2015-12-21 15:24:11 +03:00
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return err
}
2014-04-10 22:20:58 +04:00
2015-12-21 15:24:11 +03:00
if _ , err = sess . Insert ( & Follow { UserID : userID , FollowID : followID } ) ; err != nil {
2014-04-10 22:20:58 +04:00
return err
}
2015-12-21 15:24:11 +03:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_followers = num_followers + 1 WHERE id = ?" , followID ) ; err != nil {
2014-04-10 22:20:58 +04:00
return err
}
2015-12-21 15:24:11 +03:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_following = num_following + 1 WHERE id = ?" , userID ) ; err != nil {
2014-04-10 22:20:58 +04:00
return err
}
2014-10-03 21:12:54 +04:00
return sess . Commit ( )
2014-04-10 22:20:58 +04:00
}
2015-12-21 15:24:11 +03:00
// UnfollowUser unmarks someone be another's follower.
func UnfollowUser ( userID , followID int64 ) ( err error ) {
if userID == followID || ! IsFollowing ( userID , followID ) {
return nil
2014-04-10 22:20:58 +04:00
}
2015-12-21 15:24:11 +03:00
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
2014-04-10 22:20:58 +04:00
return err
}
2015-12-21 15:24:11 +03:00
if _ , err = sess . Delete ( & Follow { UserID : userID , FollowID : followID } ) ; err != nil {
2014-04-10 22:20:58 +04:00
return err
}
2014-07-24 21:15:31 +04:00
2015-12-21 15:24:11 +03:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_followers = num_followers - 1 WHERE id = ?" , followID ) ; err != nil {
2014-07-24 21:15:31 +04:00
return err
}
2015-12-21 15:24:11 +03:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_following = num_following - 1 WHERE id = ?" , userID ) ; err != nil {
2014-07-24 21:15:31 +04:00
return err
}
2015-12-21 15:24:11 +03:00
return sess . Commit ( )
2014-07-24 21:15:31 +04:00
}