Skip to content

Commit

Permalink
Merge branch 'main' into refactor-branch-selector
Browse files Browse the repository at this point in the history
  • Loading branch information
wxiaoguang authored Mar 14, 2023
2 parents d43f435 + d56bb74 commit 41f24bc
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 44 deletions.
4 changes: 2 additions & 2 deletions models/issues/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,10 +660,10 @@ func GetPullRequestByIssueID(ctx context.Context, issueID int64) (*PullRequest,

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

err := db.GetEngine(db.DefaultContext).
err := db.GetEngine(ctx).
Where("has_merged=? AND flow = ? AND issue.is_closed=? AND issue.poster_id=?",
false, PullRequestFlowAGit, false, uid).
Join("INNER", "issue", "issue.id=pull_request.issue_id").
Expand Down
4 changes: 2 additions & 2 deletions models/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -742,13 +742,13 @@ func VerifyUserActiveCode(code string) (user *User) {
}

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

ctx, committer, err := db.TxContext(db.DefaultContext)
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
Expand Down
9 changes: 9 additions & 0 deletions modules/structs/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,12 @@ type UserSettingsOptions struct {
HideEmail *bool `json:"hide_email"`
HideActivity *bool `json:"hide_activity"`
}

// RenameUserOption options when renaming a user
type RenameUserOption struct {
// New username for this user. This name cannot be in use yet by any other user.
//
// required: true
// unique: true
NewName string `json:"new_username" binding:"Required"`
}
3 changes: 3 additions & 0 deletions modules/structs/user_email.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package structs
Expand All @@ -9,6 +10,8 @@ type Email struct {
Email string `json:"email"`
Verified bool `json:"verified"`
Primary bool `json:"primary"`
UserID int64 `json:"user_id"`
UserName string `json:"username"`
}

// CreateEmailOption options when creating email addresses
Expand Down
87 changes: 87 additions & 0 deletions routers/api/v1/admin/email.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package admin

import (
"net/http"

user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/convert"
)

// GetAllEmails
func GetAllEmails(ctx *context.APIContext) {
// swagger:operation GET /admin/emails admin adminGetAllEmails
// ---
// summary: List all emails
// produces:
// - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/EmailList"
// "403":
// "$ref": "#/responses/forbidden"

listOptions := utils.GetListOptions(ctx)

emails, maxResults, err := user_model.SearchEmails(&user_model.SearchEmailOptions{
Keyword: ctx.Params(":email"),
ListOptions: listOptions,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetAllEmails", err)
return
}

results := make([]*api.Email, len(emails))
for i := range emails {
results[i] = convert.ToEmailSearch(emails[i])
}

ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, &results)
}

// SearchEmail
func SearchEmail(ctx *context.APIContext) {
// swagger:operation GET /admin/emails/search admin adminSearchEmails
// ---
// summary: Search all emails
// produces:
// - application/json
// parameters:
// - name: q
// in: query
// description: keyword
// type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/EmailList"
// "403":
// "$ref": "#/responses/forbidden"

ctx.SetParams(":email", ctx.FormTrim("q"))
GetAllEmails(ctx)
}
58 changes: 58 additions & 0 deletions routers/api/v1/admin/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,3 +461,61 @@ func GetAllUsers(ctx *context.APIContext) {
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, &results)
}

// RenameUser api for renaming a user
func RenameUser(ctx *context.APIContext) {
// swagger:operation POST /admin/users/{username}/rename admin adminRenameUser
// ---
// summary: Rename a user
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: existing username of user
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/RenameUserOption"
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"

if ctx.ContextUser.IsOrganization() {
ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("%s is an organization not a user", ctx.ContextUser.Name))
return
}

newName := web.GetForm(ctx).(*api.RenameUserOption).NewName

if strings.EqualFold(newName, ctx.ContextUser.Name) {
// Noop as username is not changed
ctx.Status(http.StatusNoContent)
return
}

// Check if user name has been changed
if err := user_service.RenameUser(ctx, ctx.ContextUser, newName); err != nil {
switch {
case user_model.IsErrUserAlreadyExist(err):
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("form.username_been_taken"))
case db.IsErrNameReserved(err):
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_reserved", newName))
case db.IsErrNamePatternNotAllowed(err):
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_pattern_not_allowed", newName))
case db.IsErrNameCharsNotAllowed(err):
ctx.Error(http.StatusUnprocessableEntity, "", ctx.Tr("user.form.name_chars_not_allowed", newName))
default:
ctx.ServerError("ChangeUserName", err)
}
return
}
ctx.Status(http.StatusNoContent)
}
5 changes: 5 additions & 0 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1257,8 +1257,13 @@ func Routes(ctx gocontext.Context) *web.Route {
m.Get("/orgs", org.ListUserOrgs)
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser)
}, context_service.UserAssignmentAPI())
})
m.Group("/emails", func() {
m.Get("", admin.GetAllEmails)
m.Get("/search", admin.SearchEmail)
})
m.Group("/unadopted", func() {
m.Get("", admin.ListUnadoptedRepositories)
m.Post("/{username}/{reponame}", admin.AdoptRepository)
Expand Down
3 changes: 3 additions & 0 deletions routers/api/v1/swagger/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ type swaggerParameterBodies struct {
// in:body
CreateKeyOption api.CreateKeyOption

// in:body
RenameUserOption api.RenameUserOption

// in:body
CreateLabelOption api.CreateLabelOption
// in:body
Expand Down
2 changes: 1 addition & 1 deletion routers/web/org/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func SettingsPost(ctx *context.Context) {
ctx.Data["OrgName"] = true
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
return
} else if err = user_model.ChangeUserName(org.AsUser(), form.Name); err != nil {
} else if err = user_model.ChangeUserName(ctx, org.AsUser(), form.Name); err != nil {
switch {
case db.IsErrNameReserved(err):
ctx.Data["OrgName"] = true
Expand Down
52 changes: 15 additions & 37 deletions routers/web/user/setting/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/agit"
"code.gitea.io/gitea/services/forms"
container_service "code.gitea.io/gitea/services/packages/container"
user_service "code.gitea.io/gitea/services/user"
)

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

// Check if user name has been changed
if user.LowerName != strings.ToLower(newName) {
if err := user_model.ChangeUserName(user, newName); err != nil {
switch {
case user_model.IsErrUserAlreadyExist(err):
ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
case user_model.IsErrEmailAlreadyUsed(err):
ctx.Flash.Error(ctx.Tr("form.email_been_used"))
case db.IsErrNameReserved(err):
ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName))
case db.IsErrNamePatternNotAllowed(err):
ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
case db.IsErrNameCharsNotAllowed(err):
ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName))
default:
ctx.ServerError("ChangeUserName", err)
}
return err
}
} else {
if err := repo_model.UpdateRepositoryOwnerNames(user.ID, newName); err != nil {
ctx.ServerError("UpdateRepository", err)
return err
// rename user
if err := user_service.RenameUser(ctx, user, newName); err != nil {
switch {
case user_model.IsErrUserAlreadyExist(err):
ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
case user_model.IsErrEmailAlreadyUsed(err):
ctx.Flash.Error(ctx.Tr("form.email_been_used"))
case db.IsErrNameReserved(err):
ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName))
case db.IsErrNamePatternNotAllowed(err):
ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
case db.IsErrNameCharsNotAllowed(err):
ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName))
default:
ctx.ServerError("ChangeUserName", err)
}
}

// update all agit flow pull request header
err := agit.UserNameChanged(user, newName)
if err != nil {
ctx.ServerError("agit.UserNameChanged", err)
return err
}

if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil {
ctx.ServerError("UpdateRepositoryNames", err)
return err
}

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

Expand Down
4 changes: 2 additions & 2 deletions services/agit/agit.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
}

// UserNameChanged handle user name change for agit flow pull
func UserNameChanged(user *user_model.User, newName string) error {
pulls, err := issues_model.GetAllUnmergedAgitPullRequestByPoster(user.ID)
func UserNameChanged(ctx context.Context, user *user_model.User, newName string) error {
pulls, err := issues_model.GetAllUnmergedAgitPullRequestByPoster(ctx, user.ID)
if err != nil {
return err
}
Expand Down
11 changes: 11 additions & 0 deletions services/convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ func ToEmail(email *user_model.EmailAddress) *api.Email {
}
}

// ToEmail convert models.EmailAddress to api.Email
func ToEmailSearch(email *user_model.SearchEmailResult) *api.Email {
return &api.Email{
Email: email.Email,
Verified: email.IsActivated,
Primary: email.IsPrimary,
UserID: email.UID,
UserName: email.Name,
}
}

// ToBranch convert a git.Commit and git.Branch to an api.Branch
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) {
if bp == nil {
Expand Down
41 changes: 41 additions & 0 deletions services/user/rename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package user

import (
"context"
"fmt"
"strings"

user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/services/agit"
container_service "code.gitea.io/gitea/services/packages/container"
)

func renameUser(ctx context.Context, u *user_model.User, newUserName string) error {
if u.IsOrganization() {
return fmt.Errorf("cannot rename organization")
}

if err := user_model.ChangeUserName(ctx, u, newUserName); err != nil {
return err
}

if err := agit.UserNameChanged(ctx, u, newUserName); err != nil {
return err
}
if err := container_service.UpdateRepositoryNames(ctx, u, newUserName); err != nil {
return err
}

u.Name = newUserName
u.LowerName = strings.ToLower(newUserName)
if err := user_model.UpdateUser(ctx, u, false); err != nil {
return err
}

log.Trace("User name changed: %s -> %s", u.Name, newUserName)
return nil
}
Loading

0 comments on commit 41f24bc

Please sign in to comment.