mirror of https://github.com/gogs/gogs.git
refactor(db): move some methods from `user.go` to `users.go` (#7195)
parent
260e990be7
commit
c502dc6ed8
|
@ -5,6 +5,8 @@ vars:
|
||||||
sh: echo '{{if eq OS "windows"}}.exe{{end}}'
|
sh: echo '{{if eq OS "windows"}}.exe{{end}}'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
|
default:
|
||||||
|
deps: [build]
|
||||||
web:
|
web:
|
||||||
desc: Build the binary and start the web server
|
desc: Build the binary and start the web server
|
||||||
deps: [build]
|
deps: [build]
|
||||||
|
|
|
@ -99,7 +99,7 @@ func newMacaron() *macaron.Macaron {
|
||||||
conf.Picture.AvatarUploadPath,
|
conf.Picture.AvatarUploadPath,
|
||||||
macaron.StaticOptions{
|
macaron.StaticOptions{
|
||||||
ETag: true,
|
ETag: true,
|
||||||
Prefix: db.USER_AVATAR_URL_PREFIX,
|
Prefix: conf.UsersAvatarURLPath,
|
||||||
SkipLogging: conf.Server.DisableRouterLog,
|
SkipLogging: conf.Server.DisableRouterLog,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
|
|
@ -16,6 +16,14 @@ func SetMockApp(t *testing.T, opts AppOpts) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetMockAuth(t *testing.T, otps AuthOpts) {
|
||||||
|
before := Auth
|
||||||
|
Auth = otps
|
||||||
|
t.Cleanup(func() {
|
||||||
|
Auth = before
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func SetMockServer(t *testing.T, opts ServerOpts) {
|
func SetMockServer(t *testing.T, opts ServerOpts) {
|
||||||
before := Server
|
before := Server
|
||||||
Server = opts
|
Server = opts
|
||||||
|
|
|
@ -12,6 +12,12 @@ import (
|
||||||
"github.com/gogs/go-libravatar"
|
"github.com/gogs/go-libravatar"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UsersAvatarURLPath is used to identify whether a URL is to access user
|
||||||
|
// avatars.
|
||||||
|
UsersAvatarURLPath = "avatars"
|
||||||
|
)
|
||||||
|
|
||||||
// ℹ️ README: This file contains static values that should only be set at initialization time.
|
// ℹ️ README: This file contains static values that should only be set at initialization time.
|
||||||
//
|
//
|
||||||
// ⚠️ WARNING: After changing any options, do not forget to update template of
|
// ⚠️ WARNING: After changing any options, do not forget to update template of
|
||||||
|
@ -71,20 +77,6 @@ var (
|
||||||
FromEmail string `ini:"-"` // Parsed email address of From without person's name.
|
FromEmail string `ini:"-"` // Parsed email address of From without person's name.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication settings
|
|
||||||
Auth struct {
|
|
||||||
ActivateCodeLives int
|
|
||||||
ResetPasswordCodeLives int
|
|
||||||
RequireEmailConfirmation bool
|
|
||||||
RequireSigninView bool
|
|
||||||
DisableRegistration bool
|
|
||||||
EnableRegistrationCaptcha bool
|
|
||||||
|
|
||||||
EnableReverseProxyAuthentication bool
|
|
||||||
EnableReverseProxyAutoRegistration bool
|
|
||||||
ReverseProxyAuthenticationHeader string
|
|
||||||
}
|
|
||||||
|
|
||||||
// User settings
|
// User settings
|
||||||
User struct {
|
User struct {
|
||||||
EnableEmailNotification bool
|
EnableEmailNotification bool
|
||||||
|
@ -268,6 +260,22 @@ type AppOpts struct {
|
||||||
// Application settings
|
// Application settings
|
||||||
var App AppOpts
|
var App AppOpts
|
||||||
|
|
||||||
|
type AuthOpts struct {
|
||||||
|
ActivateCodeLives int
|
||||||
|
ResetPasswordCodeLives int
|
||||||
|
RequireEmailConfirmation bool
|
||||||
|
RequireSigninView bool
|
||||||
|
DisableRegistration bool
|
||||||
|
EnableRegistrationCaptcha bool
|
||||||
|
|
||||||
|
EnableReverseProxyAuthentication bool
|
||||||
|
EnableReverseProxyAutoRegistration bool
|
||||||
|
ReverseProxyAuthenticationHeader string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authentication settings
|
||||||
|
var Auth AuthOpts
|
||||||
|
|
||||||
type ServerOpts struct {
|
type ServerOpts struct {
|
||||||
ExternalURL string `ini:"EXTERNAL_URL"`
|
ExternalURL string `ini:"EXTERNAL_URL"`
|
||||||
Domain string
|
Domain string
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/email"
|
"gogs.io/gogs/internal/email"
|
||||||
"gogs.io/gogs/internal/markup"
|
"gogs.io/gogs/internal/markup"
|
||||||
|
"gogs.io/gogs/internal/userutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (issue *Issue) MailSubject() string {
|
func (issue *Issue) MailSubject() string {
|
||||||
|
@ -36,12 +37,14 @@ func (this mailerUser) Email() string {
|
||||||
return this.user.Email
|
return this.user.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this mailerUser) GenerateActivateCode() string {
|
|
||||||
return this.user.GenerateActivateCode()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this mailerUser) GenerateEmailActivateCode(email string) string {
|
func (this mailerUser) GenerateEmailActivateCode(email string) string {
|
||||||
return this.user.GenerateEmailActivateCode(email)
|
return userutil.GenerateActivateCode(
|
||||||
|
this.user.ID,
|
||||||
|
email,
|
||||||
|
this.user.Name,
|
||||||
|
this.user.Passwd,
|
||||||
|
this.user.Rands,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMailerUser(u *User) email.User {
|
func NewMailerUser(u *User) email.User {
|
||||||
|
|
|
@ -177,7 +177,7 @@ func GetOrgByName(name string) (*User, error) {
|
||||||
}
|
}
|
||||||
u := &User{
|
u := &User{
|
||||||
LowerName: strings.ToLower(name),
|
LowerName: strings.ToLower(name),
|
||||||
Type: UserOrganization,
|
Type: UserTypeOrganization,
|
||||||
}
|
}
|
||||||
has, err := x.Get(u)
|
has, err := x.Get(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1157,8 +1157,8 @@ func (err ErrReachLimitOfRepo) Error() string {
|
||||||
|
|
||||||
// CreateRepository creates a repository for given user or organization.
|
// CreateRepository creates a repository for given user or organization.
|
||||||
func CreateRepository(doer, owner *User, opts CreateRepoOptionsLegacy) (_ *Repository, err error) {
|
func CreateRepository(doer, owner *User, opts CreateRepoOptionsLegacy) (_ *Repository, err error) {
|
||||||
if !owner.CanCreateRepo() {
|
if !owner.canCreateRepo() {
|
||||||
return nil, ErrReachLimitOfRepo{Limit: owner.RepoCreationNum()}
|
return nil, ErrReachLimitOfRepo{Limit: owner.maxNumRepos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := &Repository{
|
repo := &Repository{
|
||||||
|
@ -2467,8 +2467,8 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool, error) {
|
||||||
|
|
||||||
// ForkRepository creates a fork of target repository under another user domain.
|
// ForkRepository creates a fork of target repository under another user domain.
|
||||||
func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string) (_ *Repository, err error) {
|
func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string) (_ *Repository, err error) {
|
||||||
if !owner.CanCreateRepo() {
|
if !owner.canCreateRepo() {
|
||||||
return nil, ErrReachLimitOfRepo{Limit: owner.RepoCreationNum()}
|
return nil, ErrReachLimitOfRepo{Limit: owner.maxNumRepos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := &Repository{
|
repo := &Repository{
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
|
||||||
"github.com/gogs/git-module"
|
"github.com/gogs/git-module"
|
||||||
api "github.com/gogs/go-gogs-client"
|
|
||||||
|
|
||||||
"gogs.io/gogs/internal/avatar"
|
"gogs.io/gogs/internal/avatar"
|
||||||
"gogs.io/gogs/internal/conf"
|
"gogs.io/gogs/internal/conf"
|
||||||
|
@ -37,77 +36,13 @@ import (
|
||||||
"gogs.io/gogs/internal/tool"
|
"gogs.io/gogs/internal/tool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// USER_AVATAR_URL_PREFIX is used to identify a URL is to access user avatar.
|
// TODO(unknwon): Delete me once refactoring is done.
|
||||||
const USER_AVATAR_URL_PREFIX = "avatars"
|
|
||||||
|
|
||||||
type UserType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
UserIndividual UserType = iota // Historic reason to make it starts at 0.
|
|
||||||
UserOrganization
|
|
||||||
)
|
|
||||||
|
|
||||||
// User represents the object of individual and member of organization.
|
|
||||||
type User struct {
|
|
||||||
ID int64 `gorm:"primaryKey"`
|
|
||||||
LowerName string `xorm:"UNIQUE NOT NULL" gorm:"unique;not null"`
|
|
||||||
Name string `xorm:"UNIQUE NOT NULL" gorm:"not null"`
|
|
||||||
FullName string
|
|
||||||
// Email is the primary email address (to be used for communication)
|
|
||||||
Email string `xorm:"NOT NULL" gorm:"not null"`
|
|
||||||
Passwd string `xorm:"NOT NULL" gorm:"not null"`
|
|
||||||
LoginSource int64 `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
|
|
||||||
LoginName string
|
|
||||||
Type UserType
|
|
||||||
OwnedOrgs []*User `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
Orgs []*User `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
Repos []*Repository `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
Location string
|
|
||||||
Website string
|
|
||||||
Rands string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
|
|
||||||
Salt string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
|
|
||||||
|
|
||||||
Created time.Time `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
CreatedUnix int64
|
|
||||||
Updated time.Time `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
UpdatedUnix int64
|
|
||||||
|
|
||||||
// Remember visibility choice for convenience, true for private
|
|
||||||
LastRepoVisibility bool
|
|
||||||
// Maximum repository creation limit, -1 means use global default
|
|
||||||
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1" gorm:"not null;default:-1"`
|
|
||||||
|
|
||||||
// Permissions
|
|
||||||
IsActive bool // Activate primary email
|
|
||||||
IsAdmin bool
|
|
||||||
AllowGitHook bool
|
|
||||||
AllowImportLocal bool // Allow migrate repository by local path
|
|
||||||
ProhibitLogin bool
|
|
||||||
|
|
||||||
// Avatar
|
|
||||||
Avatar string `xorm:"VARCHAR(2048) NOT NULL" gorm:"type:VARCHAR(2048);not null"`
|
|
||||||
AvatarEmail string `xorm:"NOT NULL" gorm:"not null"`
|
|
||||||
UseCustomAvatar bool
|
|
||||||
|
|
||||||
// Counters
|
|
||||||
NumFollowers int
|
|
||||||
NumFollowing int `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
|
|
||||||
NumStars int
|
|
||||||
NumRepos int
|
|
||||||
|
|
||||||
// For organization
|
|
||||||
Description string
|
|
||||||
NumTeams int
|
|
||||||
NumMembers int
|
|
||||||
Teams []*Team `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
Members []*User `xorm:"-" gorm:"-" json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) BeforeInsert() {
|
func (u *User) BeforeInsert() {
|
||||||
u.CreatedUnix = time.Now().Unix()
|
u.CreatedUnix = time.Now().Unix()
|
||||||
u.UpdatedUnix = u.CreatedUnix
|
u.UpdatedUnix = u.CreatedUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(unknwon): Refactoring together with methods that do updates.
|
||||||
func (u *User) BeforeUpdate() {
|
func (u *User) BeforeUpdate() {
|
||||||
if u.MaxRepoCreation < -1 {
|
if u.MaxRepoCreation < -1 {
|
||||||
u.MaxRepoCreation = -1
|
u.MaxRepoCreation = -1
|
||||||
|
@ -115,6 +50,7 @@ func (u *User) BeforeUpdate() {
|
||||||
u.UpdatedUnix = time.Now().Unix()
|
u.UpdatedUnix = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(unknwon): Delete me once refactoring is done.
|
||||||
func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||||
switch colName {
|
switch colName {
|
||||||
case "created_unix":
|
case "created_unix":
|
||||||
|
@ -124,81 +60,6 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) APIFormat() *api.User {
|
|
||||||
return &api.User{
|
|
||||||
ID: u.ID,
|
|
||||||
UserName: u.Name,
|
|
||||||
Login: u.Name,
|
|
||||||
FullName: u.FullName,
|
|
||||||
Email: u.Email,
|
|
||||||
AvatarUrl: u.AvatarLink(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) RepoCreationNum() int {
|
|
||||||
if u.MaxRepoCreation <= -1 {
|
|
||||||
return conf.Repository.MaxCreationLimit
|
|
||||||
}
|
|
||||||
return u.MaxRepoCreation
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) CanCreateRepo() bool {
|
|
||||||
if u.MaxRepoCreation <= -1 {
|
|
||||||
if conf.Repository.MaxCreationLimit <= -1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return u.NumRepos < conf.Repository.MaxCreationLimit
|
|
||||||
}
|
|
||||||
return u.NumRepos < u.MaxRepoCreation
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) CanCreateOrganization() bool {
|
|
||||||
return !conf.Admin.DisableRegularOrgCreation || u.IsAdmin
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 conf.Repository.EnableLocalPathMigration && (u.IsAdmin || u.AllowImportLocal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DashboardLink returns the user dashboard page link.
|
|
||||||
func (u *User) DashboardLink() string {
|
|
||||||
if u.IsOrganization() {
|
|
||||||
return conf.Server.Subpath + "/org/" + u.Name + "/dashboard/"
|
|
||||||
}
|
|
||||||
return conf.Server.Subpath + "/"
|
|
||||||
}
|
|
||||||
|
|
||||||
// HomeLink returns the user or organization home page link.
|
|
||||||
func (u *User) HomeLink() string {
|
|
||||||
return conf.Server.Subpath + "/" + u.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) HTMLURL() string {
|
|
||||||
return conf.Server.ExternalURL + u.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateEmailActivateCode generates an activate code based on user information and given e-mail.
|
|
||||||
func (u *User) GenerateEmailActivateCode(email string) string {
|
|
||||||
code := tool.CreateTimeLimitCode(
|
|
||||||
com.ToStr(u.ID)+email+u.LowerName+u.Passwd+u.Rands,
|
|
||||||
conf.Auth.ActivateCodeLives, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CustomAvatarPath returns user custom avatar file path.
|
// CustomAvatarPath returns user custom avatar file path.
|
||||||
func (u *User) CustomAvatarPath() string {
|
func (u *User) CustomAvatarPath() string {
|
||||||
return filepath.Join(conf.Picture.AvatarUploadPath, com.ToStr(u.ID))
|
return filepath.Join(conf.Picture.AvatarUploadPath, com.ToStr(u.ID))
|
||||||
|
@ -246,7 +107,7 @@ func (u *User) RelAvatarLink() string {
|
||||||
if !com.IsExist(u.CustomAvatarPath()) {
|
if !com.IsExist(u.CustomAvatarPath()) {
|
||||||
return defaultImgUrl
|
return defaultImgUrl
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, USER_AVATAR_URL_PREFIX, u.ID)
|
return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, conf.UsersAvatarURLPath, u.ID)
|
||||||
case conf.Picture.DisableGravatar:
|
case conf.Picture.DisableGravatar:
|
||||||
if !com.IsExist(u.CustomAvatarPath()) {
|
if !com.IsExist(u.CustomAvatarPath()) {
|
||||||
if err := u.GenerateRandomAvatar(); err != nil {
|
if err := u.GenerateRandomAvatar(); err != nil {
|
||||||
|
@ -254,7 +115,7 @@ func (u *User) RelAvatarLink() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, USER_AVATAR_URL_PREFIX, u.ID)
|
return fmt.Sprintf("%s/%s/%d", conf.Server.Subpath, conf.UsersAvatarURLPath, u.ID)
|
||||||
}
|
}
|
||||||
return tool.AvatarLink(u.AvatarEmail)
|
return tool.AvatarLink(u.AvatarEmail)
|
||||||
}
|
}
|
||||||
|
@ -374,7 +235,7 @@ func (u *User) IsWriterOfRepo(repo *Repository) bool {
|
||||||
|
|
||||||
// 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 == UserOrganization
|
return u.Type == UserTypeOrganization
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
// IsUserOrgOwner returns true if user is in the owner team of given organization.
|
||||||
|
@ -434,7 +295,7 @@ func (u *User) GetOrganizations(showPrivate bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Orgs = make([]*User, 0, len(orgIDs))
|
u.Orgs = make([]*User, 0, len(orgIDs))
|
||||||
if err = x.Where("type = ?", UserOrganization).In("id", orgIDs).Find(&u.Orgs); err != nil {
|
if err = x.Where("type = ?", UserTypeOrganization).In("id", orgIDs).Find(&u.Orgs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -69,7 +69,7 @@ func isEmailUsed(e Engine, email string) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to check primary email of users as well.
|
// We need to check primary email of users as well.
|
||||||
return e.Where("type=?", UserIndividual).And("email=?", email).Get(new(User))
|
return e.Where("type=?", UserTypeIndividual).And("email=?", email).Get(new(User))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmailUsed returns true if the email has been used.
|
// IsEmailUsed returns true if the email has been used.
|
||||||
|
|
|
@ -11,10 +11,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-macaron/binding"
|
"github.com/go-macaron/binding"
|
||||||
|
api "github.com/gogs/go-gogs-client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"gogs.io/gogs/internal/auth"
|
"gogs.io/gogs/internal/auth"
|
||||||
|
"gogs.io/gogs/internal/conf"
|
||||||
"gogs.io/gogs/internal/cryptoutil"
|
"gogs.io/gogs/internal/cryptoutil"
|
||||||
"gogs.io/gogs/internal/errutil"
|
"gogs.io/gogs/internal/errutil"
|
||||||
)
|
)
|
||||||
|
@ -54,27 +56,6 @@ type UsersStore interface {
|
||||||
|
|
||||||
var Users UsersStore
|
var Users UsersStore
|
||||||
|
|
||||||
// BeforeCreate implements the GORM create hook.
|
|
||||||
func (u *User) BeforeCreate(tx *gorm.DB) error {
|
|
||||||
if u.CreatedUnix == 0 {
|
|
||||||
u.CreatedUnix = tx.NowFunc().Unix()
|
|
||||||
u.UpdatedUnix = u.CreatedUnix
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterFind implements the GORM query hook.
|
|
||||||
func (u *User) AfterFind(_ *gorm.DB) error {
|
|
||||||
u.Created = time.Unix(u.CreatedUnix, 0).Local()
|
|
||||||
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLocal returns true if user is created as local account.
|
|
||||||
func (u *User) IsLocal() bool {
|
|
||||||
return u.LoginSource <= 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ UsersStore = (*users)(nil)
|
var _ UsersStore = (*users)(nil)
|
||||||
|
|
||||||
type users struct {
|
type users struct {
|
||||||
|
@ -303,7 +284,7 @@ func (db *users) GetByEmail(ctx context.Context, email string) (*User, error) {
|
||||||
// First try to find the user by primary email
|
// First try to find the user by primary email
|
||||||
user := new(User)
|
user := new(User)
|
||||||
err := db.WithContext(ctx).
|
err := db.WithContext(ctx).
|
||||||
Where("email = ? AND type = ? AND is_active = ?", email, UserIndividual, true).
|
Where("email = ? AND type = ? AND is_active = ?", email, UserTypeIndividual, true).
|
||||||
First(user).
|
First(user).
|
||||||
Error
|
Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -357,3 +338,147 @@ func (db *users) HasForkedRepository(ctx context.Context, userID, repoID int64)
|
||||||
db.WithContext(ctx).Model(new(Repository)).Where("owner_id = ? AND fork_id = ?", userID, repoID).Count(&count)
|
db.WithContext(ctx).Model(new(Repository)).Where("owner_id = ? AND fork_id = ?", userID, repoID).Count(&count)
|
||||||
return count > 0
|
return count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserType indicates the type of the user account.
|
||||||
|
type UserType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserTypeIndividual UserType = iota // NOTE: Historic reason to make it starts at 0.
|
||||||
|
UserTypeOrganization
|
||||||
|
)
|
||||||
|
|
||||||
|
// User represents the object of an individual or an organization.
|
||||||
|
type User struct {
|
||||||
|
ID int64 `gorm:"primaryKey"`
|
||||||
|
LowerName string `xorm:"UNIQUE NOT NULL" gorm:"unique;not null"`
|
||||||
|
Name string `xorm:"UNIQUE NOT NULL" gorm:"not null"`
|
||||||
|
FullName string
|
||||||
|
// Email is the primary email address (to be used for communication)
|
||||||
|
Email string `xorm:"NOT NULL" gorm:"not null"`
|
||||||
|
Passwd string `xorm:"NOT NULL" gorm:"not null"`
|
||||||
|
LoginSource int64 `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
|
||||||
|
LoginName string
|
||||||
|
Type UserType
|
||||||
|
OwnedOrgs []*User `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
Orgs []*User `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
Repos []*Repository `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
Location string
|
||||||
|
Website string
|
||||||
|
Rands string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
|
||||||
|
Salt string `xorm:"VARCHAR(10)" gorm:"type:VARCHAR(10)"`
|
||||||
|
|
||||||
|
Created time.Time `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
CreatedUnix int64
|
||||||
|
Updated time.Time `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
UpdatedUnix int64
|
||||||
|
|
||||||
|
// Remember visibility choice for convenience, true for private
|
||||||
|
LastRepoVisibility bool
|
||||||
|
// Maximum repository creation limit, -1 means use global default
|
||||||
|
MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1" gorm:"not null;default:-1"`
|
||||||
|
|
||||||
|
// Permissions
|
||||||
|
IsActive bool // Activate primary email
|
||||||
|
IsAdmin bool
|
||||||
|
AllowGitHook bool
|
||||||
|
AllowImportLocal bool // Allow migrate repository by local path
|
||||||
|
ProhibitLogin bool
|
||||||
|
|
||||||
|
// Avatar
|
||||||
|
Avatar string `xorm:"VARCHAR(2048) NOT NULL" gorm:"type:VARCHAR(2048);not null"`
|
||||||
|
AvatarEmail string `xorm:"NOT NULL" gorm:"not null"`
|
||||||
|
UseCustomAvatar bool
|
||||||
|
|
||||||
|
// Counters
|
||||||
|
NumFollowers int
|
||||||
|
NumFollowing int `xorm:"NOT NULL DEFAULT 0" gorm:"not null;default:0"`
|
||||||
|
NumStars int
|
||||||
|
NumRepos int
|
||||||
|
|
||||||
|
// For organization
|
||||||
|
Description string
|
||||||
|
NumTeams int
|
||||||
|
NumMembers int
|
||||||
|
Teams []*Team `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
Members []*User `xorm:"-" gorm:"-" json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeCreate implements the GORM create hook.
|
||||||
|
func (u *User) BeforeCreate(tx *gorm.DB) error {
|
||||||
|
if u.CreatedUnix == 0 {
|
||||||
|
u.CreatedUnix = tx.NowFunc().Unix()
|
||||||
|
u.UpdatedUnix = u.CreatedUnix
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterFind implements the GORM query hook.
|
||||||
|
func (u *User) AfterFind(_ *gorm.DB) error {
|
||||||
|
u.Created = time.Unix(u.CreatedUnix, 0).Local()
|
||||||
|
u.Updated = time.Unix(u.UpdatedUnix, 0).Local()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLocal returns true if user is created as local account.
|
||||||
|
func (u *User) IsLocal() bool {
|
||||||
|
return u.LoginSource <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIFormat returns the API format of a user.
|
||||||
|
func (u *User) APIFormat() *api.User {
|
||||||
|
return &api.User{
|
||||||
|
ID: u.ID,
|
||||||
|
UserName: u.Name,
|
||||||
|
Login: u.Name,
|
||||||
|
FullName: u.FullName,
|
||||||
|
Email: u.Email,
|
||||||
|
AvatarUrl: u.AvatarLink(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxNumRepos returns the maximum number of repositories that the user can have
|
||||||
|
// direct ownership.
|
||||||
|
func (u *User) maxNumRepos() int {
|
||||||
|
if u.MaxRepoCreation <= -1 {
|
||||||
|
return conf.Repository.MaxCreationLimit
|
||||||
|
}
|
||||||
|
return u.MaxRepoCreation
|
||||||
|
}
|
||||||
|
|
||||||
|
// canCreateRepo returns true if the user can create a repository.
|
||||||
|
func (u *User) canCreateRepo() bool {
|
||||||
|
return u.maxNumRepos() <= -1 || u.NumRepos < u.maxNumRepos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanCreateOrganization returns true if user can create organizations.
|
||||||
|
func (u *User) CanCreateOrganization() bool {
|
||||||
|
return !conf.Admin.DisableRegularOrgCreation || u.IsAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 repositories by local path.
|
||||||
|
func (u *User) CanImportLocal() bool {
|
||||||
|
return conf.Repository.EnableLocalPathMigration && (u.IsAdmin || u.AllowImportLocal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HomeURLPath returns the URL path to the user or organization home page.
|
||||||
|
//
|
||||||
|
// TODO(unknwon): This is also used in templates, which should be fixed by
|
||||||
|
// having a dedicated type `template.User` and move this to the "userutil"
|
||||||
|
// package.
|
||||||
|
func (u *User) HomeURLPath() string {
|
||||||
|
return conf.Server.Subpath + "/" + u.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLURL returns the HTML URL to the user or organization home page.
|
||||||
|
//
|
||||||
|
// TODO(unknwon): This is also used in templates, which should be fixed by
|
||||||
|
// having a dedicated type `template.User` and move this to the "userutil"
|
||||||
|
// package.
|
||||||
|
func (u *User) HTMLURL() string {
|
||||||
|
return conf.Server.ExternalURL + u.Name
|
||||||
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ func usersGetByEmail(t *testing.T, db *users) {
|
||||||
org, err := db.Create(ctx, "gogs", "gogs@exmaple.com", CreateUserOptions{})
|
org, err := db.Create(ctx, "gogs", "gogs@exmaple.com", CreateUserOptions{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserOrganization).Error
|
err = db.Model(&User{}).Where("id", org.ID).UpdateColumn("type", UserTypeOrganization).Error
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = db.GetByEmail(ctx, org.Email)
|
_, err = db.GetByEmail(ctx, org.Email)
|
||||||
|
|
|
@ -86,7 +86,6 @@ type User interface {
|
||||||
ID() int64
|
ID() int64
|
||||||
DisplayName() string
|
DisplayName() string
|
||||||
Email() string
|
Email() string
|
||||||
GenerateActivateCode() string
|
|
||||||
GenerateEmailActivateCode(string) string
|
GenerateEmailActivateCode(string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +121,11 @@ func SendUserMail(_ *macaron.Context, u User, tpl, code, subject, info string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendActivateAccountMail(c *macaron.Context, u User) {
|
func SendActivateAccountMail(c *macaron.Context, u User) {
|
||||||
SendUserMail(c, u, MAIL_AUTH_ACTIVATE, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account")
|
SendUserMail(c, u, MAIL_AUTH_ACTIVATE, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.activate_account"), "activate account")
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendResetPasswordMail(c *macaron.Context, u User) {
|
func SendResetPasswordMail(c *macaron.Context, u User) {
|
||||||
SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password")
|
SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, u.GenerateEmailActivateCode(u.Email()), c.Tr("mail.reset_password"), "reset password")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendActivateAccountMail sends confirmation email.
|
// SendActivateAccountMail sends confirmation email.
|
||||||
|
|
|
@ -21,7 +21,7 @@ func Organizations(c *context.Context) {
|
||||||
c.Data["PageIsAdminOrganizations"] = true
|
c.Data["PageIsAdminOrganizations"] = true
|
||||||
|
|
||||||
route.RenderUserSearch(c, &route.UserSearchOptions{
|
route.RenderUserSearch(c, &route.UserSearchOptions{
|
||||||
Type: db.UserOrganization,
|
Type: db.UserTypeOrganization,
|
||||||
Counter: db.CountOrganizations,
|
Counter: db.CountOrganizations,
|
||||||
Ranger: db.Organizations,
|
Ranger: db.Organizations,
|
||||||
PageSize: conf.UI.Admin.OrgPagingNum,
|
PageSize: conf.UI.Admin.OrgPagingNum,
|
||||||
|
|
|
@ -30,7 +30,7 @@ func Users(c *context.Context) {
|
||||||
c.Data["PageIsAdminUsers"] = true
|
c.Data["PageIsAdminUsers"] = true
|
||||||
|
|
||||||
route.RenderUserSearch(c, &route.UserSearchOptions{
|
route.RenderUserSearch(c, &route.UserSearchOptions{
|
||||||
Type: db.UserIndividual,
|
Type: db.UserTypeIndividual,
|
||||||
Counter: db.CountUsers,
|
Counter: db.CountUsers,
|
||||||
Ranger: db.ListUsers,
|
Ranger: db.ListUsers,
|
||||||
PageSize: conf.UI.Admin.UserPagingNum,
|
PageSize: conf.UI.Admin.UserPagingNum,
|
||||||
|
|
|
@ -27,7 +27,7 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
|
||||||
Website: apiForm.Website,
|
Website: apiForm.Website,
|
||||||
Location: apiForm.Location,
|
Location: apiForm.Location,
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
Type: db.UserOrganization,
|
Type: db.UserTypeOrganization,
|
||||||
}
|
}
|
||||||
if err := db.CreateOrganization(org, user); err != nil {
|
if err := db.CreateOrganization(org, user); err != nil {
|
||||||
if db.IsErrUserAlreadyExist(err) ||
|
if db.IsErrUserAlreadyExist(err) ||
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
func Search(c *context.APIContext) {
|
func Search(c *context.APIContext) {
|
||||||
opts := &db.SearchUserOptions{
|
opts := &db.SearchUserOptions{
|
||||||
Keyword: c.Query("q"),
|
Keyword: c.Query("q"),
|
||||||
Type: db.UserIndividual,
|
Type: db.UserTypeIndividual,
|
||||||
PageSize: com.StrTo(c.Query("limit")).MustInt(),
|
PageSize: com.StrTo(c.Query("limit")).MustInt(),
|
||||||
}
|
}
|
||||||
if opts.PageSize == 0 {
|
if opts.PageSize == 0 {
|
||||||
|
|
|
@ -138,7 +138,7 @@ func ExploreUsers(c *context.Context) {
|
||||||
c.Data["PageIsExploreUsers"] = true
|
c.Data["PageIsExploreUsers"] = true
|
||||||
|
|
||||||
RenderUserSearch(c, &UserSearchOptions{
|
RenderUserSearch(c, &UserSearchOptions{
|
||||||
Type: db.UserIndividual,
|
Type: db.UserTypeIndividual,
|
||||||
Counter: db.CountUsers,
|
Counter: db.CountUsers,
|
||||||
Ranger: db.ListUsers,
|
Ranger: db.ListUsers,
|
||||||
PageSize: conf.UI.ExplorePagingNum,
|
PageSize: conf.UI.ExplorePagingNum,
|
||||||
|
@ -153,7 +153,7 @@ func ExploreOrganizations(c *context.Context) {
|
||||||
c.Data["PageIsExploreOrganizations"] = true
|
c.Data["PageIsExploreOrganizations"] = true
|
||||||
|
|
||||||
RenderUserSearch(c, &UserSearchOptions{
|
RenderUserSearch(c, &UserSearchOptions{
|
||||||
Type: db.UserOrganization,
|
Type: db.UserTypeOrganization,
|
||||||
Counter: db.CountOrganizations,
|
Counter: db.CountOrganizations,
|
||||||
Ranger: db.Organizations,
|
Ranger: db.Organizations,
|
||||||
PageSize: conf.UI.ExplorePagingNum,
|
PageSize: conf.UI.ExplorePagingNum,
|
||||||
|
|
|
@ -32,7 +32,7 @@ func CreatePost(c *context.Context, f form.CreateOrg) {
|
||||||
org := &db.User{
|
org := &db.User{
|
||||||
Name: f.OrgName,
|
Name: f.OrgName,
|
||||||
IsActive: true,
|
IsActive: true,
|
||||||
Type: db.UserOrganization,
|
Type: db.UserTypeOrganization,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.CreateOrganization(org, c.User); err != nil {
|
if err := db.CreateOrganization(org, c.User); err != nil {
|
||||||
|
|
|
@ -135,7 +135,7 @@ func ForkPost(c *context.Context, f form.CreateRepo) {
|
||||||
c.Data["Err_RepoName"] = true
|
c.Data["Err_RepoName"] = true
|
||||||
switch {
|
switch {
|
||||||
case db.IsErrReachLimitOfRepo(err):
|
case db.IsErrReachLimitOfRepo(err):
|
||||||
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", c.User.RepoCreationNum()), FORK, &f)
|
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(db.ErrReachLimitOfRepo).Limit), FORK, &f)
|
||||||
case db.IsErrRepoAlreadyExist(err):
|
case db.IsErrRepoAlreadyExist(err):
|
||||||
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), FORK, &f)
|
c.RenderWithErr(c.Tr("repo.settings.new_owner_has_same_repo"), FORK, &f)
|
||||||
case db.IsErrNameNotAllowed(err):
|
case db.IsErrNameNotAllowed(err):
|
||||||
|
|
|
@ -86,10 +86,10 @@ func Create(c *context.Context) {
|
||||||
c.Success(CREATE)
|
c.Success(CREATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleCreateError(c *context.Context, owner *db.User, err error, name, tpl string, form interface{}) {
|
func handleCreateError(c *context.Context, err error, name, tpl string, form interface{}) {
|
||||||
switch {
|
switch {
|
||||||
case db.IsErrReachLimitOfRepo(err):
|
case db.IsErrReachLimitOfRepo(err):
|
||||||
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", owner.RepoCreationNum()), tpl, form)
|
c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", err.(db.ErrReachLimitOfRepo).Limit), tpl, form)
|
||||||
case db.IsErrRepoAlreadyExist(err):
|
case db.IsErrRepoAlreadyExist(err):
|
||||||
c.Data["Err_RepoName"] = true
|
c.Data["Err_RepoName"] = true
|
||||||
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
|
c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
|
||||||
|
@ -141,7 +141,7 @@ func CreatePost(c *context.Context, f form.CreateRepo) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreateError(c, ctxUser, err, "CreatePost", CREATE, &f)
|
handleCreateError(c, err, "CreatePost", CREATE, &f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Migrate(c *context.Context) {
|
func Migrate(c *context.Context) {
|
||||||
|
@ -227,7 +227,7 @@ func MigratePost(c *context.Context, f form.MigrateRepo) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreateError(c, ctxUser, err, "MigratePost", MIGRATE, &f)
|
handleCreateError(c, err, "MigratePost", MIGRATE, &f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Action(c *context.Context) {
|
func Action(c *context.Context) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"gogs.io/gogs/internal/form"
|
"gogs.io/gogs/internal/form"
|
||||||
"gogs.io/gogs/internal/osutil"
|
"gogs.io/gogs/internal/osutil"
|
||||||
"gogs.io/gogs/internal/tool"
|
"gogs.io/gogs/internal/tool"
|
||||||
|
"gogs.io/gogs/internal/userutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -269,7 +270,7 @@ func SettingsPost(c *context.Context, f form.RepoSetting) {
|
||||||
log.Trace("Repository deleted: %s/%s", c.Repo.Owner.Name, repo.Name)
|
log.Trace("Repository deleted: %s/%s", c.Repo.Owner.Name, repo.Name)
|
||||||
|
|
||||||
c.Flash.Success(c.Tr("repo.settings.deletion_success"))
|
c.Flash.Success(c.Tr("repo.settings.deletion_success"))
|
||||||
c.Redirect(c.Repo.Owner.DashboardLink())
|
c.Redirect(userutil.DashboardURLPath(c.Repo.Owner.Name, c.Repo.Owner.IsOrganization()))
|
||||||
|
|
||||||
case "delete-wiki":
|
case "delete-wiki":
|
||||||
if !c.Repo.IsOwner() {
|
if !c.Repo.IsOwner() {
|
||||||
|
|
|
@ -118,7 +118,7 @@ func Action(c *context.Context, puser *context.ParamsUser) {
|
||||||
|
|
||||||
redirectTo := c.Query("redirect_to")
|
redirectTo := c.Query("redirect_to")
|
||||||
if !tool.IsSameSiteURLPath(redirectTo) {
|
if !tool.IsSameSiteURLPath(redirectTo) {
|
||||||
redirectTo = puser.HomeLink()
|
redirectTo = puser.HomeURLPath()
|
||||||
}
|
}
|
||||||
c.Redirect(redirectTo)
|
c.Redirect(redirectTo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2022 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 userutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gogs.io/gogs/internal/conf"
|
||||||
|
"gogs.io/gogs/internal/tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DashboardURLPath returns the URL path to the user or organization dashboard.
|
||||||
|
func DashboardURLPath(name string, isOrganization bool) string {
|
||||||
|
if isOrganization {
|
||||||
|
return conf.Server.Subpath + "/org/" + name + "/dashboard/"
|
||||||
|
}
|
||||||
|
return conf.Server.Subpath + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateActivateCode generates an activate code based on user information and
|
||||||
|
// the given email.
|
||||||
|
func GenerateActivateCode(id int64, email, name, password, rands string) string {
|
||||||
|
code := tool.CreateTimeLimitCode(
|
||||||
|
fmt.Sprintf("%d%s%s%s%s", id, email, strings.ToLower(name), password, rands),
|
||||||
|
conf.Auth.ActivateCodeLives,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add tailing hex username
|
||||||
|
code += hex.EncodeToString([]byte(strings.ToLower(name)))
|
||||||
|
return code
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2022 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 userutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"gogs.io/gogs/internal/conf"
|
||||||
|
"gogs.io/gogs/internal/tool"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDashboardURLPath(t *testing.T) {
|
||||||
|
t.Run("user", func(t *testing.T) {
|
||||||
|
got := DashboardURLPath("alice", false)
|
||||||
|
want := "/"
|
||||||
|
assert.Equal(t, want, got)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("organization", func(t *testing.T) {
|
||||||
|
got := DashboardURLPath("acme", true)
|
||||||
|
want := "/org/acme/dashboard/"
|
||||||
|
assert.Equal(t, want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateActivateCode(t *testing.T) {
|
||||||
|
conf.SetMockAuth(t,
|
||||||
|
conf.AuthOpts{
|
||||||
|
ActivateCodeLives: 10,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
code := GenerateActivateCode(1, "alice@example.com", "Alice", "123456", "rands")
|
||||||
|
got := tool.VerifyTimeLimitCode("1alice@example.comalice123456rands", conf.Auth.ActivateCodeLives, code[:tool.TIME_LIMIT_CODE_LENGTH])
|
||||||
|
assert.True(t, got)
|
||||||
|
}
|
|
@ -48,7 +48,7 @@
|
||||||
{{/*
|
{{/*
|
||||||
<li>
|
<li>
|
||||||
<i class="octicon octicon-star"></i>
|
<i class="octicon octicon-star"></i>
|
||||||
<a href="{{.Owner.HomeLink}}/stars">
|
<a href="{{.Owner.HomeURLPath}}/stars">
|
||||||
{{.Owner.NumStars}} {{.i18n.Tr "user.starred"}}
|
{{.Owner.NumStars}} {{.i18n.Tr "user.starred"}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
{{if .Orgs}}
|
{{if .Orgs}}
|
||||||
<li>
|
<li>
|
||||||
{{range .Orgs}}
|
{{range .Orgs}}
|
||||||
<a href="{{.HomeLink}}"><img class="ui mini image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a>
|
<a href="{{.HomeURLPath}}"><img class="ui mini image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
Loading…
Reference in New Issue