refactor(db): migrate `UpdateUser` off `user.go` (#7267)

pull/7268/head
Joe Chen 2022-11-27 19:36:10 +08:00 committed by GitHub
parent 44333afd20
commit ae20d03aec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 350 additions and 234 deletions

View File

@ -27,6 +27,7 @@ All notable changes to Gogs are documented in this file.
### Fixed ### Fixed
- Unable to use LDAP authentication on ARM machines. [#6761](https://github.com/gogs/gogs/issues/6761) - Unable to use LDAP authentication on ARM machines. [#6761](https://github.com/gogs/gogs/issues/6761)
- Unable to choose "Lookup Avatar by mail" in user settings without deleting custom avatar. [#7267](https://github.com/gogs/gogs/pull/7267)
### Removed ### Removed

View File

@ -34,6 +34,7 @@ import (
"gogs.io/gogs/internal/avatar" "gogs.io/gogs/internal/avatar"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
dberrors "gogs.io/gogs/internal/db/errors" dberrors "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/dbutil"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/markup" "gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/osutil" "gogs.io/gogs/internal/osutil"
@ -1112,11 +1113,9 @@ func createRepository(e *xorm.Session, doer, owner *User, repo *Repository) (err
return err return err
} }
owner.NumRepos++ _, err = e.Exec(dbutil.Quote("UPDATE %s SET num_repos = num_repos + 1 WHERE id = ?", "user"), owner.ID)
// Remember visibility preference. if err != nil {
owner.LastRepoVisibility = repo.IsPrivate return errors.Wrap(err, "increase owned repository count")
if err = updateUser(e, owner); err != nil {
return fmt.Errorf("updateUser: %v", err)
} }
// Give access to all members in owner team. // Give access to all members in owner team.
@ -1222,8 +1221,17 @@ func CreateRepository(doer, owner *User, opts CreateRepoOptionsLegacy) (_ *Repos
return nil, fmt.Errorf("CreateRepository 'git update-server-info': %s", stderr) return nil, fmt.Errorf("CreateRepository 'git update-server-info': %s", stderr)
} }
} }
if err = sess.Commit(); err != nil {
return nil, err
}
return repo, sess.Commit() // Remember visibility preference
err = Users.Update(context.TODO(), owner.ID, UpdateUserOptions{LastRepoVisibility: &repo.IsPrivate})
if err != nil {
return nil, errors.Wrap(err, "update user")
}
return repo, nil
} }
func countRepositories(userID int64, private bool) int64 { func countRepositories(userID int64, private bool) int64 {
@ -2544,6 +2552,12 @@ func ForkRepository(doer, owner *User, baseRepo *Repository, name, desc string)
return nil, fmt.Errorf("Commit: %v", err) return nil, fmt.Errorf("Commit: %v", err)
} }
// Remember visibility preference
err = Users.Update(context.TODO(), owner.ID, UpdateUserOptions{LastRepoVisibility: &repo.IsPrivate})
if err != nil {
return nil, errors.Wrap(err, "update user")
}
if err = repo.UpdateSize(); err != nil { if err = repo.UpdateSize(); err != nil {
log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err) log.Error("UpdateSize [repo_id: %d]: %v", repo.ID, err)
} }

View File

@ -19,10 +19,7 @@ import (
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/repoutil" "gogs.io/gogs/internal/repoutil"
"gogs.io/gogs/internal/strutil"
"gogs.io/gogs/internal/tool"
"gogs.io/gogs/internal/userutil" "gogs.io/gogs/internal/userutil"
) )
@ -42,48 +39,6 @@ func (u *User) AfterSet(colName string, _ xorm.Cell) {
} }
} }
// TODO(unknwon): Update call sites to use refactored methods and delete this one.
func updateUser(e Engine, u *User) error {
// Organization does not need email
if !u.IsOrganization() {
u.Email = strings.ToLower(u.Email)
has, err := e.Where("id!=?", u.ID).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
if err != nil {
return err
} else if has {
return ErrEmailAlreadyUsed{args: errutil.Args{"email": u.Email}}
}
if u.AvatarEmail == "" {
u.AvatarEmail = u.Email
}
u.Avatar = tool.HashEmail(u.AvatarEmail)
}
u.LowerName = strings.ToLower(u.Name)
u.Location = strutil.Truncate(u.Location, 255)
u.Website = strutil.Truncate(u.Website, 255)
u.Description = strutil.Truncate(u.Description, 255)
_, err := e.ID(u.ID).AllCols().Update(u)
return err
}
// TODO(unknwon): Refactoring together with methods that do updates.
func (u *User) BeforeUpdate() {
if u.MaxRepoCreation < -1 {
u.MaxRepoCreation = -1
}
u.UpdatedUnix = time.Now().Unix()
}
// UpdateUser updates user's information.
//
// TODO(unknwon): Update call sites to use refactored methods and delete this one.
func UpdateUser(u *User) error {
return updateUser(x, u)
}
// deleteBeans deletes all given beans, beans should contain delete conditions. // deleteBeans deletes all given beans, beans should contain delete conditions.
func deleteBeans(e Engine, beans ...interface{}) (err error) { func deleteBeans(e Engine, beans ...interface{}) (err error) {
for i := range beans { for i := range beans {

View File

@ -11,7 +11,6 @@ import (
"gogs.io/gogs/internal/db/errors" "gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/errutil" "gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/userutil"
) )
// EmailAddresses is the list of all email addresses of a user. Can contain the // EmailAddresses is the list of all email addresses of a user. Can contain the
@ -120,28 +119,11 @@ func AddEmailAddresses(emails []*EmailAddress) error {
} }
func (email *EmailAddress) Activate() error { func (email *EmailAddress) Activate() error {
user, err := Users.GetByID(context.TODO(), email.UserID)
if err != nil {
return err
}
if user.Rands, err = userutil.RandomSalt(); err != nil {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
email.IsActivated = true email.IsActivated = true
if _, err := sess.ID(email.ID).AllCols().Update(email); err != nil { if _, err := x.ID(email.ID).AllCols().Update(email); err != nil {
return err
} else if err = updateUser(sess, user); err != nil {
return err return err
} }
return Users.Update(context.TODO(), email.UserID, UpdateUserOptions{GenerateNewRands: true})
return sess.Commit()
} }
func DeleteEmailAddress(email *EmailAddress) (err error) { func DeleteEmailAddress(email *EmailAddress) (err error) {

View File

@ -87,8 +87,7 @@ type UsersStore interface {
// Results are paginated by given page and page size, and sorted by the time of // Results are paginated by given page and page size, and sorted by the time of
// follow in descending order. // follow in descending order.
ListFollowings(ctx context.Context, userID int64, page, pageSize int) ([]*User, error) ListFollowings(ctx context.Context, userID int64, page, pageSize int) ([]*User, error)
// Update updates all fields for the given user, all values are persisted as-is // Update updates fields for the given user.
// (i.e. empty values would overwrite/wipe out existing values).
Update(ctx context.Context, userID int64, opts UpdateUserOptions) error Update(ctx context.Context, userID int64, opts UpdateUserOptions) error
// UseCustomAvatar uses the given avatar as the user custom avatar. // UseCustomAvatar uses the given avatar as the user custom avatar.
UseCustomAvatar(ctx context.Context, userID int64, avatar []byte) error UseCustomAvatar(ctx context.Context, userID int64, avatar []byte) error
@ -324,8 +323,10 @@ type ErrEmailAlreadyUsed struct {
args errutil.Args args errutil.Args
} }
// IsErrEmailAlreadyUsed returns true if the underlying error has the type
// ErrEmailAlreadyUsed.
func IsErrEmailAlreadyUsed(err error) bool { func IsErrEmailAlreadyUsed(err error) bool {
_, ok := err.(ErrEmailAlreadyUsed) _, ok := errors.Cause(err).(ErrEmailAlreadyUsed)
return ok return ok
} }
@ -415,8 +416,10 @@ type ErrUserNotExist struct {
args errutil.Args args errutil.Args
} }
// IsErrUserNotExist returns true if the underlying error has the type
// ErrUserNotExist.
func IsErrUserNotExist(err error) bool { func IsErrUserNotExist(err error) bool {
_, ok := err.(ErrUserNotExist) _, ok := errors.Cause(err).(ErrUserNotExist)
return ok return ok
} }
@ -549,30 +552,117 @@ func (db *users) ListFollowings(ctx context.Context, userID int64, page, pageSiz
} }
type UpdateUserOptions struct { type UpdateUserOptions struct {
FullName string LoginSource *int64
Website string LoginName *string
Location string
Description string
MaxRepoCreation int Password *string
// GenerateNewRands indicates whether to force generate new rands for the user.
GenerateNewRands bool
FullName *string
Email *string
Website *string
Location *string
Description *string
MaxRepoCreation *int
LastRepoVisibility *bool
IsActivated *bool
IsAdmin *bool
AllowGitHook *bool
AllowImportLocal *bool
ProhibitLogin *bool
Avatar *string
AvatarEmail *string
} }
func (db *users) Update(ctx context.Context, userID int64, opts UpdateUserOptions) error { func (db *users) Update(ctx context.Context, userID int64, opts UpdateUserOptions) error {
if opts.MaxRepoCreation < -1 { updates := map[string]any{
opts.MaxRepoCreation = -1
}
return db.WithContext(ctx).
Model(&User{}).
Where("id = ?", userID).
Updates(map[string]any{
"full_name": strutil.Truncate(opts.FullName, 255),
"website": strutil.Truncate(opts.Website, 255),
"location": strutil.Truncate(opts.Location, 255),
"description": strutil.Truncate(opts.Description, 255),
"max_repo_creation": opts.MaxRepoCreation,
"updated_unix": db.NowFunc().Unix(), "updated_unix": db.NowFunc().Unix(),
}). }
Error
if opts.LoginSource != nil {
updates["login_source"] = *opts.LoginSource
}
if opts.LoginName != nil {
updates["login_name"] = *opts.LoginName
}
if opts.Password != nil {
salt, err := userutil.RandomSalt()
if err != nil {
return errors.Wrap(err, "generate salt")
}
updates["salt"] = salt
updates["passwd"] = userutil.EncodePassword(*opts.Password, salt)
opts.GenerateNewRands = true
}
if opts.GenerateNewRands {
rands, err := userutil.RandomSalt()
if err != nil {
return errors.Wrap(err, "generate rands")
}
updates["rands"] = rands
}
if opts.FullName != nil {
updates["full_name"] = strutil.Truncate(*opts.FullName, 255)
}
if opts.Email != nil {
_, err := db.GetByEmail(ctx, *opts.Email)
if err == nil {
return ErrEmailAlreadyUsed{args: errutil.Args{"email": *opts.Email}}
} else if !IsErrUserNotExist(err) {
return errors.Wrap(err, "check email")
}
updates["email"] = *opts.Email
}
if opts.Website != nil {
updates["website"] = strutil.Truncate(*opts.Website, 255)
}
if opts.Location != nil {
updates["location"] = strutil.Truncate(*opts.Location, 255)
}
if opts.Description != nil {
updates["description"] = strutil.Truncate(*opts.Description, 255)
}
if opts.MaxRepoCreation != nil {
if *opts.MaxRepoCreation < -1 {
*opts.MaxRepoCreation = -1
}
updates["max_repo_creation"] = *opts.MaxRepoCreation
}
if opts.LastRepoVisibility != nil {
updates["last_repo_visibility"] = *opts.LastRepoVisibility
}
if opts.IsActivated != nil {
updates["is_active"] = *opts.IsActivated
}
if opts.IsAdmin != nil {
updates["is_admin"] = *opts.IsAdmin
}
if opts.AllowGitHook != nil {
updates["allow_git_hook"] = *opts.AllowGitHook
}
if opts.AllowImportLocal != nil {
updates["allow_import_local"] = *opts.AllowImportLocal
}
if opts.ProhibitLogin != nil {
updates["prohibit_login"] = *opts.ProhibitLogin
}
if opts.Avatar != nil {
updates["avatar"] = strutil.Truncate(*opts.Avatar, 2048)
}
if opts.AvatarEmail != nil {
updates["avatar_email"] = strutil.Truncate(*opts.AvatarEmail, 255)
}
return db.WithContext(ctx).Model(&User{}).Where("id = ?", userID).Updates(updates).Error
} }
func (db *users) UseCustomAvatar(ctx context.Context, userID int64, avatar []byte) error { func (db *users) UseCustomAvatar(ctx context.Context, userID int64, avatar []byte) error {

View File

@ -731,16 +731,69 @@ func usersListFollowings(t *testing.T, db *users) {
func usersUpdate(t *testing.T, db *users) { func usersUpdate(t *testing.T, db *users) {
ctx := context.Background() ctx := context.Background()
alice, err := db.Create(ctx, "alice", "alice@example.com", CreateUserOptions{}) const oldPassword = "Password"
alice, err := db.Create(
ctx,
"alice",
"alice@example.com",
CreateUserOptions{
FullName: "FullName",
Password: oldPassword,
LoginSource: 9,
LoginName: "LoginName",
Location: "Location",
Website: "Website",
Activated: false,
Admin: false,
},
)
require.NoError(t, err) require.NoError(t, err)
overLimitStr := strings.Repeat("a", 300) t.Run("update password", func(t *testing.T) {
got := userutil.ValidatePassword(alice.Password, alice.Salt, oldPassword)
require.True(t, got)
newPassword := "NewPassword"
err = db.Update(ctx, alice.ID, UpdateUserOptions{Password: &newPassword})
require.NoError(t, err)
alice, err = db.GetByID(ctx, alice.ID)
require.NoError(t, err)
got = userutil.ValidatePassword(alice.Password, alice.Salt, oldPassword)
assert.False(t, got, "Old password should stop working")
got = userutil.ValidatePassword(alice.Password, alice.Salt, newPassword)
assert.True(t, got, "New password should work")
})
t.Run("update email but already used", func(t *testing.T) {
// todo
})
loginSource := int64(1)
maxRepoCreation := 99
lastRepoVisibility := true
overLimitStr := strings.Repeat("a", 2050)
opts := UpdateUserOptions{ opts := UpdateUserOptions{
FullName: overLimitStr, LoginSource: &loginSource,
Website: overLimitStr, LoginName: &alice.Name,
Location: overLimitStr,
Description: overLimitStr, FullName: &overLimitStr,
MaxRepoCreation: 1, Website: &overLimitStr,
Location: &overLimitStr,
Description: &overLimitStr,
MaxRepoCreation: &maxRepoCreation,
LastRepoVisibility: &lastRepoVisibility,
IsActivated: &lastRepoVisibility,
IsAdmin: &lastRepoVisibility,
AllowGitHook: &lastRepoVisibility,
AllowImportLocal: &lastRepoVisibility,
ProhibitLogin: &lastRepoVisibility,
Avatar: &overLimitStr,
AvatarEmail: &overLimitStr,
} }
err = db.Update(ctx, alice.ID, opts) err = db.Update(ctx, alice.ID, opts)
require.NoError(t, err) require.NoError(t, err)
@ -748,28 +801,34 @@ func usersUpdate(t *testing.T, db *users) {
alice, err = db.GetByID(ctx, alice.ID) alice, err = db.GetByID(ctx, alice.ID)
require.NoError(t, err) require.NoError(t, err)
wantStr := strings.Repeat("a", 255) assertValues := func() {
assert.Equal(t, wantStr, alice.FullName) assert.Equal(t, loginSource, alice.LoginSource)
assert.Equal(t, wantStr, alice.Website) assert.Equal(t, alice.Name, alice.LoginName)
assert.Equal(t, wantStr, alice.Location) wantStr255 := strings.Repeat("a", 255)
assert.Equal(t, wantStr, alice.Description) assert.Equal(t, wantStr255, alice.FullName)
assert.Equal(t, 1, alice.MaxRepoCreation) assert.Equal(t, wantStr255, alice.Website)
assert.Equal(t, wantStr255, alice.Location)
// Test empty values assert.Equal(t, wantStr255, alice.Description)
opts = UpdateUserOptions{ assert.Equal(t, maxRepoCreation, alice.MaxRepoCreation)
FullName: "Alice John", assert.Equal(t, lastRepoVisibility, alice.LastRepoVisibility)
Website: "https://gogs.io", assert.Equal(t, lastRepoVisibility, alice.IsActive)
assert.Equal(t, lastRepoVisibility, alice.IsAdmin)
assert.Equal(t, lastRepoVisibility, alice.AllowGitHook)
assert.Equal(t, lastRepoVisibility, alice.AllowImportLocal)
assert.Equal(t, lastRepoVisibility, alice.ProhibitLogin)
wantStr2048 := strings.Repeat("a", 2048)
assert.Equal(t, wantStr2048, alice.Avatar)
assert.Equal(t, wantStr255, alice.AvatarEmail)
} }
err = db.Update(ctx, alice.ID, opts) assertValues()
// Test ignored values
err = db.Update(ctx, alice.ID, UpdateUserOptions{})
require.NoError(t, err) require.NoError(t, err)
alice, err = db.GetByID(ctx, alice.ID) alice, err = db.GetByID(ctx, alice.ID)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, opts.FullName, alice.FullName) assertValues()
assert.Equal(t, opts.Website, alice.Website)
assert.Empty(t, alice.Location)
assert.Empty(t, alice.Description)
assert.Empty(t, alice.MaxRepoCreation)
} }
func usersUseCustomAvatar(t *testing.T, db *users) { func usersUseCustomAvatar(t *testing.T, db *users) {

View File

@ -232,6 +232,10 @@ func NewContext() {
// It returns without confirmation (mail processed asynchronously) in normal cases, // It returns without confirmation (mail processed asynchronously) in normal cases,
// but waits/blocks under hook mode to make sure mail has been sent. // but waits/blocks under hook mode to make sure mail has been sent.
func Send(msg *Message) { func Send(msg *Message) {
if !conf.Email.Enabled {
return
}
mailQueue <- msg mailQueue <- msg
if conf.HookMode { if conf.HookMode {

View File

@ -104,8 +104,8 @@ func (f *UpdateProfile) Validate(ctx *macaron.Context, errs binding.Errors) bind
} }
const ( const (
AVATAR_LOCAL string = "local" AvatarLocal string = "local"
AVATAR_BYMAIL string = "bymail" AvatarLookup string = "lookup"
) )
type Avatar struct { type Avatar struct {

View File

@ -8,7 +8,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/unknwon/com"
log "unknwon.dev/clog/v2" log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf" "gogs.io/gogs/internal/conf"
@ -17,7 +16,6 @@ import (
"gogs.io/gogs/internal/email" "gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/form" "gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/route" "gogs.io/gogs/internal/route"
"gogs.io/gogs/internal/userutil"
) )
const ( const (
@ -176,38 +174,37 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
return return
} }
opts := db.UpdateUserOptions{
LoginName: &f.LoginName,
FullName: &f.FullName,
Website: &f.Website,
Location: &f.Location,
MaxRepoCreation: &f.MaxRepoCreation,
IsActivated: &f.Active,
IsAdmin: &f.Admin,
AllowGitHook: &f.AllowGitHook,
AllowImportLocal: &f.AllowImportLocal,
ProhibitLogin: &f.ProhibitLogin,
}
fields := strings.Split(f.LoginType, "-") fields := strings.Split(f.LoginType, "-")
if len(fields) == 2 { if len(fields) == 2 {
loginSource := com.StrTo(fields[1]).MustInt64() loginSource, _ := strconv.ParseInt(fields[1], 10, 64)
if u.LoginSource != loginSource { if u.LoginSource != loginSource {
u.LoginSource = loginSource opts.LoginSource = &loginSource
} }
} }
if len(f.Password) > 0 { if f.Password != "" {
u.Password = f.Password opts.Password = &f.Password
var err error
if u.Salt, err = userutil.RandomSalt(); err != nil {
c.Error(err, "get user salt")
return
}
u.Password = userutil.EncodePassword(u.Password, u.Salt)
} }
u.LoginName = f.LoginName if u.Email != f.Email {
u.FullName = f.FullName opts.Email = &f.Email
u.Email = f.Email }
u.Website = f.Website
u.Location = f.Location
u.MaxRepoCreation = f.MaxRepoCreation
u.IsActive = f.Active
u.IsAdmin = f.Admin
u.AllowGitHook = f.AllowGitHook
u.AllowImportLocal = f.AllowImportLocal
u.ProhibitLogin = f.ProhibitLogin
if err := db.UpdateUser(u); err != nil { err := db.Users.Update(c.Req.Context(), u.ID, opts)
if err != nil {
if db.IsErrEmailAlreadyUsed(err) { if db.IsErrEmailAlreadyUsed(err) {
c.Data["Err_Email"] = true c.Data["Err_Email"] = true
c.RenderWithErr(c.Tr("form.email_been_used"), USER_EDIT, &f) c.RenderWithErr(c.Tr("form.email_been_used"), USER_EDIT, &f)
@ -216,7 +213,7 @@ func EditUserPost(c *context.Context, f form.AdminEditUser) {
} }
return return
} }
log.Trace("Account profile updated by admin (%s): %s", c.User.Name, u.Name) log.Trace("Account updated by admin %q: %s", c.User.Name, u.Name)
c.Flash.Success(c.Tr("admin.users.update_profile_success")) c.Flash.Success(c.Tr("admin.users.update_profile_success"))
c.Redirect(conf.Server.Subpath + "/admin/users/" + c.Params(":userid")) c.Redirect(conf.Server.Subpath + "/admin/users/" + c.Params(":userid"))

View File

@ -15,7 +15,6 @@ import (
"gogs.io/gogs/internal/db" "gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/email" "gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/route/api/v1/user" "gogs.io/gogs/internal/route/api/v1/user"
"gogs.io/gogs/internal/userutil"
) )
func parseLoginSource(c *context.APIContext, sourceID int64) { func parseLoginSource(c *context.APIContext, sourceID int64) {
@ -83,39 +82,30 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
return return
} }
if len(form.Password) > 0 { opts := db.UpdateUserOptions{
u.Password = form.Password LoginSource: &form.SourceID,
var err error LoginName: &form.LoginName,
if u.Salt, err = userutil.RandomSalt(); err != nil { FullName: &form.FullName,
c.Error(err, "get user salt") Website: &form.Website,
return Location: &form.Location,
} MaxRepoCreation: form.MaxRepoCreation,
u.Password = userutil.EncodePassword(u.Password, u.Salt) IsActivated: form.Active,
IsAdmin: form.Admin,
AllowGitHook: form.AllowGitHook,
AllowImportLocal: form.AllowImportLocal,
ProhibitLogin: nil, // TODO: Add this option to API
} }
u.LoginSource = form.SourceID if form.Password != "" {
u.LoginName = form.LoginName opts.Password = &form.Password
u.FullName = form.FullName
u.Email = form.Email
u.Website = form.Website
u.Location = form.Location
if form.Active != nil {
u.IsActive = *form.Active
}
if form.Admin != nil {
u.IsAdmin = *form.Admin
}
if form.AllowGitHook != nil {
u.AllowGitHook = *form.AllowGitHook
}
if form.AllowImportLocal != nil {
u.AllowImportLocal = *form.AllowImportLocal
}
if form.MaxRepoCreation != nil {
u.MaxRepoCreation = *form.MaxRepoCreation
} }
if err := db.UpdateUser(u); err != nil { if u.Email != form.Email {
opts.Email = &form.Email
}
err := db.Users.Update(c.Req.Context(), u.ID, opts)
if err != nil {
if db.IsErrEmailAlreadyUsed(err) { if db.IsErrEmailAlreadyUsed(err) {
c.ErrorStatus(http.StatusUnprocessableEntity, err) c.ErrorStatus(http.StatusUnprocessableEntity, err)
} else { } else {
@ -123,8 +113,13 @@ func EditUser(c *context.APIContext, form api.EditUserOption) {
} }
return return
} }
log.Trace("Account profile updated by admin %q: %s", c.User.Name, u.Name) log.Trace("Account updated by admin %q: %s", c.User.Name, u.Name)
u, err = db.Users.GetByID(c.Req.Context(), u.ID)
if err != nil {
c.Error(err, "get user")
return
}
c.JSONSuccess(u.APIFormat()) c.JSONSuccess(u.APIFormat())
} }

View File

@ -89,14 +89,25 @@ func Edit(c *context.APIContext, form api.EditOrgOption) {
return return
} }
org.FullName = form.FullName err := db.Users.Update(
org.Description = form.Description c.Req.Context(),
org.Website = form.Website c.Org.Organization.ID,
org.Location = form.Location db.UpdateUserOptions{
if err := db.UpdateUser(org); err != nil { FullName: &form.FullName,
c.Error(err, "update user") Website: &form.Website,
Location: &form.Location,
Description: &form.Description,
},
)
if err != nil {
c.Error(err, "update organization")
return return
} }
org, err = db.GetOrgByName(org.Name)
if err != nil {
c.Error(err, "get organization")
return
}
c.JSONSuccess(convert.ToOrganization(org)) c.JSONSuccess(convert.ToOrganization(org))
} }

View File

@ -63,16 +63,15 @@ func SettingsPost(c *context.Context, f form.UpdateOrgSetting) {
} }
opts := db.UpdateUserOptions{ opts := db.UpdateUserOptions{
FullName: f.FullName, FullName: &f.FullName,
Website: f.Website, Website: &f.Website,
Location: f.Location, Location: &f.Location,
Description: f.Description, Description: &f.Description,
MaxRepoCreation: org.MaxRepoCreation,
} }
if c.User.IsAdmin { if c.User.IsAdmin {
opts.MaxRepoCreation = f.MaxRepoCreation opts.MaxRepoCreation = &f.MaxRepoCreation
} }
err := db.Users.Update(c.Req.Context(), c.User.ID, opts) err := db.Users.Update(c.Req.Context(), c.Org.Organization.ID, opts)
if err != nil { if err != nil {
c.Error(err, "update organization") c.Error(err, "update organization")
return return
@ -83,7 +82,7 @@ func SettingsPost(c *context.Context, f form.UpdateOrgSetting) {
} }
func SettingsAvatar(c *context.Context, f form.Avatar) { func SettingsAvatar(c *context.Context, f form.Avatar) {
f.Source = form.AVATAR_LOCAL f.Source = form.AvatarLocal
if err := user.UpdateAvatarSetting(c, f, c.Org.Organization); err != nil { if err := user.UpdateAvatarSetting(c, f, c.Org.Organization); err != nil {
c.Flash.Error(err.Error()) c.Flash.Error(err.Error())
} else { } else {

View File

@ -309,7 +309,7 @@ func SettingsAvatar(c *context.Context) {
} }
func SettingsAvatarPost(c *context.Context, f form.Avatar) { func SettingsAvatarPost(c *context.Context, f form.Avatar) {
f.Source = form.AVATAR_LOCAL f.Source = form.AvatarLocal
if err := UpdateAvatarSetting(c, f, c.Repo.Repository); err != nil { if err := UpdateAvatarSetting(c, f, c.Repo.Repository); err != nil {
c.Flash.Error(err.Error()) c.Flash.Error(err.Error())
} else { } else {

View File

@ -367,9 +367,16 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
// //
// Auto-set admin for the only user. // Auto-set admin for the only user.
if db.Users.Count(c.Req.Context()) == 1 { if db.Users.Count(c.Req.Context()) == 1 {
user.IsAdmin = true v := true
user.IsActive = true err := db.Users.Update(
if err := db.UpdateUser(user); err != nil { c.Req.Context(),
user.ID,
db.UpdateUserOptions{
IsActivated: &v,
IsAdmin: &v,
},
)
if err != nil {
c.Error(err, "update user") c.Error(err, "update user")
return return
} }
@ -476,13 +483,16 @@ func Activate(c *context.Context) {
// Verify code. // Verify code.
if user := verifyUserActiveCode(code); user != nil { if user := verifyUserActiveCode(code); user != nil {
user.IsActive = true v := true
var err error err := db.Users.Update(
if user.Rands, err = userutil.RandomSalt(); err != nil { c.Req.Context(),
c.Error(err, "get user salt") user.ID,
return db.UpdateUserOptions{
} GenerateNewRands: true,
if err := db.UpdateUser(user); err != nil { IsActivated: &v,
},
)
if err != nil {
c.Error(err, "update user") c.Error(err, "update user")
return return
} }
@ -601,26 +611,16 @@ func ResetPasswdPost(c *context.Context) {
if u := verifyUserActiveCode(code); u != nil { if u := verifyUserActiveCode(code); u != nil {
// Validate password length. // Validate password length.
passwd := c.Query("password") password := c.Query("password")
if len(passwd) < 6 { if len(password) < 6 {
c.Data["IsResetForm"] = true c.Data["IsResetForm"] = true
c.Data["Err_Password"] = true c.Data["Err_Password"] = true
c.RenderWithErr(c.Tr("auth.password_too_short"), RESET_PASSWORD, nil) c.RenderWithErr(c.Tr("auth.password_too_short"), RESET_PASSWORD, nil)
return return
} }
u.Password = passwd err := db.Users.Update(c.Req.Context(), u.ID, db.UpdateUserOptions{Password: &password})
var err error if err != nil {
if u.Rands, err = userutil.RandomSalt(); err != nil {
c.Error(err, "get user salt")
return
}
if u.Salt, err = userutil.RandomSalt(); err != nil {
c.Error(err, "get user salt")
return
}
u.Password = userutil.EncodePassword(u.Password, u.Salt)
if err := db.UpdateUser(u); err != nil {
c.Error(err, "update user") c.Error(err, "update user")
return return
} }

View File

@ -96,10 +96,9 @@ func SettingsPost(c *context.Context, f form.UpdateProfile) {
c.Req.Context(), c.Req.Context(),
c.User.ID, c.User.ID,
db.UpdateUserOptions{ db.UpdateUserOptions{
FullName: f.FullName, FullName: &f.FullName,
Website: f.Website, Website: &f.Website,
Location: f.Location, Location: &f.Location,
MaxRepoCreation: c.User.MaxRepoCreation,
}, },
) )
if err != nil { if err != nil {
@ -113,13 +112,23 @@ func SettingsPost(c *context.Context, f form.UpdateProfile) {
// FIXME: limit upload size // FIXME: limit upload size
func UpdateAvatarSetting(c *context.Context, f form.Avatar, ctxUser *db.User) error { func UpdateAvatarSetting(c *context.Context, f form.Avatar, ctxUser *db.User) error {
if f.Source == form.AVATAR_BYMAIL && len(f.Gravatar) > 0 { if f.Source == form.AvatarLookup && f.Gravatar != "" {
ctxUser.UseCustomAvatar = false avatar := cryptoutil.MD5(f.Gravatar)
ctxUser.Avatar = cryptoutil.MD5(f.Gravatar) err := db.Users.Update(
ctxUser.AvatarEmail = f.Gravatar c.Req.Context(),
ctxUser.ID,
db.UpdateUserOptions{
Avatar: &avatar,
AvatarEmail: &f.Gravatar,
},
)
if err != nil {
return errors.Wrap(err, "update user")
}
if err := db.UpdateUser(ctxUser); err != nil { err = db.Users.DeleteCustomAvatar(c.Req.Context(), c.User.ID)
return fmt.Errorf("update user: %v", err) if err != nil {
return errors.Wrap(err, "delete custom avatar")
} }
return nil return nil
} }
@ -193,14 +202,14 @@ func SettingsPasswordPost(c *context.Context, f form.ChangePassword) {
} else if f.Password != f.Retype { } else if f.Password != f.Retype {
c.Flash.Error(c.Tr("form.password_not_match")) c.Flash.Error(c.Tr("form.password_not_match"))
} else { } else {
c.User.Password = f.Password err := db.Users.Update(
var err error c.Req.Context(),
if c.User.Salt, err = userutil.RandomSalt(); err != nil { c.User.ID,
c.Errorf(err, "get user salt") db.UpdateUserOptions{
return Password: &f.Password,
} },
c.User.Password = userutil.EncodePassword(c.User.Password, c.User.Salt) )
if err := db.UpdateUser(c.User); err != nil { if err != nil {
c.Errorf(err, "update user") c.Errorf(err, "update user")
return return
} }