Skip to content

Commit 41f24bc

Browse files
authored
Merge branch 'main' into refactor-branch-selector
2 parents d43f435 + d56bb74 commit 41f24bc

File tree

15 files changed

+394
-44
lines changed

15 files changed

+394
-44
lines changed

models/issues/pull.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,10 @@ func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest,
660660

661661
// GetAllUnmergedAgitPullRequestByPoster get all unmerged agit flow pull request
662662
// By poster id.
663-
func GetAllUnmergedAgitPullRequestByPoster(uid int64) ([]*PullRequest, error) {
663+
func GetAllUnmergedAgitPullRequestByPoster(ctx context.Context, uid int64) ([]*PullRequest, error) {
664664
pulls := make([]*PullRequest, 0, 10)
665665

666-
err := db.GetEngine(db.DefaultContext).
666+
err := db.GetEngine(ctx).
667667
Where("has_merged=? AND flow = ? AND issue.is_closed=? AND issue.poster_id=?",
668668
false, PullRequestFlowAGit, false, uid).
669669
Join("INNER", "issue", "issue.id=pull_request.issue_id").

models/user/user.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,13 +742,13 @@ func VerifyUserActiveCode(code string) (user *User) {
742742
}
743743

744744
// ChangeUserName changes all corresponding setting from old user name to new one.
745-
func ChangeUserName(u *User, newUserName string) (err error) {
745+
func ChangeUserName(ctx context.Context, u *User, newUserName string) (err error) {
746746
oldUserName := u.Name
747747
if err = IsUsableUsername(newUserName); err != nil {
748748
return err
749749
}
750750

751-
ctx, committer, err := db.TxContext(db.DefaultContext)
751+
ctx, committer, err := db.TxContext(ctx)
752752
if err != nil {
753753
return err
754754
}

modules/structs/user.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,12 @@ type UserSettingsOptions struct {
9393
HideEmail *bool `json:"hide_email"`
9494
HideActivity *bool `json:"hide_activity"`
9595
}
96+
97+
// RenameUserOption options when renaming a user
98+
type RenameUserOption struct {
99+
// New username for this user. This name cannot be in use yet by any other user.
100+
//
101+
// required: true
102+
// unique: true
103+
NewName string `json:"new_username" binding:"Required"`
104+
}

modules/structs/user_email.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2015 The Gogs Authors. All rights reserved.
2+
// Copyright 2023 The Gitea Authors. All rights reserved.
23
// SPDX-License-Identifier: MIT
34

45
package structs
@@ -9,6 +10,8 @@ type Email struct {
910
Email string `json:"email"`
1011
Verified bool `json:"verified"`
1112
Primary bool `json:"primary"`
13+
UserID int64 `json:"user_id"`
14+
UserName string `json:"username"`
1215
}
1316

1417
// CreateEmailOption options when creating email addresses

routers/api/v1/admin/email.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package admin
5+
6+
import (
7+
"net/http"
8+
9+
user_model "code.gitea.io/gitea/models/user"
10+
"code.gitea.io/gitea/modules/context"
11+
api "code.gitea.io/gitea/modules/structs"
12+
"code.gitea.io/gitea/routers/api/v1/utils"
13+
"code.gitea.io/gitea/services/convert"
14+
)
15+
16+
// GetAllEmails
17+
func GetAllEmails(ctx *context.APIContext) {
18+
// swagger:operation GET /admin/emails admin adminGetAllEmails
19+
// ---
20+
// summary: List all emails
21+
// produces:
22+
// - application/json
23+
// parameters:
24+
// - name: page
25+
// in: query
26+
// description: page number of results to return (1-based)
27+
// type: integer
28+
// - name: limit
29+
// in: query
30+
// description: page size of results
31+
// type: integer
32+
// responses:
33+
// "200":
34+
// "$ref": "#/responses/EmailList"
35+
// "403":
36+
// "$ref": "#/responses/forbidden"
37+
38+
listOptions := utils.GetListOptions(ctx)
39+
40+
emails, maxResults, err := user_model.SearchEmails(&user_model.SearchEmailOptions{
41+
Keyword: ctx.Params(":email"),
42+
ListOptions: listOptions,
43+
})
44+
if err != nil {
45+
ctx.Error(http.StatusInternalServerError, "GetAllEmails", err)
46+
return
47+
}
48+
49+
results := make([]*api.Email, len(emails))
50+
for i := range emails {
51+
results[i] = convert.ToEmailSearch(emails[i])
52+
}
53+
54+
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
55+
ctx.SetTotalCountHeader(maxResults)
56+
ctx.JSON(http.StatusOK, &results)
57+
}
58+
59+
// SearchEmail
60+
func SearchEmail(ctx *context.APIContext) {
61+
// swagger:operation GET /admin/emails/search admin adminSearchEmails
62+
// ---
63+
// summary: Search all emails
64+
// produces:
65+
// - application/json
66+
// parameters:
67+
// - name: q
68+
// in: query
69+
// description: keyword
70+
// type: string
71+
// - name: page
72+
// in: query
73+
// description: page number of results to return (1-based)
74+
// type: integer
75+
// - name: limit
76+
// in: query
77+
// description: page size of results
78+
// type: integer
79+
// responses:
80+
// "200":
81+
// "$ref": "#/responses/EmailList"
82+
// "403":
83+
// "$ref": "#/responses/forbidden"
84+
85+
ctx.SetParams(":email", ctx.FormTrim("q"))
86+
GetAllEmails(ctx)
87+
}

routers/api/v1/admin/user.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,61 @@ func GetAllUsers(ctx *context.APIContext) {
461461
ctx.SetTotalCountHeader(maxResults)
462462
ctx.JSON(http.StatusOK, &results)
463463
}
464+
465+
// RenameUser api for renaming a user
466+
func RenameUser(ctx *context.APIContext) {
467+
// swagger:operation POST /admin/users/{username}/rename admin adminRenameUser
468+
// ---
469+
// summary: Rename a user
470+
// produces:
471+
// - application/json
472+
// parameters:
473+
// - name: username
474+
// in: path
475+
// description: existing username of user
476+
// type: string
477+
// required: true
478+
// - name: body
479+
// in: body
480+
// required: true
481+
// schema:
482+
// "$ref": "#/definitions/RenameUserOption"
483+
// responses:
484+
// "204":
485+
// "$ref": "#/responses/empty"
486+
// "403":
487+
// "$ref": "#/responses/forbidden"
488+
// "422":
489+
// "$ref": "#/responses/validationError"
490+
491+
if ctx.ContextUser.IsOrganization() {
492+
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
493+
return
494+
}
495+
496+
newName := web.GetForm(ctx).(*api.RenameUserOption).NewName
497+
498+
if strings.EqualFold(newName, ctx.ContextUser.Name) {
499+
// Noop as username is not changed
500+
ctx.Status(http.StatusNoContent)
501+
return
502+
}
503+
504+
// Check if user name has been changed
505+
if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil {
506+
switch {
507+
case user_model.IsErrUserAlreadyExist(err):
508+
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken"))
509+
case db.IsErrNameReserved(err):
510+
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_reserved", newName))
511+
case db.IsErrNamePatternNotAllowed(err):
512+
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_pattern_not_allowed", newName))
513+
case db.IsErrNameCharsNotAllowed(err):
514+
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_chars_not_allowed", newName))
515+
default:
516+
ctx.ServerError("ChangeUserName", err)
517+
}
518+
return
519+
}
520+
ctx.Status(http.StatusNoContent)
521+
}

routers/api/v1/api.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,8 +1257,13 @@ func Routes(ctx gocontext.Context) *web.Route {
12571257
m.Get("/orgs", org.ListUserOrgs)
12581258
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
12591259
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
1260+
m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser)
12601261
}, context_service.UserAssignmentAPI())
12611262
})
1263+
m.Group("/emails", func() {
1264+
m.Get("", admin.GetAllEmails)
1265+
m.Get("/search", admin.SearchEmail)
1266+
})
12621267
m.Group("/unadopted", func() {
12631268
m.Get("", admin.ListUnadoptedRepositories)
12641269
m.Post("/{username}/{reponame}", admin.AdoptRepository)

routers/api/v1/swagger/options.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ type swaggerParameterBodies struct {
4848
// in:body
4949
CreateKeyOption api.CreateKeyOption
5050

51+
// in:body
52+
RenameUserOption api.RenameUserOption
53+
5154
// in:body
5255
CreateLabelOption api.CreateLabelOption
5356
// in:body

routers/web/org/setting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func SettingsPost(ctx *context.Context) {
7979
ctx.Data["OrgName"] = true
8080
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
8181
return
82-
} else if err = user_model.ChangeUserName(org.AsUser(), form.Name); err != nil {
82+
} else if err = user_model.ChangeUserName(ctx, org.AsUser(), form.Name); err != nil {
8383
switch {
8484
case db.IsErrNameReserved(err):
8585
ctx.Data["OrgName"] = true

routers/web/user/setting/profile.go

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ import (
2727
"code.gitea.io/gitea/modules/util"
2828
"code.gitea.io/gitea/modules/web"
2929
"code.gitea.io/gitea/modules/web/middleware"
30-
"code.gitea.io/gitea/services/agit"
3130
"code.gitea.io/gitea/services/forms"
32-
container_service "code.gitea.io/gitea/services/packages/container"
3331
user_service "code.gitea.io/gitea/services/user"
3432
)
3533

@@ -57,45 +55,25 @@ func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName s
5755
return fmt.Errorf(ctx.Tr("form.username_change_not_local_user"))
5856
}
5957

60-
// Check if user name has been changed
61-
if user.LowerName != strings.ToLower(newName) {
62-
if err := user_model.ChangeUserName(user, newName); err != nil {
63-
switch {
64-
case user_model.IsErrUserAlreadyExist(err):
65-
ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
66-
case user_model.IsErrEmailAlreadyUsed(err):
67-
ctx.Flash.Error(ctx.Tr("form.email_been_used"))
68-
case db.IsErrNameReserved(err):
69-
ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName))
70-
case db.IsErrNamePatternNotAllowed(err):
71-
ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
72-
case db.IsErrNameCharsNotAllowed(err):
73-
ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName))
74-
default:
75-
ctx.ServerError("ChangeUserName", err)
76-
}
77-
return err
78-
}
79-
} else {
80-
if err := repo_model.UpdateRepositoryOwnerNames(user.ID, newName); err != nil {
81-
ctx.ServerError("UpdateRepository", err)
82-
return err
58+
// rename user
59+
if err := user_service.RenameUser(ctx, user, newName); err != nil {
60+
switch {
61+
case user_model.IsErrUserAlreadyExist(err):
62+
ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
63+
case user_model.IsErrEmailAlreadyUsed(err):
64+
ctx.Flash.Error(ctx.Tr("form.email_been_used"))
65+
case db.IsErrNameReserved(err):
66+
ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName))
67+
case db.IsErrNamePatternNotAllowed(err):
68+
ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
69+
case db.IsErrNameCharsNotAllowed(err):
70+
ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName))
71+
default:
72+
ctx.ServerError("ChangeUserName", err)
8373
}
84-
}
85-
86-
// update all agit flow pull request header
87-
err := agit.UserNameChanged(user, newName)
88-
if err != nil {
89-
ctx.ServerError("agit.UserNameChanged", err)
90-
return err
91-
}
92-
93-
if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil {
94-
ctx.ServerError("UpdateRepositoryNames", err)
9574
return err
9675
}
9776

98-
log.Trace("User name changed: %s -> %s", user.Name, newName)
9977
return nil
10078
}
10179

services/agit/agit.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
226226
}
227227

228228
// UserNameChanged handle user name change for agit flow pull
229-
func UserNameChanged(user *user_model.User, newName string) error {
230-
pulls, err := issues_model.GetAllUnmergedAgitPullRequestByPoster(user.ID)
229+
func UserNameChanged(ctx context.Context, user *user_model.User, newName string) error {
230+
pulls, err := issues_model.GetAllUnmergedAgitPullRequestByPoster(ctx, user.ID)
231231
if err != nil {
232232
return err
233233
}

services/convert/convert.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ func ToEmail(email *user_model.EmailAddress) *api.Email {
3838
}
3939
}
4040

41+
// ToEmail convert models.EmailAddress to api.Email
42+
func ToEmailSearch(email *user_model.SearchEmailResult) *api.Email {
43+
return &api.Email{
44+
Email: email.Email,
45+
Verified: email.IsActivated,
46+
Primary: email.IsPrimary,
47+
UserID: email.UID,
48+
UserName: email.Name,
49+
}
50+
}
51+
4152
// ToBranch convert a git.Commit and git.Branch to an api.Branch
4253
func ToBranch(ctx context.Context, repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git_model.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
4354
if bp == nil {

services/user/rename.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package user
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"strings"
10+
11+
user_model "code.gitea.io/gitea/models/user"
12+
"code.gitea.io/gitea/modules/log"
13+
"code.gitea.io/gitea/services/agit"
14+
container_service "code.gitea.io/gitea/services/packages/container"
15+
)
16+
17+
func renameUser(ctx context.Context, u *user_model.User, newUserName string) error {
18+
if u.IsOrganization() {
19+
return fmt.Errorf("cannot rename organization")
20+
}
21+
22+
if err := user_model.ChangeUserName(ctx, u, newUserName); err != nil {
23+
return err
24+
}
25+
26+
if err := agit.UserNameChanged(ctx, u, newUserName); err != nil {
27+
return err
28+
}
29+
if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil {
30+
return err
31+
}
32+
33+
u.Name = newUserName
34+
u.LowerName = strings.ToLower(newUserName)
35+
if err := user_model.UpdateUser(ctx, u, false); err != nil {
36+
return err
37+
}
38+
39+
log.Trace("User name changed: %s -> %s", u.Name, newUserName)
40+
return nil
41+
}

0 commit comments

Comments
 (0)